Loading drivers/gpu/drm/nouveau/nv50_pm.c +154 −46 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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))) Loading @@ -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) Loading @@ -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); Loading Loading @@ -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... Loading Loading
drivers/gpu/drm/nouveau/nv50_pm.c +154 −46 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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))) Loading @@ -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) Loading @@ -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); Loading Loading @@ -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... Loading