diff options
Diffstat (limited to 'drivers/video/matrox/g450_pll.c')
-rw-r--r-- | drivers/video/matrox/g450_pll.c | 479 |
1 files changed, 479 insertions, 0 deletions
diff --git a/drivers/video/matrox/g450_pll.c b/drivers/video/matrox/g450_pll.c new file mode 100644 index 000000000000..8073a73f6f35 --- /dev/null +++ b/drivers/video/matrox/g450_pll.c | |||
@@ -0,0 +1,479 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Hardware accelerated Matrox PCI cards - G450/G550 PLL control. | ||
4 | * | ||
5 | * (c) 2001-2002 Petr Vandrovec <vandrove@vc.cvut.cz> | ||
6 | * | ||
7 | * Portions Copyright (c) 2001 Matrox Graphics Inc. | ||
8 | * | ||
9 | * Version: 1.64 2002/06/10 | ||
10 | * | ||
11 | * This file is subject to the terms and conditions of the GNU General Public | ||
12 | * License. See the file COPYING in the main directory of this archive for | ||
13 | * more details. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #include "g450_pll.h" | ||
18 | #include "matroxfb_DAC1064.h" | ||
19 | |||
20 | static inline unsigned int g450_vco2f(unsigned char p, unsigned int fvco) { | ||
21 | return (p & 0x40) ? fvco : fvco >> ((p & 3) + 1); | ||
22 | } | ||
23 | |||
24 | static inline unsigned int g450_f2vco(unsigned char p, unsigned int fin) { | ||
25 | return (p & 0x40) ? fin : fin << ((p & 3) + 1); | ||
26 | } | ||
27 | |||
28 | static unsigned int g450_mnp2vco(CPMINFO unsigned int mnp) { | ||
29 | unsigned int m, n; | ||
30 | |||
31 | m = ((mnp >> 16) & 0x0FF) + 1; | ||
32 | n = ((mnp >> 7) & 0x1FE) + 4; | ||
33 | return (ACCESS_FBINFO(features).pll.ref_freq * n + (m >> 1)) / m; | ||
34 | } | ||
35 | |||
36 | unsigned int g450_mnp2f(CPMINFO unsigned int mnp) { | ||
37 | return g450_vco2f(mnp, g450_mnp2vco(PMINFO mnp)); | ||
38 | } | ||
39 | |||
40 | static inline unsigned int pll_freq_delta(unsigned int f1, unsigned int f2) { | ||
41 | if (f2 < f1) { | ||
42 | f2 = f1 - f2; | ||
43 | } else { | ||
44 | f2 = f2 - f1; | ||
45 | } | ||
46 | return f2; | ||
47 | } | ||
48 | |||
49 | #define NO_MORE_MNP 0x01FFFFFF | ||
50 | #define G450_MNP_FREQBITS (0xFFFFFF43) /* do not mask high byte so we'll catch NO_MORE_MNP */ | ||
51 | |||
52 | static unsigned int g450_nextpll(CPMINFO const struct matrox_pll_limits* pi, unsigned int* fvco, unsigned int mnp) { | ||
53 | unsigned int m, n, p; | ||
54 | unsigned int tvco = *fvco; | ||
55 | |||
56 | m = (mnp >> 16) & 0xFF; | ||
57 | p = mnp & 0xFF; | ||
58 | |||
59 | do { | ||
60 | if (m == 0 || m == 0xFF) { | ||
61 | if (m == 0) { | ||
62 | if (p & 0x40) { | ||
63 | return NO_MORE_MNP; | ||
64 | } | ||
65 | if (p & 3) { | ||
66 | p--; | ||
67 | } else { | ||
68 | p = 0x40; | ||
69 | } | ||
70 | tvco >>= 1; | ||
71 | if (tvco < pi->vcomin) { | ||
72 | return NO_MORE_MNP; | ||
73 | } | ||
74 | *fvco = tvco; | ||
75 | } | ||
76 | |||
77 | p &= 0x43; | ||
78 | if (tvco < 550000) { | ||
79 | /* p |= 0x00; */ | ||
80 | } else if (tvco < 700000) { | ||
81 | p |= 0x08; | ||
82 | } else if (tvco < 1000000) { | ||
83 | p |= 0x10; | ||
84 | } else if (tvco < 1150000) { | ||
85 | p |= 0x18; | ||
86 | } else { | ||
87 | p |= 0x20; | ||
88 | } | ||
89 | m = 9; | ||
90 | } else { | ||
91 | m--; | ||
92 | } | ||
93 | n = ((tvco * (m+1) + ACCESS_FBINFO(features).pll.ref_freq) / (ACCESS_FBINFO(features).pll.ref_freq * 2)) - 2; | ||
94 | } while (n < 0x03 || n > 0x7A); | ||
95 | return (m << 16) | (n << 8) | p; | ||
96 | } | ||
97 | |||
98 | static unsigned int g450_firstpll(CPMINFO const struct matrox_pll_limits* pi, unsigned int* vco, unsigned int fout) { | ||
99 | unsigned int p; | ||
100 | unsigned int vcomax; | ||
101 | |||
102 | vcomax = pi->vcomax; | ||
103 | if (fout > (vcomax / 2)) { | ||
104 | if (fout > vcomax) { | ||
105 | *vco = vcomax; | ||
106 | } else { | ||
107 | *vco = fout; | ||
108 | } | ||
109 | p = 0x40; | ||
110 | } else { | ||
111 | unsigned int tvco; | ||
112 | |||
113 | p = 3; | ||
114 | tvco = g450_f2vco(p, fout); | ||
115 | while (p && (tvco > vcomax)) { | ||
116 | p--; | ||
117 | tvco >>= 1; | ||
118 | } | ||
119 | if (tvco < pi->vcomin) { | ||
120 | tvco = pi->vcomin; | ||
121 | } | ||
122 | *vco = tvco; | ||
123 | } | ||
124 | return g450_nextpll(PMINFO pi, vco, 0xFF0000 | p); | ||
125 | } | ||
126 | |||
127 | static inline unsigned int g450_setpll(CPMINFO unsigned int mnp, unsigned int pll) { | ||
128 | switch (pll) { | ||
129 | case M_PIXEL_PLL_A: | ||
130 | matroxfb_DAC_out(PMINFO M1064_XPIXPLLAM, mnp >> 16); | ||
131 | matroxfb_DAC_out(PMINFO M1064_XPIXPLLAN, mnp >> 8); | ||
132 | matroxfb_DAC_out(PMINFO M1064_XPIXPLLAP, mnp); | ||
133 | return M1064_XPIXPLLSTAT; | ||
134 | |||
135 | case M_PIXEL_PLL_B: | ||
136 | matroxfb_DAC_out(PMINFO M1064_XPIXPLLBM, mnp >> 16); | ||
137 | matroxfb_DAC_out(PMINFO M1064_XPIXPLLBN, mnp >> 8); | ||
138 | matroxfb_DAC_out(PMINFO M1064_XPIXPLLBP, mnp); | ||
139 | return M1064_XPIXPLLSTAT; | ||
140 | |||
141 | case M_PIXEL_PLL_C: | ||
142 | matroxfb_DAC_out(PMINFO M1064_XPIXPLLCM, mnp >> 16); | ||
143 | matroxfb_DAC_out(PMINFO M1064_XPIXPLLCN, mnp >> 8); | ||
144 | matroxfb_DAC_out(PMINFO M1064_XPIXPLLCP, mnp); | ||
145 | return M1064_XPIXPLLSTAT; | ||
146 | |||
147 | case M_SYSTEM_PLL: | ||
148 | matroxfb_DAC_out(PMINFO DAC1064_XSYSPLLM, mnp >> 16); | ||
149 | matroxfb_DAC_out(PMINFO DAC1064_XSYSPLLN, mnp >> 8); | ||
150 | matroxfb_DAC_out(PMINFO DAC1064_XSYSPLLP, mnp); | ||
151 | return DAC1064_XSYSPLLSTAT; | ||
152 | |||
153 | case M_VIDEO_PLL: | ||
154 | matroxfb_DAC_out(PMINFO M1064_XVIDPLLM, mnp >> 16); | ||
155 | matroxfb_DAC_out(PMINFO M1064_XVIDPLLN, mnp >> 8); | ||
156 | matroxfb_DAC_out(PMINFO M1064_XVIDPLLP, mnp); | ||
157 | return M1064_XVIDPLLSTAT; | ||
158 | } | ||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | static inline unsigned int g450_cmppll(CPMINFO unsigned int mnp, unsigned int pll) { | ||
163 | unsigned char m = mnp >> 16; | ||
164 | unsigned char n = mnp >> 8; | ||
165 | unsigned char p = mnp; | ||
166 | |||
167 | switch (pll) { | ||
168 | case M_PIXEL_PLL_A: | ||
169 | return (matroxfb_DAC_in(PMINFO M1064_XPIXPLLAM) != m || | ||
170 | matroxfb_DAC_in(PMINFO M1064_XPIXPLLAN) != n || | ||
171 | matroxfb_DAC_in(PMINFO M1064_XPIXPLLAP) != p); | ||
172 | |||
173 | case M_PIXEL_PLL_B: | ||
174 | return (matroxfb_DAC_in(PMINFO M1064_XPIXPLLBM) != m || | ||
175 | matroxfb_DAC_in(PMINFO M1064_XPIXPLLBN) != n || | ||
176 | matroxfb_DAC_in(PMINFO M1064_XPIXPLLBP) != p); | ||
177 | |||
178 | case M_PIXEL_PLL_C: | ||
179 | return (matroxfb_DAC_in(PMINFO M1064_XPIXPLLCM) != m || | ||
180 | matroxfb_DAC_in(PMINFO M1064_XPIXPLLCN) != n || | ||
181 | matroxfb_DAC_in(PMINFO M1064_XPIXPLLCP) != p); | ||
182 | |||
183 | case M_SYSTEM_PLL: | ||
184 | return (matroxfb_DAC_in(PMINFO DAC1064_XSYSPLLM) != m || | ||
185 | matroxfb_DAC_in(PMINFO DAC1064_XSYSPLLN) != n || | ||
186 | matroxfb_DAC_in(PMINFO DAC1064_XSYSPLLP) != p); | ||
187 | |||
188 | case M_VIDEO_PLL: | ||
189 | return (matroxfb_DAC_in(PMINFO M1064_XVIDPLLM) != m || | ||
190 | matroxfb_DAC_in(PMINFO M1064_XVIDPLLN) != n || | ||
191 | matroxfb_DAC_in(PMINFO M1064_XVIDPLLP) != p); | ||
192 | } | ||
193 | return 1; | ||
194 | } | ||
195 | |||
196 | static inline int g450_isplllocked(CPMINFO unsigned int regidx) { | ||
197 | unsigned int j; | ||
198 | |||
199 | for (j = 0; j < 1000; j++) { | ||
200 | if (matroxfb_DAC_in(PMINFO regidx) & 0x40) { | ||
201 | unsigned int r = 0; | ||
202 | int i; | ||
203 | |||
204 | for (i = 0; i < 100; i++) { | ||
205 | r += matroxfb_DAC_in(PMINFO regidx) & 0x40; | ||
206 | } | ||
207 | return r >= (90 * 0x40); | ||
208 | } | ||
209 | /* udelay(1)... but DAC_in is much slower... */ | ||
210 | } | ||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | static int g450_testpll(CPMINFO unsigned int mnp, unsigned int pll) { | ||
215 | return g450_isplllocked(PMINFO g450_setpll(PMINFO mnp, pll)); | ||
216 | } | ||
217 | |||
218 | static void updatehwstate_clk(struct matrox_hw_state* hw, unsigned int mnp, unsigned int pll) { | ||
219 | switch (pll) { | ||
220 | case M_SYSTEM_PLL: | ||
221 | hw->DACclk[3] = mnp >> 16; | ||
222 | hw->DACclk[4] = mnp >> 8; | ||
223 | hw->DACclk[5] = mnp; | ||
224 | break; | ||
225 | } | ||
226 | } | ||
227 | |||
228 | void matroxfb_g450_setpll_cond(WPMINFO unsigned int mnp, unsigned int pll) { | ||
229 | if (g450_cmppll(PMINFO mnp, pll)) { | ||
230 | g450_setpll(PMINFO mnp, pll); | ||
231 | } | ||
232 | } | ||
233 | |||
234 | static inline unsigned int g450_findworkingpll(WPMINFO unsigned int pll, unsigned int* mnparray, unsigned int mnpcount) { | ||
235 | unsigned int found = 0; | ||
236 | unsigned int idx; | ||
237 | unsigned int mnpfound = mnparray[0]; | ||
238 | |||
239 | for (idx = 0; idx < mnpcount; idx++) { | ||
240 | unsigned int sarray[3]; | ||
241 | unsigned int *sptr; | ||
242 | { | ||
243 | unsigned int mnp; | ||
244 | |||
245 | sptr = sarray; | ||
246 | mnp = mnparray[idx]; | ||
247 | if (mnp & 0x38) { | ||
248 | *sptr++ = mnp - 8; | ||
249 | } | ||
250 | if ((mnp & 0x38) != 0x38) { | ||
251 | *sptr++ = mnp + 8; | ||
252 | } | ||
253 | *sptr = mnp; | ||
254 | } | ||
255 | while (sptr >= sarray) { | ||
256 | unsigned int mnp = *sptr--; | ||
257 | |||
258 | if (g450_testpll(PMINFO mnp - 0x0300, pll) && | ||
259 | g450_testpll(PMINFO mnp + 0x0300, pll) && | ||
260 | g450_testpll(PMINFO mnp - 0x0200, pll) && | ||
261 | g450_testpll(PMINFO mnp + 0x0200, pll) && | ||
262 | g450_testpll(PMINFO mnp - 0x0100, pll) && | ||
263 | g450_testpll(PMINFO mnp + 0x0100, pll)) { | ||
264 | if (g450_testpll(PMINFO mnp, pll)) { | ||
265 | return mnp; | ||
266 | } | ||
267 | } else if (!found && g450_testpll(PMINFO mnp, pll)) { | ||
268 | mnpfound = mnp; | ||
269 | found = 1; | ||
270 | } | ||
271 | } | ||
272 | } | ||
273 | g450_setpll(PMINFO mnpfound, pll); | ||
274 | return mnpfound; | ||
275 | } | ||
276 | |||
277 | static void g450_addcache(struct matrox_pll_cache* ci, unsigned int mnp_key, unsigned int mnp_value) { | ||
278 | if (++ci->valid > ARRAY_SIZE(ci->data)) { | ||
279 | ci->valid = ARRAY_SIZE(ci->data); | ||
280 | } | ||
281 | memmove(ci->data + 1, ci->data, (ci->valid - 1) * sizeof(*ci->data)); | ||
282 | ci->data[0].mnp_key = mnp_key & G450_MNP_FREQBITS; | ||
283 | ci->data[0].mnp_value = mnp_value; | ||
284 | } | ||
285 | |||
286 | static int g450_checkcache(WPMINFO struct matrox_pll_cache* ci, unsigned int mnp_key) { | ||
287 | unsigned int i; | ||
288 | |||
289 | mnp_key &= G450_MNP_FREQBITS; | ||
290 | for (i = 0; i < ci->valid; i++) { | ||
291 | if (ci->data[i].mnp_key == mnp_key) { | ||
292 | unsigned int mnp; | ||
293 | |||
294 | mnp = ci->data[i].mnp_value; | ||
295 | if (i) { | ||
296 | memmove(ci->data + 1, ci->data, i * sizeof(*ci->data)); | ||
297 | ci->data[0].mnp_key = mnp_key; | ||
298 | ci->data[0].mnp_value = mnp; | ||
299 | } | ||
300 | return mnp; | ||
301 | } | ||
302 | } | ||
303 | return NO_MORE_MNP; | ||
304 | } | ||
305 | |||
306 | static int __g450_setclk(WPMINFO unsigned int fout, unsigned int pll, | ||
307 | unsigned int* mnparray, unsigned int* deltaarray) { | ||
308 | unsigned int mnpcount; | ||
309 | unsigned int pixel_vco; | ||
310 | const struct matrox_pll_limits* pi; | ||
311 | struct matrox_pll_cache* ci; | ||
312 | |||
313 | pixel_vco = 0; | ||
314 | switch (pll) { | ||
315 | case M_PIXEL_PLL_A: | ||
316 | case M_PIXEL_PLL_B: | ||
317 | case M_PIXEL_PLL_C: | ||
318 | { | ||
319 | u_int8_t tmp; | ||
320 | unsigned long flags; | ||
321 | |||
322 | matroxfb_DAC_lock_irqsave(flags); | ||
323 | tmp = matroxfb_DAC_in(PMINFO M1064_XPIXCLKCTRL); | ||
324 | if (!(tmp & M1064_XPIXCLKCTRL_PLL_UP)) { | ||
325 | matroxfb_DAC_out(PMINFO M1064_XPIXCLKCTRL, tmp | M1064_XPIXCLKCTRL_PLL_UP); | ||
326 | } | ||
327 | matroxfb_DAC_unlock_irqrestore(flags); | ||
328 | } | ||
329 | { | ||
330 | u_int8_t misc; | ||
331 | |||
332 | misc = mga_inb(M_MISC_REG_READ) & ~0x0C; | ||
333 | switch (pll) { | ||
334 | case M_PIXEL_PLL_A: | ||
335 | break; | ||
336 | case M_PIXEL_PLL_B: | ||
337 | misc |= 0x04; | ||
338 | break; | ||
339 | default: | ||
340 | misc |= 0x0C; | ||
341 | break; | ||
342 | } | ||
343 | mga_outb(M_MISC_REG, misc); | ||
344 | } | ||
345 | pi = &ACCESS_FBINFO(limits.pixel); | ||
346 | ci = &ACCESS_FBINFO(cache.pixel); | ||
347 | break; | ||
348 | case M_SYSTEM_PLL: | ||
349 | { | ||
350 | u_int32_t opt; | ||
351 | |||
352 | pci_read_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, &opt); | ||
353 | if (!(opt & 0x20)) { | ||
354 | pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, opt | 0x20); | ||
355 | } | ||
356 | } | ||
357 | pi = &ACCESS_FBINFO(limits.system); | ||
358 | ci = &ACCESS_FBINFO(cache.system); | ||
359 | break; | ||
360 | case M_VIDEO_PLL: | ||
361 | { | ||
362 | u_int8_t tmp; | ||
363 | unsigned int mnp; | ||
364 | unsigned long flags; | ||
365 | |||
366 | matroxfb_DAC_lock_irqsave(flags); | ||
367 | tmp = matroxfb_DAC_in(PMINFO M1064_XPWRCTRL); | ||
368 | if (!(tmp & 2)) { | ||
369 | matroxfb_DAC_out(PMINFO M1064_XPWRCTRL, tmp | 2); | ||
370 | } | ||
371 | |||
372 | mnp = matroxfb_DAC_in(PMINFO M1064_XPIXPLLCM) << 16; | ||
373 | mnp |= matroxfb_DAC_in(PMINFO M1064_XPIXPLLCN) << 8; | ||
374 | pixel_vco = g450_mnp2vco(PMINFO mnp); | ||
375 | matroxfb_DAC_unlock_irqrestore(flags); | ||
376 | } | ||
377 | pi = &ACCESS_FBINFO(limits.video); | ||
378 | ci = &ACCESS_FBINFO(cache.video); | ||
379 | break; | ||
380 | default: | ||
381 | return -EINVAL; | ||
382 | } | ||
383 | |||
384 | mnpcount = 0; | ||
385 | { | ||
386 | unsigned int mnp; | ||
387 | unsigned int xvco; | ||
388 | |||
389 | for(mnp = g450_firstpll(PMINFO pi, &xvco, fout); mnp != NO_MORE_MNP; mnp = g450_nextpll(PMINFO pi, &xvco, mnp)) { | ||
390 | unsigned int idx; | ||
391 | unsigned int vco; | ||
392 | unsigned int delta; | ||
393 | |||
394 | vco = g450_mnp2vco(PMINFO mnp); | ||
395 | #if 0 | ||
396 | if (pll == M_VIDEO_PLL) { | ||
397 | unsigned int big, small; | ||
398 | |||
399 | if (vco < pixel_vco) { | ||
400 | small = vco; | ||
401 | big = pixel_vco; | ||
402 | } else { | ||
403 | small = pixel_vco; | ||
404 | big = vco; | ||
405 | } | ||
406 | while (big > small) { | ||
407 | big >>= 1; | ||
408 | } | ||
409 | if (big == small) { | ||
410 | continue; | ||
411 | } | ||
412 | } | ||
413 | #endif | ||
414 | delta = pll_freq_delta(fout, g450_vco2f(mnp, vco)); | ||
415 | for (idx = mnpcount; idx > 0; idx--) { | ||
416 | /* == is important; due to nextpll algorithm we get | ||
417 | sorted equally good frequencies from lower VCO | ||
418 | frequency to higher - with <= lowest wins, while | ||
419 | with < highest one wins */ | ||
420 | if (delta <= deltaarray[idx-1]) { | ||
421 | mnparray[idx] = mnparray[idx-1]; | ||
422 | deltaarray[idx] = deltaarray[idx-1]; | ||
423 | } else { | ||
424 | break; | ||
425 | } | ||
426 | } | ||
427 | mnparray[idx] = mnp; | ||
428 | deltaarray[idx] = delta; | ||
429 | mnpcount++; | ||
430 | } | ||
431 | } | ||
432 | /* VideoPLL and PixelPLL matched: do nothing... In all other cases we should get at least one frequency */ | ||
433 | if (!mnpcount) { | ||
434 | return -EBUSY; | ||
435 | } | ||
436 | { | ||
437 | unsigned long flags; | ||
438 | unsigned int mnp; | ||
439 | |||
440 | matroxfb_DAC_lock_irqsave(flags); | ||
441 | mnp = g450_checkcache(PMINFO ci, mnparray[0]); | ||
442 | if (mnp != NO_MORE_MNP) { | ||
443 | matroxfb_g450_setpll_cond(PMINFO mnp, pll); | ||
444 | } else { | ||
445 | mnp = g450_findworkingpll(PMINFO pll, mnparray, mnpcount); | ||
446 | g450_addcache(ci, mnparray[0], mnp); | ||
447 | } | ||
448 | updatehwstate_clk(&ACCESS_FBINFO(hw), mnp, pll); | ||
449 | matroxfb_DAC_unlock_irqrestore(flags); | ||
450 | return mnp; | ||
451 | } | ||
452 | } | ||
453 | |||
454 | /* It must be greater than number of possible PLL values. | ||
455 | * Currently there is 5(p) * 10(m) = 50 possible values. */ | ||
456 | #define MNP_TABLE_SIZE 64 | ||
457 | |||
458 | int matroxfb_g450_setclk(WPMINFO unsigned int fout, unsigned int pll) { | ||
459 | unsigned int* arr; | ||
460 | |||
461 | arr = kmalloc(sizeof(*arr) * MNP_TABLE_SIZE * 2, GFP_KERNEL); | ||
462 | if (arr) { | ||
463 | int r; | ||
464 | |||
465 | r = __g450_setclk(PMINFO fout, pll, arr, arr + MNP_TABLE_SIZE); | ||
466 | kfree(arr); | ||
467 | return r; | ||
468 | } | ||
469 | return -ENOMEM; | ||
470 | } | ||
471 | |||
472 | EXPORT_SYMBOL(matroxfb_g450_setclk); | ||
473 | EXPORT_SYMBOL(g450_mnp2f); | ||
474 | EXPORT_SYMBOL(matroxfb_g450_setpll_cond); | ||
475 | |||
476 | MODULE_AUTHOR("(c) 2001-2002 Petr Vandrovec <vandrove@vc.cvut.cz>"); | ||
477 | MODULE_DESCRIPTION("Matrox G450/G550 PLL driver"); | ||
478 | |||
479 | MODULE_LICENSE("GPL"); | ||