diff options
Diffstat (limited to 'drivers/video/matrox/matroxfb_misc.c')
-rw-r--r-- | drivers/video/matrox/matroxfb_misc.c | 777 |
1 files changed, 777 insertions, 0 deletions
diff --git a/drivers/video/matrox/matroxfb_misc.c b/drivers/video/matrox/matroxfb_misc.c new file mode 100644 index 000000000000..76fd3a519b8a --- /dev/null +++ b/drivers/video/matrox/matroxfb_misc.c | |||
@@ -0,0 +1,777 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200 and G400 | ||
4 | * | ||
5 | * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz> | ||
6 | * | ||
7 | * Portions Copyright (c) 2001 Matrox Graphics Inc. | ||
8 | * | ||
9 | * Version: 1.65 2002/08/14 | ||
10 | * | ||
11 | * MTRR stuff: 1998 Tom Rini <trini@kernel.crashing.org> | ||
12 | * | ||
13 | * Contributors: "menion?" <menion@mindless.com> | ||
14 | * Betatesting, fixes, ideas | ||
15 | * | ||
16 | * "Kurt Garloff" <garloff@suse.de> | ||
17 | * Betatesting, fixes, ideas, videomodes, videomodes timmings | ||
18 | * | ||
19 | * "Tom Rini" <trini@kernel.crashing.org> | ||
20 | * MTRR stuff, PPC cleanups, betatesting, fixes, ideas | ||
21 | * | ||
22 | * "Bibek Sahu" <scorpio@dodds.net> | ||
23 | * Access device through readb|w|l and write b|w|l | ||
24 | * Extensive debugging stuff | ||
25 | * | ||
26 | * "Daniel Haun" <haund@usa.net> | ||
27 | * Testing, hardware cursor fixes | ||
28 | * | ||
29 | * "Scott Wood" <sawst46+@pitt.edu> | ||
30 | * Fixes | ||
31 | * | ||
32 | * "Gerd Knorr" <kraxel@goldbach.isdn.cs.tu-berlin.de> | ||
33 | * Betatesting | ||
34 | * | ||
35 | * "Kelly French" <targon@hazmat.com> | ||
36 | * "Fernando Herrera" <fherrera@eurielec.etsit.upm.es> | ||
37 | * Betatesting, bug reporting | ||
38 | * | ||
39 | * "Pablo Bianucci" <pbian@pccp.com.ar> | ||
40 | * Fixes, ideas, betatesting | ||
41 | * | ||
42 | * "Inaky Perez Gonzalez" <inaky@peloncho.fis.ucm.es> | ||
43 | * Fixes, enhandcements, ideas, betatesting | ||
44 | * | ||
45 | * "Ryuichi Oikawa" <roikawa@rr.iiij4u.or.jp> | ||
46 | * PPC betatesting, PPC support, backward compatibility | ||
47 | * | ||
48 | * "Paul Womar" <Paul@pwomar.demon.co.uk> | ||
49 | * "Owen Waller" <O.Waller@ee.qub.ac.uk> | ||
50 | * PPC betatesting | ||
51 | * | ||
52 | * "Thomas Pornin" <pornin@bolet.ens.fr> | ||
53 | * Alpha betatesting | ||
54 | * | ||
55 | * "Pieter van Leuven" <pvl@iae.nl> | ||
56 | * "Ulf Jaenicke-Roessler" <ujr@physik.phy.tu-dresden.de> | ||
57 | * G100 testing | ||
58 | * | ||
59 | * "H. Peter Arvin" <hpa@transmeta.com> | ||
60 | * Ideas | ||
61 | * | ||
62 | * "Cort Dougan" <cort@cs.nmt.edu> | ||
63 | * CHRP fixes and PReP cleanup | ||
64 | * | ||
65 | * "Mark Vojkovich" <mvojkovi@ucsd.edu> | ||
66 | * G400 support | ||
67 | * | ||
68 | * "David C. Hansen" <haveblue@us.ibm.com> | ||
69 | * Fixes | ||
70 | * | ||
71 | * (following author is not in any relation with this code, but his code | ||
72 | * is included in this driver) | ||
73 | * | ||
74 | * Based on framebuffer driver for VBE 2.0 compliant graphic boards | ||
75 | * (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de> | ||
76 | * | ||
77 | * (following author is not in any relation with this code, but his ideas | ||
78 | * were used when writting this driver) | ||
79 | * | ||
80 | * FreeVBE/AF (Matrox), "Shawn Hargreaves" <shawn@talula.demon.co.uk> | ||
81 | * | ||
82 | */ | ||
83 | |||
84 | /* make checkconfig does not check includes for this... */ | ||
85 | #include <linux/config.h> | ||
86 | |||
87 | #include "matroxfb_misc.h" | ||
88 | #include <linux/interrupt.h> | ||
89 | #include <linux/matroxfb.h> | ||
90 | |||
91 | void matroxfb_DAC_out(CPMINFO int reg, int val) { | ||
92 | DBG_REG(__FUNCTION__) | ||
93 | mga_outb(M_RAMDAC_BASE+M_X_INDEX, reg); | ||
94 | mga_outb(M_RAMDAC_BASE+M_X_DATAREG, val); | ||
95 | } | ||
96 | |||
97 | int matroxfb_DAC_in(CPMINFO int reg) { | ||
98 | DBG_REG(__FUNCTION__) | ||
99 | mga_outb(M_RAMDAC_BASE+M_X_INDEX, reg); | ||
100 | return mga_inb(M_RAMDAC_BASE+M_X_DATAREG); | ||
101 | } | ||
102 | |||
103 | void matroxfb_var2my(struct fb_var_screeninfo* var, struct my_timming* mt) { | ||
104 | unsigned int pixclock = var->pixclock; | ||
105 | |||
106 | DBG(__FUNCTION__) | ||
107 | |||
108 | if (!pixclock) pixclock = 10000; /* 10ns = 100MHz */ | ||
109 | mt->pixclock = 1000000000 / pixclock; | ||
110 | if (mt->pixclock < 1) mt->pixclock = 1; | ||
111 | mt->mnp = -1; | ||
112 | mt->dblscan = var->vmode & FB_VMODE_DOUBLE; | ||
113 | mt->interlaced = var->vmode & FB_VMODE_INTERLACED; | ||
114 | mt->HDisplay = var->xres; | ||
115 | mt->HSyncStart = mt->HDisplay + var->right_margin; | ||
116 | mt->HSyncEnd = mt->HSyncStart + var->hsync_len; | ||
117 | mt->HTotal = mt->HSyncEnd + var->left_margin; | ||
118 | mt->VDisplay = var->yres; | ||
119 | mt->VSyncStart = mt->VDisplay + var->lower_margin; | ||
120 | mt->VSyncEnd = mt->VSyncStart + var->vsync_len; | ||
121 | mt->VTotal = mt->VSyncEnd + var->upper_margin; | ||
122 | mt->sync = var->sync; | ||
123 | } | ||
124 | |||
125 | int matroxfb_PLL_calcclock(const struct matrox_pll_features* pll, unsigned int freq, unsigned int fmax, | ||
126 | unsigned int* in, unsigned int* feed, unsigned int* post) { | ||
127 | unsigned int bestdiff = ~0; | ||
128 | unsigned int bestvco = 0; | ||
129 | unsigned int fxtal = pll->ref_freq; | ||
130 | unsigned int fwant; | ||
131 | unsigned int p; | ||
132 | |||
133 | DBG(__FUNCTION__) | ||
134 | |||
135 | fwant = freq; | ||
136 | |||
137 | #ifdef DEBUG | ||
138 | printk(KERN_ERR "post_shift_max: %d\n", pll->post_shift_max); | ||
139 | printk(KERN_ERR "ref_freq: %d\n", pll->ref_freq); | ||
140 | printk(KERN_ERR "freq: %d\n", freq); | ||
141 | printk(KERN_ERR "vco_freq_min: %d\n", pll->vco_freq_min); | ||
142 | printk(KERN_ERR "in_div_min: %d\n", pll->in_div_min); | ||
143 | printk(KERN_ERR "in_div_max: %d\n", pll->in_div_max); | ||
144 | printk(KERN_ERR "feed_div_min: %d\n", pll->feed_div_min); | ||
145 | printk(KERN_ERR "feed_div_max: %d\n", pll->feed_div_max); | ||
146 | printk(KERN_ERR "fmax: %d\n", fmax); | ||
147 | #endif | ||
148 | for (p = 1; p <= pll->post_shift_max; p++) { | ||
149 | if (fwant * 2 > fmax) | ||
150 | break; | ||
151 | fwant *= 2; | ||
152 | } | ||
153 | if (fwant < pll->vco_freq_min) fwant = pll->vco_freq_min; | ||
154 | if (fwant > fmax) fwant = fmax; | ||
155 | for (; p-- > 0; fwant >>= 1, bestdiff >>= 1) { | ||
156 | unsigned int m; | ||
157 | |||
158 | if (fwant < pll->vco_freq_min) break; | ||
159 | for (m = pll->in_div_min; m <= pll->in_div_max; m++) { | ||
160 | unsigned int diff, fvco; | ||
161 | unsigned int n; | ||
162 | |||
163 | n = (fwant * (m + 1) + (fxtal >> 1)) / fxtal - 1; | ||
164 | if (n > pll->feed_div_max) | ||
165 | break; | ||
166 | if (n < pll->feed_div_min) | ||
167 | n = pll->feed_div_min; | ||
168 | fvco = (fxtal * (n + 1)) / (m + 1); | ||
169 | if (fvco < fwant) | ||
170 | diff = fwant - fvco; | ||
171 | else | ||
172 | diff = fvco - fwant; | ||
173 | if (diff < bestdiff) { | ||
174 | bestdiff = diff; | ||
175 | *post = p; | ||
176 | *in = m; | ||
177 | *feed = n; | ||
178 | bestvco = fvco; | ||
179 | } | ||
180 | } | ||
181 | } | ||
182 | dprintk(KERN_ERR "clk: %02X %02X %02X %d %d %d\n", *in, *feed, *post, fxtal, bestvco, fwant); | ||
183 | return bestvco; | ||
184 | } | ||
185 | |||
186 | int matroxfb_vgaHWinit(WPMINFO struct my_timming* m) { | ||
187 | unsigned int hd, hs, he, hbe, ht; | ||
188 | unsigned int vd, vs, ve, vt, lc; | ||
189 | unsigned int wd; | ||
190 | unsigned int divider; | ||
191 | int i; | ||
192 | int fwidth; | ||
193 | struct matrox_hw_state * const hw = &ACCESS_FBINFO(hw); | ||
194 | |||
195 | fwidth = 8; | ||
196 | |||
197 | DBG(__FUNCTION__) | ||
198 | |||
199 | hw->SEQ[0] = 0x00; | ||
200 | if (fwidth == 9) | ||
201 | hw->SEQ[1] = 0x00; | ||
202 | else | ||
203 | hw->SEQ[1] = 0x01; /* or 0x09 */ | ||
204 | hw->SEQ[2] = 0x0F; /* bitplanes */ | ||
205 | hw->SEQ[3] = 0x00; | ||
206 | hw->SEQ[4] = 0x0E; | ||
207 | /* CRTC 0..7, 9, 16..19, 21, 22 are reprogrammed by Matrox Millennium code... Hope that by MGA1064 too */ | ||
208 | if (m->dblscan) { | ||
209 | m->VTotal <<= 1; | ||
210 | m->VDisplay <<= 1; | ||
211 | m->VSyncStart <<= 1; | ||
212 | m->VSyncEnd <<= 1; | ||
213 | } | ||
214 | if (m->interlaced) { | ||
215 | m->VTotal >>= 1; | ||
216 | m->VDisplay >>= 1; | ||
217 | m->VSyncStart >>= 1; | ||
218 | m->VSyncEnd >>= 1; | ||
219 | } | ||
220 | |||
221 | /* GCTL is ignored when not using 0xA0000 aperture */ | ||
222 | hw->GCTL[0] = 0x00; | ||
223 | hw->GCTL[1] = 0x00; | ||
224 | hw->GCTL[2] = 0x00; | ||
225 | hw->GCTL[3] = 0x00; | ||
226 | hw->GCTL[4] = 0x00; | ||
227 | hw->GCTL[5] = 0x40; | ||
228 | hw->GCTL[6] = 0x05; | ||
229 | hw->GCTL[7] = 0x0F; | ||
230 | hw->GCTL[8] = 0xFF; | ||
231 | |||
232 | /* Whole ATTR is ignored in PowerGraphics mode */ | ||
233 | for (i = 0; i < 16; i++) | ||
234 | hw->ATTR[i] = i; | ||
235 | hw->ATTR[16] = 0x41; | ||
236 | hw->ATTR[17] = 0xFF; | ||
237 | hw->ATTR[18] = 0x0F; | ||
238 | if (fwidth == 9) | ||
239 | hw->ATTR[19] = 0x08; | ||
240 | else | ||
241 | hw->ATTR[19] = 0x00; | ||
242 | hw->ATTR[20] = 0x00; | ||
243 | |||
244 | hd = m->HDisplay >> 3; | ||
245 | hs = m->HSyncStart >> 3; | ||
246 | he = m->HSyncEnd >> 3; | ||
247 | ht = m->HTotal >> 3; | ||
248 | /* standard timmings are in 8pixels, but for interleaved we cannot */ | ||
249 | /* do it for 4bpp (because of (4bpp >> 1(interleaved))/4 == 0) */ | ||
250 | /* using 16 or more pixels per unit can save us */ | ||
251 | divider = ACCESS_FBINFO(curr.final_bppShift); | ||
252 | while (divider & 3) { | ||
253 | hd >>= 1; | ||
254 | hs >>= 1; | ||
255 | he >>= 1; | ||
256 | ht >>= 1; | ||
257 | divider <<= 1; | ||
258 | } | ||
259 | divider = divider / 4; | ||
260 | /* divider can be from 1 to 8 */ | ||
261 | while (divider > 8) { | ||
262 | hd <<= 1; | ||
263 | hs <<= 1; | ||
264 | he <<= 1; | ||
265 | ht <<= 1; | ||
266 | divider >>= 1; | ||
267 | } | ||
268 | hd = hd - 1; | ||
269 | hs = hs - 1; | ||
270 | he = he - 1; | ||
271 | ht = ht - 1; | ||
272 | vd = m->VDisplay - 1; | ||
273 | vs = m->VSyncStart - 1; | ||
274 | ve = m->VSyncEnd - 1; | ||
275 | vt = m->VTotal - 2; | ||
276 | lc = vd; | ||
277 | /* G200 cannot work with (ht & 7) == 6 */ | ||
278 | if (((ht & 0x07) == 0x06) || ((ht & 0x0F) == 0x04)) | ||
279 | ht++; | ||
280 | hbe = ht; | ||
281 | wd = ACCESS_FBINFO(fbcon).var.xres_virtual * ACCESS_FBINFO(curr.final_bppShift) / 64; | ||
282 | |||
283 | hw->CRTCEXT[0] = 0; | ||
284 | hw->CRTCEXT[5] = 0; | ||
285 | if (m->interlaced) { | ||
286 | hw->CRTCEXT[0] = 0x80; | ||
287 | hw->CRTCEXT[5] = (hs + he - ht) >> 1; | ||
288 | if (!m->dblscan) | ||
289 | wd <<= 1; | ||
290 | vt &= ~1; | ||
291 | } | ||
292 | hw->CRTCEXT[0] |= (wd & 0x300) >> 4; | ||
293 | hw->CRTCEXT[1] = (((ht - 4) & 0x100) >> 8) | | ||
294 | ((hd & 0x100) >> 7) | /* blanking */ | ||
295 | ((hs & 0x100) >> 6) | /* sync start */ | ||
296 | (hbe & 0x040); /* end hor. blanking */ | ||
297 | /* FIXME: Enable vidrst only on G400, and only if TV-out is used */ | ||
298 | if (ACCESS_FBINFO(outputs[1]).src == MATROXFB_SRC_CRTC1) | ||
299 | hw->CRTCEXT[1] |= 0x88; /* enable horizontal and vertical vidrst */ | ||
300 | hw->CRTCEXT[2] = ((vt & 0xC00) >> 10) | | ||
301 | ((vd & 0x400) >> 8) | /* disp end */ | ||
302 | ((vd & 0xC00) >> 7) | /* vblanking start */ | ||
303 | ((vs & 0xC00) >> 5) | | ||
304 | ((lc & 0x400) >> 3); | ||
305 | hw->CRTCEXT[3] = (divider - 1) | 0x80; | ||
306 | hw->CRTCEXT[4] = 0; | ||
307 | |||
308 | hw->CRTC[0] = ht-4; | ||
309 | hw->CRTC[1] = hd; | ||
310 | hw->CRTC[2] = hd; | ||
311 | hw->CRTC[3] = (hbe & 0x1F) | 0x80; | ||
312 | hw->CRTC[4] = hs; | ||
313 | hw->CRTC[5] = ((hbe & 0x20) << 2) | (he & 0x1F); | ||
314 | hw->CRTC[6] = vt & 0xFF; | ||
315 | hw->CRTC[7] = ((vt & 0x100) >> 8) | | ||
316 | ((vd & 0x100) >> 7) | | ||
317 | ((vs & 0x100) >> 6) | | ||
318 | ((vd & 0x100) >> 5) | | ||
319 | ((lc & 0x100) >> 4) | | ||
320 | ((vt & 0x200) >> 4) | | ||
321 | ((vd & 0x200) >> 3) | | ||
322 | ((vs & 0x200) >> 2); | ||
323 | hw->CRTC[8] = 0x00; | ||
324 | hw->CRTC[9] = ((vd & 0x200) >> 4) | | ||
325 | ((lc & 0x200) >> 3); | ||
326 | if (m->dblscan && !m->interlaced) | ||
327 | hw->CRTC[9] |= 0x80; | ||
328 | for (i = 10; i < 16; i++) | ||
329 | hw->CRTC[i] = 0x00; | ||
330 | hw->CRTC[16] = vs /* & 0xFF */; | ||
331 | hw->CRTC[17] = (ve & 0x0F) | 0x20; | ||
332 | hw->CRTC[18] = vd /* & 0xFF */; | ||
333 | hw->CRTC[19] = wd /* & 0xFF */; | ||
334 | hw->CRTC[20] = 0x00; | ||
335 | hw->CRTC[21] = vd /* & 0xFF */; | ||
336 | hw->CRTC[22] = (vt + 1) /* & 0xFF */; | ||
337 | hw->CRTC[23] = 0xC3; | ||
338 | hw->CRTC[24] = lc; | ||
339 | return 0; | ||
340 | }; | ||
341 | |||
342 | void matroxfb_vgaHWrestore(WPMINFO2) { | ||
343 | int i; | ||
344 | struct matrox_hw_state * const hw = &ACCESS_FBINFO(hw); | ||
345 | CRITFLAGS | ||
346 | |||
347 | DBG(__FUNCTION__) | ||
348 | |||
349 | dprintk(KERN_INFO "MiscOutReg: %02X\n", hw->MiscOutReg); | ||
350 | dprintk(KERN_INFO "SEQ regs: "); | ||
351 | for (i = 0; i < 5; i++) | ||
352 | dprintk("%02X:", hw->SEQ[i]); | ||
353 | dprintk("\n"); | ||
354 | dprintk(KERN_INFO "GDC regs: "); | ||
355 | for (i = 0; i < 9; i++) | ||
356 | dprintk("%02X:", hw->GCTL[i]); | ||
357 | dprintk("\n"); | ||
358 | dprintk(KERN_INFO "CRTC regs: "); | ||
359 | for (i = 0; i < 25; i++) | ||
360 | dprintk("%02X:", hw->CRTC[i]); | ||
361 | dprintk("\n"); | ||
362 | dprintk(KERN_INFO "ATTR regs: "); | ||
363 | for (i = 0; i < 21; i++) | ||
364 | dprintk("%02X:", hw->ATTR[i]); | ||
365 | dprintk("\n"); | ||
366 | |||
367 | CRITBEGIN | ||
368 | |||
369 | mga_inb(M_ATTR_RESET); | ||
370 | mga_outb(M_ATTR_INDEX, 0); | ||
371 | mga_outb(M_MISC_REG, hw->MiscOutReg); | ||
372 | for (i = 1; i < 5; i++) | ||
373 | mga_setr(M_SEQ_INDEX, i, hw->SEQ[i]); | ||
374 | mga_setr(M_CRTC_INDEX, 17, hw->CRTC[17] & 0x7F); | ||
375 | for (i = 0; i < 25; i++) | ||
376 | mga_setr(M_CRTC_INDEX, i, hw->CRTC[i]); | ||
377 | for (i = 0; i < 9; i++) | ||
378 | mga_setr(M_GRAPHICS_INDEX, i, hw->GCTL[i]); | ||
379 | for (i = 0; i < 21; i++) { | ||
380 | mga_inb(M_ATTR_RESET); | ||
381 | mga_outb(M_ATTR_INDEX, i); | ||
382 | mga_outb(M_ATTR_INDEX, hw->ATTR[i]); | ||
383 | } | ||
384 | mga_outb(M_PALETTE_MASK, 0xFF); | ||
385 | mga_outb(M_DAC_REG, 0x00); | ||
386 | for (i = 0; i < 768; i++) | ||
387 | mga_outb(M_DAC_VAL, hw->DACpal[i]); | ||
388 | mga_inb(M_ATTR_RESET); | ||
389 | mga_outb(M_ATTR_INDEX, 0x20); | ||
390 | |||
391 | CRITEND | ||
392 | } | ||
393 | |||
394 | static void get_pins(unsigned char __iomem* pins, struct matrox_bios* bd) { | ||
395 | unsigned int b0 = readb(pins); | ||
396 | |||
397 | if (b0 == 0x2E && readb(pins+1) == 0x41) { | ||
398 | unsigned int pins_len = readb(pins+2); | ||
399 | unsigned int i; | ||
400 | unsigned char cksum; | ||
401 | unsigned char* dst = bd->pins; | ||
402 | |||
403 | if (pins_len < 3 || pins_len > 128) { | ||
404 | return; | ||
405 | } | ||
406 | *dst++ = 0x2E; | ||
407 | *dst++ = 0x41; | ||
408 | *dst++ = pins_len; | ||
409 | cksum = 0x2E + 0x41 + pins_len; | ||
410 | for (i = 3; i < pins_len; i++) { | ||
411 | cksum += *dst++ = readb(pins+i); | ||
412 | } | ||
413 | if (cksum) { | ||
414 | return; | ||
415 | } | ||
416 | bd->pins_len = pins_len; | ||
417 | } else if (b0 == 0x40 && readb(pins+1) == 0x00) { | ||
418 | unsigned int i; | ||
419 | unsigned char* dst = bd->pins; | ||
420 | |||
421 | *dst++ = 0x40; | ||
422 | *dst++ = 0; | ||
423 | for (i = 2; i < 0x40; i++) { | ||
424 | *dst++ = readb(pins+i); | ||
425 | } | ||
426 | bd->pins_len = 0x40; | ||
427 | } | ||
428 | } | ||
429 | |||
430 | static void get_bios_version(unsigned char __iomem * vbios, struct matrox_bios* bd) { | ||
431 | unsigned int pcir_offset; | ||
432 | |||
433 | pcir_offset = readb(vbios + 24) | (readb(vbios + 25) << 8); | ||
434 | if (pcir_offset >= 26 && pcir_offset < 0xFFE0 && | ||
435 | readb(vbios + pcir_offset ) == 'P' && | ||
436 | readb(vbios + pcir_offset + 1) == 'C' && | ||
437 | readb(vbios + pcir_offset + 2) == 'I' && | ||
438 | readb(vbios + pcir_offset + 3) == 'R') { | ||
439 | unsigned char h; | ||
440 | |||
441 | h = readb(vbios + pcir_offset + 0x12); | ||
442 | bd->version.vMaj = (h >> 4) & 0xF; | ||
443 | bd->version.vMin = h & 0xF; | ||
444 | bd->version.vRev = readb(vbios + pcir_offset + 0x13); | ||
445 | } else { | ||
446 | unsigned char h; | ||
447 | |||
448 | h = readb(vbios + 5); | ||
449 | bd->version.vMaj = (h >> 4) & 0xF; | ||
450 | bd->version.vMin = h & 0xF; | ||
451 | bd->version.vRev = 0; | ||
452 | } | ||
453 | } | ||
454 | |||
455 | static void get_bios_output(unsigned char __iomem* vbios, struct matrox_bios* bd) { | ||
456 | unsigned char b; | ||
457 | |||
458 | b = readb(vbios + 0x7FF1); | ||
459 | if (b == 0xFF) { | ||
460 | b = 0; | ||
461 | } | ||
462 | bd->output.state = b; | ||
463 | } | ||
464 | |||
465 | static void get_bios_tvout(unsigned char __iomem* vbios, struct matrox_bios* bd) { | ||
466 | unsigned int i; | ||
467 | |||
468 | /* Check for 'IBM .*(V....TVO' string - it means TVO BIOS */ | ||
469 | bd->output.tvout = 0; | ||
470 | if (readb(vbios + 0x1D) != 'I' || | ||
471 | readb(vbios + 0x1E) != 'B' || | ||
472 | readb(vbios + 0x1F) != 'M' || | ||
473 | readb(vbios + 0x20) != ' ') { | ||
474 | return; | ||
475 | } | ||
476 | for (i = 0x2D; i < 0x2D + 128; i++) { | ||
477 | unsigned char b = readb(vbios + i); | ||
478 | |||
479 | if (b == '(' && readb(vbios + i + 1) == 'V') { | ||
480 | if (readb(vbios + i + 6) == 'T' && | ||
481 | readb(vbios + i + 7) == 'V' && | ||
482 | readb(vbios + i + 8) == 'O') { | ||
483 | bd->output.tvout = 1; | ||
484 | } | ||
485 | return; | ||
486 | } | ||
487 | if (b == 0) | ||
488 | break; | ||
489 | } | ||
490 | } | ||
491 | |||
492 | static void parse_bios(unsigned char __iomem* vbios, struct matrox_bios* bd) { | ||
493 | unsigned int pins_offset; | ||
494 | |||
495 | if (readb(vbios) != 0x55 || readb(vbios + 1) != 0xAA) { | ||
496 | return; | ||
497 | } | ||
498 | bd->bios_valid = 1; | ||
499 | get_bios_version(vbios, bd); | ||
500 | get_bios_output(vbios, bd); | ||
501 | get_bios_tvout(vbios, bd); | ||
502 | pins_offset = readb(vbios + 0x7FFC) | (readb(vbios + 0x7FFD) << 8); | ||
503 | if (pins_offset <= 0xFF80) { | ||
504 | get_pins(vbios + pins_offset, bd); | ||
505 | } | ||
506 | } | ||
507 | |||
508 | #define get_u16(x) (le16_to_cpu(get_unaligned((__u16*)(x)))) | ||
509 | #define get_u32(x) (le32_to_cpu(get_unaligned((__u32*)(x)))) | ||
510 | static int parse_pins1(WPMINFO const struct matrox_bios* bd) { | ||
511 | unsigned int maxdac; | ||
512 | |||
513 | switch (bd->pins[22]) { | ||
514 | case 0: maxdac = 175000; break; | ||
515 | case 1: maxdac = 220000; break; | ||
516 | default: maxdac = 240000; break; | ||
517 | } | ||
518 | if (get_u16(bd->pins + 24)) { | ||
519 | maxdac = get_u16(bd->pins + 24) * 10; | ||
520 | } | ||
521 | MINFO->limits.pixel.vcomax = maxdac; | ||
522 | MINFO->values.pll.system = get_u16(bd->pins + 28) ? get_u16(bd->pins + 28) * 10 : 50000; | ||
523 | /* ignore 4MB, 8MB, module clocks */ | ||
524 | MINFO->features.pll.ref_freq = 14318; | ||
525 | MINFO->values.reg.mctlwtst = 0x00030101; | ||
526 | return 0; | ||
527 | } | ||
528 | |||
529 | static void default_pins1(WPMINFO2) { | ||
530 | /* Millennium */ | ||
531 | MINFO->limits.pixel.vcomax = 220000; | ||
532 | MINFO->values.pll.system = 50000; | ||
533 | MINFO->features.pll.ref_freq = 14318; | ||
534 | MINFO->values.reg.mctlwtst = 0x00030101; | ||
535 | } | ||
536 | |||
537 | static int parse_pins2(WPMINFO const struct matrox_bios* bd) { | ||
538 | MINFO->limits.pixel.vcomax = | ||
539 | MINFO->limits.system.vcomax = (bd->pins[41] == 0xFF) ? 230000 : ((bd->pins[41] + 100) * 1000); | ||
540 | MINFO->values.reg.mctlwtst = ((bd->pins[51] & 0x01) ? 0x00000001 : 0) | | ||
541 | ((bd->pins[51] & 0x02) ? 0x00000100 : 0) | | ||
542 | ((bd->pins[51] & 0x04) ? 0x00010000 : 0) | | ||
543 | ((bd->pins[51] & 0x08) ? 0x00020000 : 0); | ||
544 | MINFO->values.pll.system = (bd->pins[43] == 0xFF) ? 50000 : ((bd->pins[43] + 100) * 1000); | ||
545 | MINFO->features.pll.ref_freq = 14318; | ||
546 | return 0; | ||
547 | } | ||
548 | |||
549 | static void default_pins2(WPMINFO2) { | ||
550 | /* Millennium II, Mystique */ | ||
551 | MINFO->limits.pixel.vcomax = | ||
552 | MINFO->limits.system.vcomax = 230000; | ||
553 | MINFO->values.reg.mctlwtst = 0x00030101; | ||
554 | MINFO->values.pll.system = 50000; | ||
555 | MINFO->features.pll.ref_freq = 14318; | ||
556 | } | ||
557 | |||
558 | static int parse_pins3(WPMINFO const struct matrox_bios* bd) { | ||
559 | MINFO->limits.pixel.vcomax = | ||
560 | MINFO->limits.system.vcomax = (bd->pins[36] == 0xFF) ? 230000 : ((bd->pins[36] + 100) * 1000); | ||
561 | MINFO->values.reg.mctlwtst = get_u32(bd->pins + 48) == 0xFFFFFFFF ? 0x01250A21 : get_u32(bd->pins + 48); | ||
562 | /* memory config */ | ||
563 | MINFO->values.reg.memrdbk = ((bd->pins[57] << 21) & 0x1E000000) | | ||
564 | ((bd->pins[57] << 22) & 0x00C00000) | | ||
565 | ((bd->pins[56] << 1) & 0x000001E0) | | ||
566 | ( bd->pins[56] & 0x0000000F); | ||
567 | MINFO->values.reg.opt = (bd->pins[54] & 7) << 10; | ||
568 | MINFO->values.reg.opt2 = bd->pins[58] << 12; | ||
569 | MINFO->features.pll.ref_freq = (bd->pins[52] & 0x20) ? 14318 : 27000; | ||
570 | return 0; | ||
571 | } | ||
572 | |||
573 | static void default_pins3(WPMINFO2) { | ||
574 | /* G100, G200 */ | ||
575 | MINFO->limits.pixel.vcomax = | ||
576 | MINFO->limits.system.vcomax = 230000; | ||
577 | MINFO->values.reg.mctlwtst = 0x01250A21; | ||
578 | MINFO->values.reg.memrdbk = 0x00000000; | ||
579 | MINFO->values.reg.opt = 0x00000C00; | ||
580 | MINFO->values.reg.opt2 = 0x00000000; | ||
581 | MINFO->features.pll.ref_freq = 27000; | ||
582 | } | ||
583 | |||
584 | static int parse_pins4(WPMINFO const struct matrox_bios* bd) { | ||
585 | MINFO->limits.pixel.vcomax = (bd->pins[ 39] == 0xFF) ? 230000 : bd->pins[ 39] * 4000; | ||
586 | MINFO->limits.system.vcomax = (bd->pins[ 38] == 0xFF) ? MINFO->limits.pixel.vcomax : bd->pins[ 38] * 4000; | ||
587 | MINFO->values.reg.mctlwtst = get_u32(bd->pins + 71); | ||
588 | MINFO->values.reg.memrdbk = ((bd->pins[87] << 21) & 0x1E000000) | | ||
589 | ((bd->pins[87] << 22) & 0x00C00000) | | ||
590 | ((bd->pins[86] << 1) & 0x000001E0) | | ||
591 | ( bd->pins[86] & 0x0000000F); | ||
592 | MINFO->values.reg.opt = ((bd->pins[53] << 15) & 0x00400000) | | ||
593 | ((bd->pins[53] << 22) & 0x10000000) | | ||
594 | ((bd->pins[53] << 7) & 0x00001C00); | ||
595 | MINFO->values.reg.opt3 = get_u32(bd->pins + 67); | ||
596 | MINFO->values.pll.system = (bd->pins[ 65] == 0xFF) ? 200000 : bd->pins[ 65] * 4000; | ||
597 | MINFO->features.pll.ref_freq = (bd->pins[ 92] & 0x01) ? 14318 : 27000; | ||
598 | return 0; | ||
599 | } | ||
600 | |||
601 | static void default_pins4(WPMINFO2) { | ||
602 | /* G400 */ | ||
603 | MINFO->limits.pixel.vcomax = | ||
604 | MINFO->limits.system.vcomax = 252000; | ||
605 | MINFO->values.reg.mctlwtst = 0x04A450A1; | ||
606 | MINFO->values.reg.memrdbk = 0x000000E7; | ||
607 | MINFO->values.reg.opt = 0x10000400; | ||
608 | MINFO->values.reg.opt3 = 0x0190A419; | ||
609 | MINFO->values.pll.system = 200000; | ||
610 | MINFO->features.pll.ref_freq = 27000; | ||
611 | } | ||
612 | |||
613 | static int parse_pins5(WPMINFO const struct matrox_bios* bd) { | ||
614 | unsigned int mult; | ||
615 | |||
616 | mult = bd->pins[4]?8000:6000; | ||
617 | |||
618 | MINFO->limits.pixel.vcomax = (bd->pins[ 38] == 0xFF) ? 600000 : bd->pins[ 38] * mult; | ||
619 | MINFO->limits.system.vcomax = (bd->pins[ 36] == 0xFF) ? MINFO->limits.pixel.vcomax : bd->pins[ 36] * mult; | ||
620 | MINFO->limits.video.vcomax = (bd->pins[ 37] == 0xFF) ? MINFO->limits.system.vcomax : bd->pins[ 37] * mult; | ||
621 | MINFO->limits.pixel.vcomin = (bd->pins[123] == 0xFF) ? 256000 : bd->pins[123] * mult; | ||
622 | MINFO->limits.system.vcomin = (bd->pins[121] == 0xFF) ? MINFO->limits.pixel.vcomin : bd->pins[121] * mult; | ||
623 | MINFO->limits.video.vcomin = (bd->pins[122] == 0xFF) ? MINFO->limits.system.vcomin : bd->pins[122] * mult; | ||
624 | MINFO->values.pll.system = | ||
625 | MINFO->values.pll.video = (bd->pins[ 92] == 0xFF) ? 284000 : bd->pins[ 92] * 4000; | ||
626 | MINFO->values.reg.opt = get_u32(bd->pins+ 48); | ||
627 | MINFO->values.reg.opt2 = get_u32(bd->pins+ 52); | ||
628 | MINFO->values.reg.opt3 = get_u32(bd->pins+ 94); | ||
629 | MINFO->values.reg.mctlwtst = get_u32(bd->pins+ 98); | ||
630 | MINFO->values.reg.memmisc = get_u32(bd->pins+102); | ||
631 | MINFO->values.reg.memrdbk = get_u32(bd->pins+106); | ||
632 | MINFO->features.pll.ref_freq = (bd->pins[110] & 0x01) ? 14318 : 27000; | ||
633 | MINFO->values.memory.ddr = (bd->pins[114] & 0x60) == 0x20; | ||
634 | MINFO->values.memory.dll = (bd->pins[115] & 0x02) != 0; | ||
635 | MINFO->values.memory.emrswen = (bd->pins[115] & 0x01) != 0; | ||
636 | MINFO->values.reg.maccess = MINFO->values.memory.emrswen ? 0x00004000 : 0x00000000; | ||
637 | if (bd->pins[115] & 4) { | ||
638 | MINFO->values.reg.mctlwtst_core = MINFO->values.reg.mctlwtst; | ||
639 | } else { | ||
640 | u_int32_t wtst_xlat[] = { 0, 1, 5, 6, 7, 5, 2, 3 }; | ||
641 | MINFO->values.reg.mctlwtst_core = (MINFO->values.reg.mctlwtst & ~7) | | ||
642 | wtst_xlat[MINFO->values.reg.mctlwtst & 7]; | ||
643 | } | ||
644 | return 0; | ||
645 | } | ||
646 | |||
647 | static void default_pins5(WPMINFO2) { | ||
648 | /* Mine 16MB G450 with SDRAM DDR */ | ||
649 | MINFO->limits.pixel.vcomax = | ||
650 | MINFO->limits.system.vcomax = | ||
651 | MINFO->limits.video.vcomax = 600000; | ||
652 | MINFO->limits.pixel.vcomin = | ||
653 | MINFO->limits.system.vcomin = | ||
654 | MINFO->limits.video.vcomin = 256000; | ||
655 | MINFO->values.pll.system = | ||
656 | MINFO->values.pll.video = 284000; | ||
657 | MINFO->values.reg.opt = 0x404A1160; | ||
658 | MINFO->values.reg.opt2 = 0x0000AC00; | ||
659 | MINFO->values.reg.opt3 = 0x0090A409; | ||
660 | MINFO->values.reg.mctlwtst_core = | ||
661 | MINFO->values.reg.mctlwtst = 0x0C81462B; | ||
662 | MINFO->values.reg.memmisc = 0x80000004; | ||
663 | MINFO->values.reg.memrdbk = 0x01001103; | ||
664 | MINFO->features.pll.ref_freq = 27000; | ||
665 | MINFO->values.memory.ddr = 1; | ||
666 | MINFO->values.memory.dll = 1; | ||
667 | MINFO->values.memory.emrswen = 1; | ||
668 | MINFO->values.reg.maccess = 0x00004000; | ||
669 | } | ||
670 | |||
671 | static int matroxfb_set_limits(WPMINFO const struct matrox_bios* bd) { | ||
672 | unsigned int pins_version; | ||
673 | static const unsigned int pinslen[] = { 64, 64, 64, 128, 128 }; | ||
674 | |||
675 | switch (ACCESS_FBINFO(chip)) { | ||
676 | case MGA_2064: default_pins1(PMINFO2); break; | ||
677 | case MGA_2164: | ||
678 | case MGA_1064: | ||
679 | case MGA_1164: default_pins2(PMINFO2); break; | ||
680 | case MGA_G100: | ||
681 | case MGA_G200: default_pins3(PMINFO2); break; | ||
682 | case MGA_G400: default_pins4(PMINFO2); break; | ||
683 | case MGA_G450: | ||
684 | case MGA_G550: default_pins5(PMINFO2); break; | ||
685 | } | ||
686 | if (!bd->bios_valid) { | ||
687 | printk(KERN_INFO "matroxfb: Your Matrox device does not have BIOS\n"); | ||
688 | return -1; | ||
689 | } | ||
690 | if (bd->pins_len < 64) { | ||
691 | printk(KERN_INFO "matroxfb: BIOS on your Matrox device does not contain powerup info\n"); | ||
692 | return -1; | ||
693 | } | ||
694 | if (bd->pins[0] == 0x2E && bd->pins[1] == 0x41) { | ||
695 | pins_version = bd->pins[5]; | ||
696 | if (pins_version < 2 || pins_version > 5) { | ||
697 | printk(KERN_INFO "matroxfb: Unknown version (%u) of powerup info\n", pins_version); | ||
698 | return -1; | ||
699 | } | ||
700 | } else { | ||
701 | pins_version = 1; | ||
702 | } | ||
703 | if (bd->pins_len != pinslen[pins_version - 1]) { | ||
704 | printk(KERN_INFO "matroxfb: Invalid powerup info\n"); | ||
705 | return -1; | ||
706 | } | ||
707 | switch (pins_version) { | ||
708 | case 1: | ||
709 | return parse_pins1(PMINFO bd); | ||
710 | case 2: | ||
711 | return parse_pins2(PMINFO bd); | ||
712 | case 3: | ||
713 | return parse_pins3(PMINFO bd); | ||
714 | case 4: | ||
715 | return parse_pins4(PMINFO bd); | ||
716 | case 5: | ||
717 | return parse_pins5(PMINFO bd); | ||
718 | default: | ||
719 | printk(KERN_DEBUG "matroxfb: Powerup info version %u is not yet supported\n", pins_version); | ||
720 | return -1; | ||
721 | } | ||
722 | } | ||
723 | |||
724 | void matroxfb_read_pins(WPMINFO2) { | ||
725 | u32 opt; | ||
726 | u32 biosbase; | ||
727 | u32 fbbase; | ||
728 | struct pci_dev* pdev = ACCESS_FBINFO(pcidev); | ||
729 | |||
730 | memset(&ACCESS_FBINFO(bios), 0, sizeof(ACCESS_FBINFO(bios))); | ||
731 | pci_read_config_dword(pdev, PCI_OPTION_REG, &opt); | ||
732 | pci_write_config_dword(pdev, PCI_OPTION_REG, opt | PCI_OPTION_ENABLE_ROM); | ||
733 | pci_read_config_dword(pdev, PCI_ROM_ADDRESS, &biosbase); | ||
734 | pci_read_config_dword(pdev, ACCESS_FBINFO(devflags.fbResource), &fbbase); | ||
735 | pci_write_config_dword(pdev, PCI_ROM_ADDRESS, (fbbase & PCI_ROM_ADDRESS_MASK) | PCI_ROM_ADDRESS_ENABLE); | ||
736 | parse_bios(vaddr_va(ACCESS_FBINFO(video).vbase), &ACCESS_FBINFO(bios)); | ||
737 | pci_write_config_dword(pdev, PCI_ROM_ADDRESS, biosbase); | ||
738 | pci_write_config_dword(pdev, PCI_OPTION_REG, opt); | ||
739 | #ifdef CONFIG_X86 | ||
740 | if (!ACCESS_FBINFO(bios).bios_valid) { | ||
741 | unsigned char __iomem* b; | ||
742 | |||
743 | b = ioremap(0x000C0000, 65536); | ||
744 | if (!b) { | ||
745 | printk(KERN_INFO "matroxfb: Unable to map legacy BIOS\n"); | ||
746 | } else { | ||
747 | unsigned int ven = readb(b+0x64+0) | (readb(b+0x64+1) << 8); | ||
748 | unsigned int dev = readb(b+0x64+2) | (readb(b+0x64+3) << 8); | ||
749 | |||
750 | if (ven != pdev->vendor || dev != pdev->device) { | ||
751 | printk(KERN_INFO "matroxfb: Legacy BIOS is for %04X:%04X, while this device is %04X:%04X\n", | ||
752 | ven, dev, pdev->vendor, pdev->device); | ||
753 | } else { | ||
754 | parse_bios(b, &ACCESS_FBINFO(bios)); | ||
755 | } | ||
756 | iounmap(b); | ||
757 | } | ||
758 | } | ||
759 | #endif | ||
760 | matroxfb_set_limits(PMINFO &ACCESS_FBINFO(bios)); | ||
761 | } | ||
762 | |||
763 | EXPORT_SYMBOL(matroxfb_DAC_in); | ||
764 | EXPORT_SYMBOL(matroxfb_DAC_out); | ||
765 | EXPORT_SYMBOL(matroxfb_var2my); | ||
766 | EXPORT_SYMBOL(matroxfb_PLL_calcclock); | ||
767 | #ifndef CONFIG_FB_MATROX_MULTIHEAD | ||
768 | struct matrox_fb_info matroxfb_global_mxinfo; | ||
769 | EXPORT_SYMBOL(matroxfb_global_mxinfo); | ||
770 | #endif | ||
771 | EXPORT_SYMBOL(matroxfb_vgaHWinit); /* DAC1064, Ti3026 */ | ||
772 | EXPORT_SYMBOL(matroxfb_vgaHWrestore); /* DAC1064, Ti3026 */ | ||
773 | EXPORT_SYMBOL(matroxfb_read_pins); | ||
774 | |||
775 | MODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec <vandrove@vc.cvut.cz>"); | ||
776 | MODULE_DESCRIPTION("Miscellaneous support for Matrox video cards"); | ||
777 | MODULE_LICENSE("GPL"); | ||