diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 0583677e4581..88213b532bdc 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -30,7 +30,7 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ nv04_fbcon.o nv50_fbcon.o nvc0_fbcon.o \ nv10_gpio.o nv50_gpio.o \ nv50_calc.o \ - nv04_pm.o nv50_pm.o nva3_pm.o \ + nv04_pm.o nv50_pm.o nva3_pm.o nvc0_pm.o \ nv50_vram.o nvc0_vram.o \ nv50_vm.o nvc0_vm.o diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h index 884bb7f90a18..f519883d9a43 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.h +++ b/drivers/gpu/drm/nouveau/nouveau_pm.h @@ -63,6 +63,9 @@ int nva3_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *); void *nva3_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *); void nva3_pm_clocks_set(struct drm_device *, void *); +/* nvc0_pm.c */ +int nvc0_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *); + /* nouveau_temp.c */ void nouveau_temp_init(struct drm_device *dev); void nouveau_temp_fini(struct drm_device *dev); diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 8dc73b6b8138..109f0d984615 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -422,6 +422,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->vram.put = nv50_vram_del; engine->vram.flags_valid = nvc0_vram_flags_valid; engine->pm.temp_get = nv84_temp_get; + engine->pm.clocks_get = nvc0_pm_clocks_get; engine->pm.voltage_get = nouveau_voltage_gpio_get; engine->pm.voltage_set = nouveau_voltage_gpio_set; break; diff --git a/drivers/gpu/drm/nouveau/nvc0_pm.c b/drivers/gpu/drm/nouveau/nvc0_pm.c new file mode 100644 index 000000000000..aff4426b2408 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvc0_pm.c @@ -0,0 +1,145 @@ +/* + * Copyright 2011 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ + +#include "drmP.h" +#include "nouveau_drv.h" +#include "nouveau_bios.h" +#include "nouveau_pm.h" + +static u32 read_div(struct drm_device *, int, u32, u32); +static u32 read_pll(struct drm_device *, u32); + +static u32 +read_vco(struct drm_device *dev, u32 dsrc) +{ + u32 ssrc = nv_rd32(dev, dsrc); + if (!(ssrc & 0x00000100)) + return read_pll(dev, 0x00e800); + return read_pll(dev, 0x00e820); +} + +static u32 +read_pll(struct drm_device *dev, u32 pll) +{ + u32 coef = nv_rd32(dev, pll + 4); + u32 P = (coef & 0x003f0000) >> 16; + u32 N = (coef & 0x0000ff00) >> 8; + u32 M = (coef & 0x000000ff) >> 0; + u32 sclk, doff; + + switch (pll & 0xfff000) { + case 0x00e000: + sclk = 27000; + P = 1; + break; + case 0x137000: + doff = (pll - 0x137000) / 0x20; + sclk = read_div(dev, doff, 0x137120, 0x137140); + break; + case 0x132000: + switch (pll) { + case 0x132000: + sclk = read_pll(dev, 0x132020); + break; + case 0x132020: + sclk = read_div(dev, 0, 0x137320, 0x137330); + break; + default: + return 0; + } + break; + default: + return 0; + } + + return sclk * N / M / P; +} + +static u32 +read_div(struct drm_device *dev, int doff, u32 dsrc, u32 dctl) +{ + u32 ssrc = nv_rd32(dev, dsrc + (doff * 4)); + u32 sctl = nv_rd32(dev, dctl + (doff * 4)); + + switch (ssrc & 0x00000003) { + case 0: + if ((ssrc & 0x00030000) != 0x00030000) + return 27000; + return 108000; + case 2: + return 100000; + case 3: + if (sctl & 0x80000000) { + u32 sclk = read_vco(dev, dsrc); + u32 sdiv = (sctl & 0x0000003f) + 2; + return (sclk * 2) / sdiv; + } + + return read_vco(dev, dsrc); + default: + return 0; + } +} + +static u32 +read_mem(struct drm_device *dev) +{ + u32 ssel = nv_rd32(dev, 0x1373f0); + if (ssel & 0x00000001) + return read_div(dev, 0, 0x137300, 0x137310); + return read_pll(dev, 0x132000); +} + +static u32 +read_clk(struct drm_device *dev, int clk) +{ + u32 sctl = nv_rd32(dev, 0x137250 + (clk * 4)); + u32 ssel = nv_rd32(dev, 0x137100); + u32 sclk, sdiv; + + if (ssel & (1 << clk)) { + if (clk < 7) + sclk = read_pll(dev, 0x137000 + (clk * 0x20)); + else + sclk = read_pll(dev, 0x1370e0); + sdiv = ((sctl & 0x00003f00) >> 8) + 2; + } else { + sclk = read_div(dev, clk, 0x137160, 0x1371d0); + sdiv = ((sctl & 0x0000003f) >> 0) + 2; + } + + if (sctl & 0x80000000) + return (sclk * 2) / sdiv; + return sclk; +} + +int +nvc0_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) +{ + perflvl->shader = read_clk(dev, 0x00); + perflvl->core = perflvl->shader / 2; + perflvl->memory = read_mem(dev); + perflvl->vdec = read_clk(dev, 0x0e); + return 0; +}