From 6bdf68c9a427220692ad7607858e96caa2cd3147 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 23 Jan 2012 13:17:11 +1000 Subject: drm/nv50/pm: initial work towards proper memory reclocking, with timings Signed-off-by: Ben Skeggs Signed-off-by: Martin Peres --- drivers/gpu/drm/nouveau/nv50_pm.c | 200 +++++++++++++++++++++++++++++--------- 1 file changed, 154 insertions(+), 46 deletions(-) (limited to 'drivers/gpu/drm/nouveau/nv50_pm.c') diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index ec5481dfcd8..17eb3277b1a 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -352,8 +352,13 @@ nv50_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) } struct nv50_pm_state { + struct nouveau_pm_level *perflvl; + struct hwsq_ucode mclk_hwsq; u32 mscript; + u32 mmast; + u32 mctrl; + u32 mcoef; u32 emast; u32 nctrl; @@ -415,36 +420,155 @@ clk_same(u32 a, u32 b) return ((a / 1000) == (b / 1000)); } +static void +mclk_precharge(struct nouveau_mem_exec_func *exec) +{ + struct nv50_pm_state *info = exec->priv; + struct hwsq_ucode *hwsq = &info->mclk_hwsq; + + hwsq_wr32(hwsq, 0x1002d4, 0x00000001); +} + +static void +mclk_refresh(struct nouveau_mem_exec_func *exec) +{ + struct nv50_pm_state *info = exec->priv; + struct hwsq_ucode *hwsq = &info->mclk_hwsq; + + hwsq_wr32(hwsq, 0x1002d0, 0x00000001); +} + +static void +mclk_refresh_auto(struct nouveau_mem_exec_func *exec, bool enable) +{ + struct nv50_pm_state *info = exec->priv; + struct hwsq_ucode *hwsq = &info->mclk_hwsq; + + hwsq_wr32(hwsq, 0x100210, enable ? 0x80000000 : 0x00000000); +} + +static void +mclk_refresh_self(struct nouveau_mem_exec_func *exec, bool enable) +{ + struct nv50_pm_state *info = exec->priv; + struct hwsq_ucode *hwsq = &info->mclk_hwsq; + + hwsq_wr32(hwsq, 0x1002dc, enable ? 0x00000001 : 0x00000000); +} + +static void +mclk_wait(struct nouveau_mem_exec_func *exec, u32 nsec) +{ + struct nv50_pm_state *info = exec->priv; + struct hwsq_ucode *hwsq = &info->mclk_hwsq; + + if (nsec > 1000) + hwsq_usec(hwsq, (nsec + 500) / 1000); +} + +static u32 +mclk_mrg(struct nouveau_mem_exec_func *exec, int mr) +{ + if (mr <= 1) + return nv_rd32(exec->dev, 0x1002c0 + ((mr - 0) * 4)); + if (mr <= 3) + return nv_rd32(exec->dev, 0x1002e0 + ((mr - 2) * 4)); + return 0; +} + +static void +mclk_mrs(struct nouveau_mem_exec_func *exec, int mr, u32 data) +{ + struct drm_nouveau_private *dev_priv = exec->dev->dev_private; + struct nv50_pm_state *info = exec->priv; + struct hwsq_ucode *hwsq = &info->mclk_hwsq; + + if (mr <= 1) { + if (dev_priv->vram_rank_B) + hwsq_wr32(hwsq, 0x1002c8 + ((mr - 0) * 4), data); + hwsq_wr32(hwsq, 0x1002c0 + ((mr - 0) * 4), data); + } else + if (mr <= 3) { + if (dev_priv->vram_rank_B) + hwsq_wr32(hwsq, 0x1002e8 + ((mr - 2) * 4), data); + hwsq_wr32(hwsq, 0x1002e0 + ((mr - 2) * 4), data); + } +} + +static void +mclk_clock_set(struct nouveau_mem_exec_func *exec) +{ + struct nv50_pm_state *info = exec->priv; + struct hwsq_ucode *hwsq = &info->mclk_hwsq; + u32 ctrl = nv_rd32(exec->dev, 0x004008); + + info->mmast = nv_rd32(exec->dev, 0x00c040); + info->mmast &= ~0xc0000000; /* get MCLK_2 from HREF */ + info->mmast |= 0x0000c000; /* use MCLK_2 as MPLL_BYPASS clock */ + + hwsq_wr32(hwsq, 0xc040, info->mmast); + hwsq_wr32(hwsq, 0x4008, ctrl | 0x00000200); /* bypass MPLL */ + if (info->mctrl & 0x80000000) + hwsq_wr32(hwsq, 0x400c, info->mcoef); + hwsq_wr32(hwsq, 0x4008, info->mctrl); +} + +static void +mclk_timing_set(struct nouveau_mem_exec_func *exec) +{ + struct drm_device *dev = exec->dev; + struct nv50_pm_state *info = exec->priv; + struct nouveau_pm_level *perflvl = info->perflvl; + struct hwsq_ucode *hwsq = &info->mclk_hwsq; + int i; + + for (i = 0; i < 9; i++) { + u32 reg = 0x100220 + (i * 4); + u32 val = nv_rd32(dev, reg); + if (val != perflvl->timing.reg[i]) + hwsq_wr32(hwsq, reg, perflvl->timing.reg[i]); + } +} + static int -calc_mclk(struct drm_device *dev, u32 freq, struct hwsq_ucode *hwsq) +calc_mclk(struct drm_device *dev, struct nouveau_pm_level *perflvl, + struct nv50_pm_state *info) { struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_mem_exec_func exec = { + .dev = dev, + .precharge = mclk_precharge, + .refresh = mclk_refresh, + .refresh_auto = mclk_refresh_auto, + .refresh_self = mclk_refresh_self, + .wait = mclk_wait, + .mrg = mclk_mrg, + .mrs = mclk_mrs, + .clock_set = mclk_clock_set, + .timing_set = mclk_timing_set, + .priv = info + }; + struct hwsq_ucode *hwsq = &info->mclk_hwsq; struct pll_lims pll; - u32 mast = nv_rd32(dev, 0x00c040); - u32 ctrl = nv_rd32(dev, 0x004008); - u32 coef = nv_rd32(dev, 0x00400c); - u32 orig = ctrl; u32 crtc_mask = 0; int N, M, P; int ret, i; /* use pcie refclock if possible, otherwise use mpll */ - ctrl &= ~0x81ff0200; - if (clk_same(freq, read_clk(dev, clk_src_href))) { - ctrl |= 0x00000200 | (pll.log2p_bias << 19); + info->mctrl = nv_rd32(dev, 0x004008); + info->mctrl &= ~0x81ff0200; + if (clk_same(perflvl->memory, read_clk(dev, clk_src_href))) { + info->mctrl |= 0x00000200 | (pll.log2p_bias << 19); } else { - ret = calc_pll(dev, 0x4008, &pll, freq, &N, &M, &P); + ret = calc_pll(dev, 0x4008, &pll, perflvl->memory, &N, &M, &P); if (ret == 0) return -EINVAL; - ctrl |= 0x80000000 | (P << 22) | (P << 16); - ctrl |= pll.log2p_bias << 19; - coef = (N << 8) | M; + info->mctrl |= 0x80000000 | (P << 22) | (P << 16); + info->mctrl |= pll.log2p_bias << 19; + info->mcoef = (N << 8) | M; } - mast &= ~0xc0000000; /* get MCLK_2 from HREF */ - mast |= 0x0000c000; /* use MCLK_2 as MPLL_BYPASS clock */ - /* determine active crtcs */ for (i = 0; i < 2; i++) { if (nv_rd32(dev, NV50_PDISPLAY_CRTC_C(i, CLOCK))) @@ -462,25 +586,10 @@ calc_mclk(struct drm_device *dev, u32 freq, struct hwsq_ucode *hwsq) hwsq_setf(hwsq, 0x10, 0); /* disable bus access */ hwsq_op5f(hwsq, 0x00, 0x01); /* no idea :s */ - /* prepare memory controller */ - hwsq_wr32(hwsq, 0x1002d4, 0x00000001); /* precharge banks and idle */ - hwsq_wr32(hwsq, 0x1002d0, 0x00000001); /* force refresh */ - hwsq_wr32(hwsq, 0x100210, 0x00000000); /* stop the automatic refresh */ - hwsq_wr32(hwsq, 0x1002dc, 0x00000001); /* start self refresh mode */ - - /* reclock memory */ - hwsq_wr32(hwsq, 0xc040, mast); - hwsq_wr32(hwsq, 0x4008, orig | 0x00000200); /* bypass MPLL */ - hwsq_wr32(hwsq, 0x400c, coef); - hwsq_wr32(hwsq, 0x4008, ctrl); - - /* restart memory controller */ - hwsq_wr32(hwsq, 0x1002d4, 0x00000001); /* precharge banks and idle */ - hwsq_wr32(hwsq, 0x1002dc, 0x00000000); /* stop self refresh mode */ - hwsq_wr32(hwsq, 0x100210, 0x80000000); /* restart automatic refresh */ - hwsq_usec(hwsq, 12); /* wait for the PLL to stabilize */ - - hwsq_usec(hwsq, 48); /* may be unnecessary: causes flickering */ + ret = nouveau_mem_exec(&exec, perflvl); + if (ret) + return ret; + hwsq_setf(hwsq, 0x10, 1); /* enable bus access */ hwsq_op5f(hwsq, 0x00, 0x00); /* no idea, reverse of 0x00, 0x01? */ if (dev_priv->chipset >= 0x92) @@ -506,6 +615,17 @@ nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) info = kmalloc(sizeof(*info), GFP_KERNEL); if (!info) return ERR_PTR(-ENOMEM); + info->perflvl = perflvl; + + /* memory: build hwsq ucode which we'll use to reclock memory. + * use pcie refclock if possible, otherwise use mpll */ + info->mclk_hwsq.len = 0; + if (perflvl->memory) { + ret = calc_mclk(dev, perflvl, info); + if (ret) + goto error; + info->mscript = perflvl->memscript; + } /* core: for the moment at least, always use nvpll */ clk = calc_pll(dev, 0x4028, &pll, perflvl->core, &N, &M, &P1); @@ -536,18 +656,6 @@ nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) info->scoef = (N << 8) | M; } - /* memory: build hwsq ucode which we'll use to reclock memory */ - info->mclk_hwsq.len = 0; - if (perflvl->memory) { - clk = calc_mclk(dev, perflvl->memory, &info->mclk_hwsq); - if (clk < 0) { - ret = clk; - goto error; - } - - info->mscript = perflvl->memscript; - } - /* vdec: avoid modifying xpll until we know exactly how the other * clock domains work, i suspect at least some of them can also be * tied to xpll... -- cgit v1.2.3-70-g09d2 From e495d0d7e36298f76336fdc58685ac4cacd454ba Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 23 Jan 2012 13:22:58 +1000 Subject: drm/nv50/disp: more accurate function to determine active crtcs Signed-off-by: Ben Skeggs Signed-off-by: Martin Peres --- drivers/gpu/drm/nouveau/nv50_display.c | 23 +++++++++++++++++++++++ drivers/gpu/drm/nouveau/nv50_display.h | 2 ++ drivers/gpu/drm/nouveau/nv50_pm.c | 11 +++-------- 3 files changed, 28 insertions(+), 8 deletions(-) (limited to 'drivers/gpu/drm/nouveau/nv50_pm.c') diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 7ba28e08ee3..ce440e2e58c 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -50,6 +50,29 @@ nv50_sor_nr(struct drm_device *dev) return 4; } +u32 +nv50_display_active_crtcs(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + u32 mask = 0; + int i; + + if (dev_priv->chipset < 0x90 || + dev_priv->chipset == 0x92 || + dev_priv->chipset == 0xa0) { + for (i = 0; i < 2; i++) + mask |= nv_rd32(dev, NV50_PDISPLAY_SOR_MODE_CTRL_C(i)); + } else { + for (i = 0; i < 4; i++) + mask |= nv_rd32(dev, NV90_PDISPLAY_SOR_MODE_CTRL_C(i)); + } + + for (i = 0; i < 3; i++) + mask |= nv_rd32(dev, NV50_PDISPLAY_DAC_MODE_CTRL_C(i)); + + return mask & 3; +} + static int evo_icmd(struct drm_device *dev, int ch, u32 mthd, u32 data) { diff --git a/drivers/gpu/drm/nouveau/nv50_display.h b/drivers/gpu/drm/nouveau/nv50_display.h index 95874f7c043..5d3dd14d283 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.h +++ b/drivers/gpu/drm/nouveau/nv50_display.h @@ -74,6 +74,8 @@ void nv50_display_destroy(struct drm_device *dev); int nv50_crtc_blank(struct nouveau_crtc *, bool blank); int nv50_crtc_set_clock(struct drm_device *, int head, int pclk); +u32 nv50_display_active_crtcs(struct drm_device *); + int nv50_display_sync(struct drm_device *); int nv50_display_flip_next(struct drm_crtc *, struct drm_framebuffer *, struct nouveau_channel *chan); diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index 17eb3277b1a..109e473fd5f 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -28,6 +28,7 @@ #include "nouveau_hw.h" #include "nouveau_pm.h" #include "nouveau_hwsq.h" +#include "nv50_display.h" enum clk_src { clk_src_crystal, @@ -535,6 +536,7 @@ calc_mclk(struct drm_device *dev, struct nouveau_pm_level *perflvl, struct nv50_pm_state *info) { struct drm_nouveau_private *dev_priv = dev->dev_private; + u32 crtc_mask = nv50_display_active_crtcs(dev); struct nouveau_mem_exec_func exec = { .dev = dev, .precharge = mclk_precharge, @@ -550,9 +552,8 @@ calc_mclk(struct drm_device *dev, struct nouveau_pm_level *perflvl, }; struct hwsq_ucode *hwsq = &info->mclk_hwsq; struct pll_lims pll; - u32 crtc_mask = 0; int N, M, P; - int ret, i; + int ret; /* use pcie refclock if possible, otherwise use mpll */ info->mctrl = nv_rd32(dev, 0x004008); @@ -569,12 +570,6 @@ calc_mclk(struct drm_device *dev, struct nouveau_pm_level *perflvl, info->mcoef = (N << 8) | M; } - /* determine active crtcs */ - for (i = 0; i < 2; i++) { - if (nv_rd32(dev, NV50_PDISPLAY_CRTC_C(i, CLOCK))) - crtc_mask |= (1 << i); - } - /* build the ucode which will reclock the memory for us */ hwsq_init(hwsq); if (crtc_mask) { -- cgit v1.2.3-70-g09d2 From 496a73bbecb81e6753715995e4519d152f814667 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 24 Jan 2012 09:47:04 +1000 Subject: drm/nv50/pm: use hwsq for engine reclocking too Idea from Martin Peres, different implementation by me. v2: Martin Peres: - fix mast calculation Signed-off-by: Ben Skeggs Signed-off-by: Martin Peres --- drivers/gpu/drm/nouveau/nv50_pm.c | 185 ++++++++++++++++++++------------------ 1 file changed, 98 insertions(+), 87 deletions(-) (limited to 'drivers/gpu/drm/nouveau/nv50_pm.c') diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index 109e473fd5f..0f2632638c9 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -354,21 +354,12 @@ nv50_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) struct nv50_pm_state { struct nouveau_pm_level *perflvl; - + struct hwsq_ucode eclk_hwsq; struct hwsq_ucode mclk_hwsq; u32 mscript; u32 mmast; u32 mctrl; u32 mcoef; - - u32 emast; - u32 nctrl; - u32 ncoef; - u32 sctrl; - u32 scoef; - - u32 amast; - u32 pdivs; }; static u32 @@ -598,10 +589,11 @@ nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nv50_pm_state *info; + struct hwsq_ucode *hwsq; struct pll_lims pll; + u32 out, mast, divs, ctrl; int clk, ret = -EINVAL; int N, M, P1, P2; - u32 out; if (dev_priv->chipset == 0xaa || dev_priv->chipset == 0xac) @@ -622,41 +614,32 @@ nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) info->mscript = perflvl->memscript; } - /* core: for the moment at least, always use nvpll */ - clk = calc_pll(dev, 0x4028, &pll, perflvl->core, &N, &M, &P1); - if (clk == 0) - goto error; + divs = read_div(dev); + mast = info->mmast; - info->emast = 0x00000003; - info->nctrl = 0x80000000 | (P1 << 19) | (P1 << 16); - info->ncoef = (N << 8) | M; + /* start building HWSQ script for engine reclocking */ + hwsq = &info->eclk_hwsq; + hwsq_init(hwsq); + hwsq_setf(hwsq, 0x10, 0); /* disable bus access */ + hwsq_op5f(hwsq, 0x00, 0x01); /* wait for access disabled? */ - /* shader: tie to nvclk if possible, otherwise use spll. have to be - * very careful that the shader clock is at least twice the core, or - * some chipsets will be very unhappy. i expect most or all of these - * cases will be handled by tying to nvclk, but it's possible there's - * corners - */ - if (P1-- && perflvl->shader == (perflvl->core << 1)) { - info->emast |= 0x00000020; - info->sctrl = 0x00000000 | (P1 << 19) | (P1 << 16); - info->scoef = nv_rd32(dev, 0x004024); - } else { - clk = calc_pll(dev, 0x4020, &pll, perflvl->shader, &N, &M, &P1); - if (clk == 0) - goto error; + /* vdec/dom6: switch to "safe" clocks temporarily */ + if (perflvl->vdec) { + mast &= ~0x00000c00; + divs &= ~0x00000700; + } - info->emast |= 0x00000030; - info->sctrl = 0x80000000 | (P1 << 19) | (P1 << 16); - info->scoef = (N << 8) | M; + if (perflvl->dom6) { + mast &= ~0x0c000000; + divs &= ~0x00000007; } + hwsq_wr32(hwsq, 0x00c040, mast); + /* vdec: avoid modifying xpll until we know exactly how the other * clock domains work, i suspect at least some of them can also be * tied to xpll... */ - info->amast = nv_rd32(dev, 0x00c040); - info->pdivs = read_div(dev); if (perflvl->vdec) { /* see how close we can get using nvclk as a source */ clk = calc_div(perflvl->core, perflvl->vdec, &P1); @@ -669,16 +652,14 @@ nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) out = calc_div(out, perflvl->vdec, &P2); /* select whichever gets us closest */ - info->amast &= ~0x00000c00; - info->pdivs &= ~0x00000700; if (abs((int)perflvl->vdec - clk) <= abs((int)perflvl->vdec - out)) { if (dev_priv->chipset != 0x98) - info->amast |= 0x00000c00; - info->pdivs |= P1 << 8; + mast |= 0x00000c00; + divs |= P1 << 8; } else { - info->amast |= 0x00000800; - info->pdivs |= P2 << 8; + mast |= 0x00000800; + divs |= P2 << 8; } } @@ -686,21 +667,82 @@ nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) * of the host clock frequency */ if (perflvl->dom6) { - info->amast &= ~0x0c000000; if (clk_same(perflvl->dom6, read_clk(dev, clk_src_href))) { - info->amast |= 0x00000000; + mast |= 0x00000000; } else if (clk_same(perflvl->dom6, read_clk(dev, clk_src_hclk))) { - info->amast |= 0x08000000; + mast |= 0x08000000; } else { clk = read_clk(dev, clk_src_hclk) * 3; clk = calc_div(clk, perflvl->dom6, &P1); - info->amast |= 0x0c000000; - info->pdivs = (info->pdivs & ~0x00000007) | P1; + mast |= 0x0c000000; + divs |= P1; } } + /* vdec/dom6: complete switch to new clocks */ + switch (dev_priv->chipset) { + case 0x92: + case 0x94: + case 0x96: + hwsq_wr32(hwsq, 0x004800, divs); + break; + default: + hwsq_wr32(hwsq, 0x004700, divs); + break; + } + + hwsq_wr32(hwsq, 0x00c040, mast); + + /* core/shader: make sure sclk/nvclk are disconnected from their + * PLLs (nvclk to dom6, sclk to hclk) + */ + if (dev_priv->chipset < 0x92) + mast = (mast & ~0x001000b0) | 0x00100080; + else + mast = (mast & ~0x000000b3) | 0x00000081; + + hwsq_wr32(hwsq, 0x00c040, mast); + + /* core: for the moment at least, always use nvpll */ + clk = calc_pll(dev, 0x4028, &pll, perflvl->core, &N, &M, &P1); + if (clk == 0) + goto error; + + ctrl = nv_rd32(dev, 0x004028) & ~0xc03f0100; + mast &= ~0x00100000; + mast |= 3; + + hwsq_wr32(hwsq, 0x004028, 0x80000000 | (P1 << 19) | (P1 << 16) | ctrl); + hwsq_wr32(hwsq, 0x00402c, (N << 8) | M); + + /* shader: tie to nvclk if possible, otherwise use spll. have to be + * very careful that the shader clock is at least twice the core, or + * some chipsets will be very unhappy. i expect most or all of these + * cases will be handled by tying to nvclk, but it's possible there's + * corners + */ + ctrl = nv_rd32(dev, 0x004020) & ~0xc03f0100; + + if (P1-- && perflvl->shader == (perflvl->core << 1)) { + hwsq_wr32(hwsq, 0x004020, (P1 << 19) | (P1 << 16) | ctrl); + hwsq_wr32(hwsq, 0x00c040, 0x00000020 | mast); + } else { + clk = calc_pll(dev, 0x4020, &pll, perflvl->shader, &N, &M, &P1); + if (clk == 0) + goto error; + ctrl |= 0x80000000; + + hwsq_wr32(hwsq, 0x004020, (P1 << 19) | (P1 << 16) | ctrl); + hwsq_wr32(hwsq, 0x004024, (N << 8) | M); + hwsq_wr32(hwsq, 0x00c040, 0x00000030 | mast); + } + + hwsq_setf(hwsq, 0x10, 1); /* enable bus access */ + hwsq_op5f(hwsq, 0x00, 0x00); /* wait for access enabled? */ + hwsq_fini(hwsq); + return info; error: kfree(info); @@ -708,7 +750,7 @@ error: } static int -prog_mclk(struct drm_device *dev, struct hwsq_ucode *hwsq) +prog_hwsq(struct drm_device *dev, struct hwsq_ucode *hwsq) { struct drm_nouveau_private *dev_priv = dev->dev_private; u32 hwsq_data, hwsq_kick; @@ -748,20 +790,17 @@ prog_mclk(struct drm_device *dev, struct hwsq_ucode *hwsq) int nv50_pm_clocks_set(struct drm_device *dev, void *data) { - struct drm_nouveau_private *dev_priv = dev->dev_private; struct nv50_pm_state *info = data; struct bit_entry M; - int ret = 0; + int ret = -EBUSY; /* halt and idle execution engines */ nv_mask(dev, 0x002504, 0x00000001, 0x00000001); if (!nv_wait(dev, 0x002504, 0x00000010, 0x00000010)) - goto error; + goto resume; - /* memory: it is *very* important we change this first, the ucode - * we build in pre() now has hardcoded 0xc040 values, which can't - * change before we execute it or the engine clocks may end up - * messed up. + /* program memory clock, if necessary - must come before engine clock + * reprogramming due to how we construct the hwsq scripts in pre() */ if (info->mclk_hwsq.len) { /* execute some scripts that do ??? from the vbios.. */ @@ -775,42 +814,14 @@ nv50_pm_clocks_set(struct drm_device *dev, void *data) nouveau_bios_init_exec(dev, info->mscript); } - ret = prog_mclk(dev, &info->mclk_hwsq); + ret = prog_hwsq(dev, &info->mclk_hwsq); if (ret) goto resume; } - /* reclock vdec/dom6 */ - nv_mask(dev, 0x00c040, 0x00000c00, 0x00000000); - switch (dev_priv->chipset) { - case 0x92: - case 0x94: - case 0x96: - nv_mask(dev, 0x004800, 0x00000707, info->pdivs); - break; - default: - nv_mask(dev, 0x004700, 0x00000707, info->pdivs); - break; - } - nv_mask(dev, 0x00c040, 0x0c000c00, info->amast); + /* program engine clocks */ + ret = prog_hwsq(dev, &info->eclk_hwsq); - /* core/shader: make sure sclk/nvclk are disconnected from their - * plls (nvclk to dom6, sclk to hclk), modify the plls, and - * reconnect sclk/nvclk to their new clock source - */ - if (dev_priv->chipset < 0x92) - nv_mask(dev, 0x00c040, 0x001000b0, 0x00100080); /* grrr! */ - else - nv_mask(dev, 0x00c040, 0x000000b3, 0x00000081); - nv_mask(dev, 0x004020, 0xc03f0100, info->sctrl); - nv_wr32(dev, 0x004024, info->scoef); - nv_mask(dev, 0x004028, 0xc03f0100, info->nctrl); - nv_wr32(dev, 0x00402c, info->ncoef); - nv_mask(dev, 0x00c040, 0x00100033, info->emast); - - goto resume; -error: - ret = -EBUSY; resume: nv_mask(dev, 0x002504, 0x00000001, 0x00000000); kfree(info); -- cgit v1.2.3-70-g09d2 From c57ebf5ef3588d21031f12e39131d79071269845 Mon Sep 17 00:00:00 2001 From: Martin Peres Date: Mon, 9 Jan 2012 15:23:10 +1000 Subject: drm/nv50/pm: wait for all fifo-connected engines to idle before reclocking Signed-off-by: Martin Peres Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_pm.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/gpu/drm/nouveau/nv50_pm.c') diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index 0f2632638c9..e677310c8f5 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -798,6 +798,8 @@ nv50_pm_clocks_set(struct drm_device *dev, void *data) nv_mask(dev, 0x002504, 0x00000001, 0x00000001); if (!nv_wait(dev, 0x002504, 0x00000010, 0x00000010)) goto resume; + if (!nv_wait(dev, 0x00251c, 0x0000003f, 0x0000003f)) + goto resume; /* program memory clock, if necessary - must come before engine clock * reprogramming due to how we construct the hwsq scripts in pre() -- cgit v1.2.3-70-g09d2 From e436d1bb0a3e5ff27b190d0bf9173f4f6ad21d3f Mon Sep 17 00:00:00 2001 From: Martin Peres Date: Fri, 9 Mar 2012 00:15:01 +0100 Subject: drm/nv50/hwsq: some nv92 fixes The shift from hwsq_data = 0x1400 to 0x080000 actually happened in nv94, not nv92 This fixes some reclocking issues on my newly acquired nv92 Signed-off-by: Martin Peres Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nv50_pm.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/nouveau/nv50_pm.c') diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index e677310c8f5..d020ed4979b 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -756,17 +756,18 @@ prog_hwsq(struct drm_device *dev, struct hwsq_ucode *hwsq) u32 hwsq_data, hwsq_kick; int i; - if (dev_priv->chipset < 0x90) { + if (dev_priv->chipset < 0x94) { hwsq_data = 0x001400; hwsq_kick = 0x00000003; } else { hwsq_data = 0x080000; hwsq_kick = 0x00000001; } - /* upload hwsq ucode */ nv_mask(dev, 0x001098, 0x00000008, 0x00000000); nv_wr32(dev, 0x001304, 0x00000000); + if (dev_priv->chipset >= 0x92) + nv_wr32(dev, 0x001318, 0x00000000); for (i = 0; i < hwsq->len / 4; i++) nv_wr32(dev, hwsq_data + (i * 4), hwsq->ptr.u32[i]); nv_mask(dev, 0x001098, 0x00000018, 0x00000018); -- cgit v1.2.3-70-g09d2