diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2011-04-27 12:34:21 -0400 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2011-05-15 20:50:59 -0400 |
commit | 52eba8dd5e830a836425e92d002bc51e42d3280e (patch) | |
tree | 88faa691a4828e7a3ca874e4d8d45a2a6feff23f /drivers/gpu | |
parent | 96d1fcf8b5a3a9c66fddeaa9fb71e4e68ee2e08b (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>
Diffstat (limited to 'drivers/gpu')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drv.h | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nv50_calc.c | 68 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nv50_crtc.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nva3_pm.c | 4 |
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. */ |
1354 | int nv50_calc_pll(struct drm_device *, struct pll_lims *, int clk, | 1354 | int 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); |
1356 | int nv50_calc_pll2(struct drm_device *, struct pll_lims *, | 1356 | int 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 | ||
49 | int | 48 | int |
50 | nv50_calc_pll2(struct drm_device *dev, struct pll_lims *pll, int clk, | 49 | nva3_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 | ||