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