aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBen Skeggs <bskeggs@redhat.com>2011-04-27 12:34:21 -0400
committerBen Skeggs <bskeggs@redhat.com>2011-05-15 20:50:59 -0400
commit52eba8dd5e830a836425e92d002bc51e42d3280e (patch)
tree88faa691a4828e7a3ca874e4d8d45a2a6feff23f
parent96d1fcf8b5a3a9c66fddeaa9fb71e4e68ee2e08b (diff)
drm/nva3/clk: better pll calculation when no fractional fb div available
The core/mem/shader clocks don't support the fractional feedback divider, causing our calculated clocks to be off by quite a lot in some cases. To solve this we will switch to a search-based algorithm when fN is NULL. For my NVA8 at PL3, this actually generates identical cooefficients to the binary driver. Hopefully that's a good sign, and that does not break VPLL calculation for someone.. Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.h4
-rw-r--r--drivers/gpu/drm/nouveau/nv50_calc.c68
-rw-r--r--drivers/gpu/drm/nouveau/nv50_crtc.c4
-rw-r--r--drivers/gpu/drm/nouveau/nva3_pm.c4
4 files changed, 43 insertions, 37 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 444a943283b1..9c56331941e2 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -1353,8 +1353,8 @@ bool nv50_gpio_irq_enable(struct drm_device *, enum dcb_gpio_tag, bool on);
1353/* nv50_calc. */ 1353/* nv50_calc. */
1354int nv50_calc_pll(struct drm_device *, struct pll_lims *, int clk, 1354int nv50_calc_pll(struct drm_device *, struct pll_lims *, int clk,
1355 int *N1, int *M1, int *N2, int *M2, int *P); 1355 int *N1, int *M1, int *N2, int *M2, int *P);
1356int nv50_calc_pll2(struct drm_device *, struct pll_lims *, 1356int nva3_calc_pll(struct drm_device *, struct pll_lims *,
1357 int clk, int *N, int *fN, int *M, int *P); 1357 int clk, int *N, int *fN, int *M, int *P);
1358 1358
1359#ifndef ioread32_native 1359#ifndef ioread32_native
1360#ifdef __BIG_ENDIAN 1360#ifdef __BIG_ENDIAN
diff --git a/drivers/gpu/drm/nouveau/nv50_calc.c b/drivers/gpu/drm/nouveau/nv50_calc.c
index de81151648f8..8cf63a8b30cd 100644
--- a/drivers/gpu/drm/nouveau/nv50_calc.c
+++ b/drivers/gpu/drm/nouveau/nv50_calc.c
@@ -23,7 +23,6 @@
23 */ 23 */
24 24
25#include "drmP.h" 25#include "drmP.h"
26#include "drm_fixed.h"
27#include "nouveau_drv.h" 26#include "nouveau_drv.h"
28#include "nouveau_hw.h" 27#include "nouveau_hw.h"
29 28
@@ -47,45 +46,52 @@ nv50_calc_pll(struct drm_device *dev, struct pll_lims *pll, int clk,
47} 46}
48 47
49int 48int
50nv50_calc_pll2(struct drm_device *dev, struct pll_lims *pll, int clk, 49nva3_calc_pll(struct drm_device *dev, struct pll_lims *pll, int clk,
51 int *N, int *fN, int *M, int *P) 50 int *pN, int *pfN, int *pM, int *P)
52{ 51{
53 fixed20_12 fb_div, a, b; 52 u32 best_err = ~0, err;
54 u32 refclk = pll->refclk / 10; 53 int M, lM, hM, N, fN;
55 u32 max_vco_freq = pll->vco1.maxfreq / 10;
56 u32 max_vco_inputfreq = pll->vco1.max_inputfreq / 10;
57 clk /= 10;
58 54
59 *P = max_vco_freq / clk; 55 *P = pll->vco1.maxfreq / clk;
60 if (*P > pll->max_p) 56 if (*P > pll->max_p)
61 *P = pll->max_p; 57 *P = pll->max_p;
62 if (*P < pll->min_p) 58 if (*P < pll->min_p)
63 *P = pll->min_p; 59 *P = pll->min_p;
64 60
65 /* *M = floor((refclk + max_vco_inputfreq) / max_vco_inputfreq); */ 61 lM = (pll->refclk + pll->vco1.max_inputfreq) / pll->vco1.max_inputfreq;
66 a.full = dfixed_const(refclk + max_vco_inputfreq); 62 lM = max(lM, (int)pll->vco1.min_m);
67 b.full = dfixed_const(max_vco_inputfreq); 63 hM = (pll->refclk + pll->vco1.min_inputfreq) / pll->vco1.min_inputfreq;
68 a.full = dfixed_div(a, b); 64 hM = min(hM, (int)pll->vco1.max_m);
69 a.full = dfixed_floor(a);
70 *M = dfixed_trunc(a);
71 65
72 /* fb_div = (vco * *M) / refclk; */ 66 for (M = lM; M <= hM; M++) {
73 fb_div.full = dfixed_const(clk * *P); 67 u32 tmp = clk * *P * M;
74 fb_div.full = dfixed_mul(fb_div, a); 68 N = tmp / pll->refclk;
75 a.full = dfixed_const(refclk); 69 fN = tmp % pll->refclk;
76 fb_div.full = dfixed_div(fb_div, a); 70 if (!pfN && fN >= pll->refclk / 2)
71 N++;
77 72
78 /* *N = floor(fb_div); */ 73 if (N < pll->vco1.min_n)
79 a.full = dfixed_floor(fb_div); 74 continue;
80 *N = dfixed_trunc(fb_div); 75 if (N > pll->vco1.max_n)
76 break;
81 77
82 /* *fN = (fmod(fb_div, 1.0) * 8192) - 4096; */ 78 err = abs(clk - (pll->refclk * N / M / *P));
83 b.full = dfixed_const(8192); 79 if (err < best_err) {
84 a.full = dfixed_mul(a, b); 80 best_err = err;
85 fb_div.full = dfixed_mul(fb_div, b); 81 *pN = N;
86 fb_div.full = fb_div.full - a.full; 82 *pM = M;
87 *fN = dfixed_trunc(fb_div) - 4096; 83 }
88 *fN &= 0xffff;
89 84
90 return clk; 85 if (pfN) {
86 *pfN = (((fN << 13) / pll->refclk) - 4096) & 0xffff;
87 return clk;
88 }
89 }
90
91 if (unlikely(best_err == ~0)) {
92 NV_ERROR(dev, "unable to find matching pll values\n");
93 return -EINVAL;
94 }
95
96 return pll->refclk * *pN / *pM / *P;
91} 97}
diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c
index e900a5135a00..b522a3a534c6 100644
--- a/drivers/gpu/drm/nouveau/nv50_crtc.c
+++ b/drivers/gpu/drm/nouveau/nv50_crtc.c
@@ -286,7 +286,7 @@ nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk)
286 nv_wr32(dev, pll.reg + 8, reg2 | (P << 28) | (M2 << 16) | N2); 286 nv_wr32(dev, pll.reg + 8, reg2 | (P << 28) | (M2 << 16) | N2);
287 } else 287 } else
288 if (dev_priv->chipset < NV_C0) { 288 if (dev_priv->chipset < NV_C0) {
289 ret = nv50_calc_pll2(dev, &pll, pclk, &N1, &N2, &M1, &P); 289 ret = nva3_calc_pll(dev, &pll, pclk, &N1, &N2, &M1, &P);
290 if (ret <= 0) 290 if (ret <= 0)
291 return 0; 291 return 0;
292 292
@@ -298,7 +298,7 @@ nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk)
298 nv_wr32(dev, pll.reg + 4, reg1 | (P << 16) | (M1 << 8) | N1); 298 nv_wr32(dev, pll.reg + 4, reg1 | (P << 16) | (M1 << 8) | N1);
299 nv_wr32(dev, pll.reg + 8, N2); 299 nv_wr32(dev, pll.reg + 8, N2);
300 } else { 300 } else {
301 ret = nv50_calc_pll2(dev, &pll, pclk, &N1, &N2, &M1, &P); 301 ret = nva3_calc_pll(dev, &pll, pclk, &N1, &N2, &M1, &P);
302 if (ret <= 0) 302 if (ret <= 0)
303 return 0; 303 return 0;
304 304
diff --git a/drivers/gpu/drm/nouveau/nva3_pm.c b/drivers/gpu/drm/nouveau/nva3_pm.c
index bc357c850dbd..e4b2b9e934b2 100644
--- a/drivers/gpu/drm/nouveau/nva3_pm.c
+++ b/drivers/gpu/drm/nouveau/nva3_pm.c
@@ -104,7 +104,7 @@ nva3_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl,
104{ 104{
105 struct nva3_pm_state *pll; 105 struct nva3_pm_state *pll;
106 struct pll_lims limits; 106 struct pll_lims limits;
107 int N, fN, M, P, diff; 107 int N, M, P, diff;
108 int ret, off; 108 int ret, off;
109 109
110 ret = get_pll_limits(dev, id, &limits); 110 ret = get_pll_limits(dev, id, &limits);
@@ -136,7 +136,7 @@ nva3_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl,
136 } 136 }
137 137
138 if (!pll->new_div) { 138 if (!pll->new_div) {
139 ret = nv50_calc_pll2(dev, &limits, khz, &N, &fN, &M, &P); 139 ret = nva3_calc_pll(dev, &limits, khz, &N, NULL, &M, &P);
140 if (ret < 0) 140 if (ret < 0)
141 return ERR_PTR(ret); 141 return ERR_PTR(ret);
142 142