]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | * "Samuel Hocevar" <sam@via.ecp.fr> | |
69 | * Fixes | |
70 | * | |
71 | * "Anton Altaparmakov" <AntonA@bigfoot.com> | |
72 | * G400 MAX/non-MAX distinction | |
73 | * | |
74 | * "Ken Aaker" <kdaaker@rchland.vnet.ibm.com> | |
75 | * memtype extension (needed for GXT130P RS/6000 adapter) | |
76 | * | |
77 | * "Uns Lider" <unslider@miranda.org> | |
78 | * G100 PLNWT fixes | |
79 | * | |
80 | * "Denis Zaitsev" <zzz@cd-club.ru> | |
81 | * Fixes | |
82 | * | |
83 | * "Mike Pieper" <mike@pieper-family.de> | |
84 | * TVOut enhandcements, V4L2 control interface. | |
85 | * | |
86 | * "Diego Biurrun" <diego@biurrun.de> | |
87 | * DFP testing | |
88 | * | |
89 | * (following author is not in any relation with this code, but his code | |
90 | * is included in this driver) | |
91 | * | |
92 | * Based on framebuffer driver for VBE 2.0 compliant graphic boards | |
93 | * (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de> | |
94 | * | |
95 | * (following author is not in any relation with this code, but his ideas | |
beb7dd86 | 96 | * were used when writing this driver) |
1da177e4 LT |
97 | * |
98 | * FreeVBE/AF (Matrox), "Shawn Hargreaves" <shawn@talula.demon.co.uk> | |
99 | * | |
100 | */ | |
101 | ||
1da177e4 LT |
102 | #include <linux/version.h> |
103 | ||
104 | #include "matroxfb_base.h" | |
105 | #include "matroxfb_misc.h" | |
106 | #include "matroxfb_accel.h" | |
107 | #include "matroxfb_DAC1064.h" | |
108 | #include "matroxfb_Ti3026.h" | |
109 | #include "matroxfb_maven.h" | |
110 | #include "matroxfb_crtc2.h" | |
111 | #include "matroxfb_g450.h" | |
112 | #include <linux/matroxfb.h> | |
113 | #include <linux/interrupt.h> | |
5a0e3ad6 | 114 | #include <linux/slab.h> |
84902b7a | 115 | #include <linux/uaccess.h> |
1da177e4 LT |
116 | |
117 | #ifdef CONFIG_PPC_PMAC | |
e8222502 | 118 | #include <asm/machdep.h> |
1da177e4 LT |
119 | unsigned char nvram_read_byte(int); |
120 | static int default_vmode = VMODE_NVRAM; | |
121 | static int default_cmode = CMODE_NVRAM; | |
122 | #endif | |
123 | ||
124 | static void matroxfb_unregister_device(struct matrox_fb_info* minfo); | |
125 | ||
126 | /* --------------------------------------------------------------------- */ | |
127 | ||
128 | /* | |
129 | * card parameters | |
130 | */ | |
131 | ||
132 | /* --------------------------------------------------------------------- */ | |
133 | ||
134 | static struct fb_var_screeninfo vesafb_defined = { | |
135 | 640,480,640,480,/* W,H, W, H (virtual) load xres,xres_virtual*/ | |
136 | 0,0, /* virtual -> visible no offset */ | |
137 | 8, /* depth -> load bits_per_pixel */ | |
138 | 0, /* greyscale ? */ | |
139 | {0,0,0}, /* R */ | |
140 | {0,0,0}, /* G */ | |
141 | {0,0,0}, /* B */ | |
142 | {0,0,0}, /* transparency */ | |
143 | 0, /* standard pixel format */ | |
144 | FB_ACTIVATE_NOW, | |
145 | -1,-1, | |
146 | FB_ACCELF_TEXT, /* accel flags */ | |
147 | 39721L,48L,16L,33L,10L, | |
148 | 96L,2L,~0, /* No sync info */ | |
149 | FB_VMODE_NONINTERLACED, | |
150 | 0, {0,0,0,0,0} | |
151 | }; | |
152 | ||
153 | ||
154 | ||
155 | /* --------------------------------------------------------------------- */ | |
316b4d64 JD |
156 | static void update_crtc2(struct matrox_fb_info *minfo, unsigned int pos) |
157 | { | |
fc2d10dd | 158 | struct matroxfb_dh_fb_info *info = minfo->crtc2.info; |
1da177e4 LT |
159 | |
160 | /* Make sure that displays are compatible */ | |
fc2d10dd JD |
161 | if (info && (info->fbcon.var.bits_per_pixel == minfo->fbcon.var.bits_per_pixel) |
162 | && (info->fbcon.var.xres_virtual == minfo->fbcon.var.xres_virtual) | |
163 | && (info->fbcon.var.green.length == minfo->fbcon.var.green.length) | |
a50d913f | 164 | ) { |
fc2d10dd | 165 | switch (minfo->fbcon.var.bits_per_pixel) { |
1da177e4 LT |
166 | case 16: |
167 | case 32: | |
168 | pos = pos * 8; | |
169 | if (info->interlaced) { | |
170 | mga_outl(0x3C2C, pos); | |
fc2d10dd | 171 | mga_outl(0x3C28, pos + minfo->fbcon.var.xres_virtual * minfo->fbcon.var.bits_per_pixel / 8); |
1da177e4 LT |
172 | } else { |
173 | mga_outl(0x3C28, pos); | |
174 | } | |
175 | break; | |
176 | } | |
177 | } | |
178 | } | |
179 | ||
316b4d64 JD |
180 | static void matroxfb_crtc1_panpos(struct matrox_fb_info *minfo) |
181 | { | |
fc2d10dd | 182 | if (minfo->crtc1.panpos >= 0) { |
1da177e4 LT |
183 | unsigned long flags; |
184 | int panpos; | |
185 | ||
186 | matroxfb_DAC_lock_irqsave(flags); | |
fc2d10dd | 187 | panpos = minfo->crtc1.panpos; |
1da177e4 LT |
188 | if (panpos >= 0) { |
189 | unsigned int extvga_reg; | |
190 | ||
fc2d10dd | 191 | minfo->crtc1.panpos = -1; /* No update pending anymore */ |
1da177e4 LT |
192 | extvga_reg = mga_inb(M_EXTVGA_INDEX); |
193 | mga_setr(M_EXTVGA_INDEX, 0x00, panpos); | |
194 | if (extvga_reg != 0x00) { | |
195 | mga_outb(M_EXTVGA_INDEX, extvga_reg); | |
196 | } | |
197 | } | |
198 | matroxfb_DAC_unlock_irqrestore(flags); | |
199 | } | |
200 | } | |
201 | ||
7d12e780 | 202 | static irqreturn_t matrox_irq(int irq, void *dev_id) |
1da177e4 LT |
203 | { |
204 | u_int32_t status; | |
205 | int handled = 0; | |
ee5a2749 | 206 | struct matrox_fb_info *minfo = dev_id; |
1da177e4 LT |
207 | |
208 | status = mga_inl(M_STATUS); | |
209 | ||
210 | if (status & 0x20) { | |
211 | mga_outl(M_ICLEAR, 0x20); | |
fc2d10dd | 212 | minfo->crtc1.vsync.cnt++; |
316b4d64 | 213 | matroxfb_crtc1_panpos(minfo); |
fc2d10dd | 214 | wake_up_interruptible(&minfo->crtc1.vsync.wait); |
1da177e4 LT |
215 | handled = 1; |
216 | } | |
217 | if (status & 0x200) { | |
218 | mga_outl(M_ICLEAR, 0x200); | |
fc2d10dd JD |
219 | minfo->crtc2.vsync.cnt++; |
220 | wake_up_interruptible(&minfo->crtc2.vsync.wait); | |
1da177e4 LT |
221 | handled = 1; |
222 | } | |
223 | return IRQ_RETVAL(handled); | |
224 | } | |
225 | ||
316b4d64 JD |
226 | int matroxfb_enable_irq(struct matrox_fb_info *minfo, int reenable) |
227 | { | |
1da177e4 | 228 | u_int32_t bm; |
a50d913f | 229 | |
fc2d10dd | 230 | if (minfo->devflags.accelerator == FB_ACCEL_MATROX_MGAG400) |
1da177e4 LT |
231 | bm = 0x220; |
232 | else | |
233 | bm = 0x020; | |
234 | ||
fc2d10dd JD |
235 | if (!test_and_set_bit(0, &minfo->irq_flags)) { |
236 | if (request_irq(minfo->pcidev->irq, matrox_irq, | |
237 | IRQF_SHARED, "matroxfb", minfo)) { | |
238 | clear_bit(0, &minfo->irq_flags); | |
1da177e4 LT |
239 | return -EINVAL; |
240 | } | |
241 | /* Clear any pending field interrupts */ | |
242 | mga_outl(M_ICLEAR, bm); | |
243 | mga_outl(M_IEN, mga_inl(M_IEN) | bm); | |
244 | } else if (reenable) { | |
245 | u_int32_t ien; | |
a50d913f | 246 | |
1da177e4 LT |
247 | ien = mga_inl(M_IEN); |
248 | if ((ien & bm) != bm) { | |
249 | printk(KERN_DEBUG "matroxfb: someone disabled IRQ [%08X]\n", ien); | |
250 | mga_outl(M_IEN, ien | bm); | |
251 | } | |
252 | } | |
253 | return 0; | |
254 | } | |
255 | ||
316b4d64 JD |
256 | static void matroxfb_disable_irq(struct matrox_fb_info *minfo) |
257 | { | |
fc2d10dd | 258 | if (test_and_clear_bit(0, &minfo->irq_flags)) { |
1da177e4 | 259 | /* Flush pending pan-at-vbl request... */ |
316b4d64 | 260 | matroxfb_crtc1_panpos(minfo); |
fc2d10dd | 261 | if (minfo->devflags.accelerator == FB_ACCEL_MATROX_MGAG400) |
1da177e4 LT |
262 | mga_outl(M_IEN, mga_inl(M_IEN) & ~0x220); |
263 | else | |
264 | mga_outl(M_IEN, mga_inl(M_IEN) & ~0x20); | |
fc2d10dd | 265 | free_irq(minfo->pcidev->irq, minfo); |
1da177e4 LT |
266 | } |
267 | } | |
268 | ||
316b4d64 JD |
269 | int matroxfb_wait_for_sync(struct matrox_fb_info *minfo, u_int32_t crtc) |
270 | { | |
1da177e4 LT |
271 | struct matrox_vsync *vs; |
272 | unsigned int cnt; | |
273 | int ret; | |
274 | ||
275 | switch (crtc) { | |
276 | case 0: | |
fc2d10dd | 277 | vs = &minfo->crtc1.vsync; |
1da177e4 LT |
278 | break; |
279 | case 1: | |
fc2d10dd | 280 | if (minfo->devflags.accelerator != FB_ACCEL_MATROX_MGAG400) { |
1da177e4 LT |
281 | return -ENODEV; |
282 | } | |
fc2d10dd | 283 | vs = &minfo->crtc2.vsync; |
1da177e4 LT |
284 | break; |
285 | default: | |
286 | return -ENODEV; | |
287 | } | |
316b4d64 | 288 | ret = matroxfb_enable_irq(minfo, 0); |
1da177e4 LT |
289 | if (ret) { |
290 | return ret; | |
291 | } | |
1da177e4 LT |
292 | |
293 | cnt = vs->cnt; | |
294 | ret = wait_event_interruptible_timeout(vs->wait, cnt != vs->cnt, HZ/10); | |
295 | if (ret < 0) { | |
296 | return ret; | |
297 | } | |
298 | if (ret == 0) { | |
316b4d64 | 299 | matroxfb_enable_irq(minfo, 1); |
1da177e4 LT |
300 | return -ETIMEDOUT; |
301 | } | |
302 | return 0; | |
303 | } | |
304 | ||
305 | /* --------------------------------------------------------------------- */ | |
306 | ||
316b4d64 JD |
307 | static void matrox_pan_var(struct matrox_fb_info *minfo, |
308 | struct fb_var_screeninfo *var) | |
309 | { | |
1da177e4 LT |
310 | unsigned int pos; |
311 | unsigned short p0, p1, p2; | |
1da177e4 | 312 | unsigned int p3; |
1da177e4 LT |
313 | int vbl; |
314 | unsigned long flags; | |
315 | ||
316 | CRITFLAGS | |
317 | ||
5ae12170 | 318 | DBG(__func__) |
1da177e4 | 319 | |
fc2d10dd | 320 | if (minfo->dead) |
1da177e4 LT |
321 | return; |
322 | ||
fc2d10dd JD |
323 | minfo->fbcon.var.xoffset = var->xoffset; |
324 | minfo->fbcon.var.yoffset = var->yoffset; | |
325 | pos = (minfo->fbcon.var.yoffset * minfo->fbcon.var.xres_virtual + minfo->fbcon.var.xoffset) * minfo->curr.final_bppShift / 32; | |
326 | pos += minfo->curr.ydstorg.chunks; | |
327 | p0 = minfo->hw.CRTC[0x0D] = pos & 0xFF; | |
328 | p1 = minfo->hw.CRTC[0x0C] = (pos & 0xFF00) >> 8; | |
329 | p2 = minfo->hw.CRTCEXT[0] = (minfo->hw.CRTCEXT[0] & 0xB0) | ((pos >> 16) & 0x0F) | ((pos >> 14) & 0x40); | |
fc2d10dd | 330 | p3 = minfo->hw.CRTCEXT[8] = pos >> 21; |
1da177e4 LT |
331 | |
332 | /* FB_ACTIVATE_VBL and we can acquire interrupts? Honor FB_ACTIVATE_VBL then... */ | |
316b4d64 | 333 | vbl = (var->activate & FB_ACTIVATE_VBL) && (matroxfb_enable_irq(minfo, 0) == 0); |
1da177e4 LT |
334 | |
335 | CRITBEGIN | |
336 | ||
337 | matroxfb_DAC_lock_irqsave(flags); | |
338 | mga_setr(M_CRTC_INDEX, 0x0D, p0); | |
339 | mga_setr(M_CRTC_INDEX, 0x0C, p1); | |
fc2d10dd | 340 | if (minfo->devflags.support32MB) |
1da177e4 | 341 | mga_setr(M_EXTVGA_INDEX, 0x08, p3); |
1da177e4 | 342 | if (vbl) { |
fc2d10dd | 343 | minfo->crtc1.panpos = p2; |
1da177e4 LT |
344 | } else { |
345 | /* Abort any pending change */ | |
fc2d10dd | 346 | minfo->crtc1.panpos = -1; |
1da177e4 LT |
347 | mga_setr(M_EXTVGA_INDEX, 0x00, p2); |
348 | } | |
349 | matroxfb_DAC_unlock_irqrestore(flags); | |
a50d913f | 350 | |
316b4d64 | 351 | update_crtc2(minfo, pos); |
1da177e4 LT |
352 | |
353 | CRITEND | |
354 | } | |
355 | ||
316b4d64 JD |
356 | static void matroxfb_remove(struct matrox_fb_info *minfo, int dummy) |
357 | { | |
1da177e4 LT |
358 | /* Currently we are holding big kernel lock on all dead & usecount updates. |
359 | * Destroy everything after all users release it. Especially do not unregister | |
360 | * framebuffer and iounmap memory, neither fbmem nor fbcon-cfb* does not check | |
361 | * for device unplugged when in use. | |
362 | * In future we should point mmio.vbase & video.vbase somewhere where we can | |
363 | * write data without causing too much damage... | |
364 | */ | |
365 | ||
fc2d10dd JD |
366 | minfo->dead = 1; |
367 | if (minfo->usecount) { | |
1da177e4 LT |
368 | /* destroy it later */ |
369 | return; | |
370 | } | |
fc2d10dd JD |
371 | matroxfb_unregister_device(minfo); |
372 | unregister_framebuffer(&minfo->fbcon); | |
316b4d64 | 373 | matroxfb_g450_shutdown(minfo); |
1da177e4 | 374 | #ifdef CONFIG_MTRR |
fc2d10dd JD |
375 | if (minfo->mtrr.vram_valid) |
376 | mtrr_del(minfo->mtrr.vram, minfo->video.base, minfo->video.len); | |
1da177e4 | 377 | #endif |
fc2d10dd JD |
378 | mga_iounmap(minfo->mmio.vbase); |
379 | mga_iounmap(minfo->video.vbase); | |
380 | release_mem_region(minfo->video.base, minfo->video.len_maximum); | |
381 | release_mem_region(minfo->mmio.base, 16384); | |
1da177e4 | 382 | kfree(minfo); |
1da177e4 LT |
383 | } |
384 | ||
385 | /* | |
386 | * Open/Release the frame buffer device | |
387 | */ | |
388 | ||
389 | static int matroxfb_open(struct fb_info *info, int user) | |
390 | { | |
ee5a2749 | 391 | struct matrox_fb_info *minfo = info2minfo(info); |
a50d913f | 392 | |
5ae12170 | 393 | DBG_LOOP(__func__) |
1da177e4 | 394 | |
fc2d10dd | 395 | if (minfo->dead) { |
1da177e4 LT |
396 | return -ENXIO; |
397 | } | |
fc2d10dd | 398 | minfo->usecount++; |
1da177e4 | 399 | if (user) { |
fc2d10dd | 400 | minfo->userusecount++; |
1da177e4 LT |
401 | } |
402 | return(0); | |
403 | } | |
404 | ||
405 | static int matroxfb_release(struct fb_info *info, int user) | |
406 | { | |
ee5a2749 | 407 | struct matrox_fb_info *minfo = info2minfo(info); |
a50d913f | 408 | |
5ae12170 | 409 | DBG_LOOP(__func__) |
1da177e4 LT |
410 | |
411 | if (user) { | |
fc2d10dd | 412 | if (0 == --minfo->userusecount) { |
316b4d64 | 413 | matroxfb_disable_irq(minfo); |
1da177e4 LT |
414 | } |
415 | } | |
fc2d10dd | 416 | if (!(--minfo->usecount) && minfo->dead) { |
316b4d64 | 417 | matroxfb_remove(minfo, 0); |
1da177e4 LT |
418 | } |
419 | return(0); | |
420 | } | |
421 | ||
422 | static int matroxfb_pan_display(struct fb_var_screeninfo *var, | |
423 | struct fb_info* info) { | |
ee5a2749 | 424 | struct matrox_fb_info *minfo = info2minfo(info); |
1da177e4 | 425 | |
5ae12170 | 426 | DBG(__func__) |
1da177e4 | 427 | |
316b4d64 | 428 | matrox_pan_var(minfo, var); |
1da177e4 LT |
429 | return 0; |
430 | } | |
431 | ||
316b4d64 JD |
432 | static int matroxfb_get_final_bppShift(const struct matrox_fb_info *minfo, |
433 | int bpp) | |
434 | { | |
1da177e4 LT |
435 | int bppshft2; |
436 | ||
5ae12170 | 437 | DBG(__func__) |
1da177e4 LT |
438 | |
439 | bppshft2 = bpp; | |
440 | if (!bppshft2) { | |
441 | return 8; | |
442 | } | |
fc2d10dd | 443 | if (isInterleave(minfo)) |
1da177e4 | 444 | bppshft2 >>= 1; |
fc2d10dd | 445 | if (minfo->devflags.video64bits) |
1da177e4 LT |
446 | bppshft2 >>= 1; |
447 | return bppshft2; | |
448 | } | |
449 | ||
316b4d64 JD |
450 | static int matroxfb_test_and_set_rounding(const struct matrox_fb_info *minfo, |
451 | int xres, int bpp) | |
452 | { | |
1da177e4 LT |
453 | int over; |
454 | int rounding; | |
455 | ||
5ae12170 | 456 | DBG(__func__) |
1da177e4 LT |
457 | |
458 | switch (bpp) { | |
459 | case 0: return xres; | |
460 | case 4: rounding = 128; | |
461 | break; | |
462 | case 8: rounding = 64; /* doc says 64; 32 is OK for G400 */ | |
463 | break; | |
464 | case 16: rounding = 32; | |
465 | break; | |
466 | case 24: rounding = 64; /* doc says 64; 32 is OK for G400 */ | |
467 | break; | |
468 | default: rounding = 16; | |
469 | /* on G400, 16 really does not work */ | |
fc2d10dd | 470 | if (minfo->devflags.accelerator == FB_ACCEL_MATROX_MGAG400) |
1da177e4 LT |
471 | rounding = 32; |
472 | break; | |
473 | } | |
fc2d10dd | 474 | if (isInterleave(minfo)) { |
1da177e4 LT |
475 | rounding *= 2; |
476 | } | |
477 | over = xres % rounding; | |
478 | if (over) | |
479 | xres += rounding-over; | |
480 | return xres; | |
481 | } | |
482 | ||
316b4d64 JD |
483 | static int matroxfb_pitch_adjust(const struct matrox_fb_info *minfo, int xres, |
484 | int bpp) | |
485 | { | |
1da177e4 LT |
486 | const int* width; |
487 | int xres_new; | |
488 | ||
5ae12170 | 489 | DBG(__func__) |
1da177e4 LT |
490 | |
491 | if (!bpp) return xres; | |
492 | ||
fc2d10dd | 493 | width = minfo->capable.vxres; |
1da177e4 | 494 | |
fc2d10dd | 495 | if (minfo->devflags.precise_width) { |
1da177e4 | 496 | while (*width) { |
316b4d64 | 497 | if ((*width >= xres) && (matroxfb_test_and_set_rounding(minfo, *width, bpp) == *width)) { |
1da177e4 LT |
498 | break; |
499 | } | |
500 | width++; | |
501 | } | |
502 | xres_new = *width; | |
503 | } else { | |
316b4d64 | 504 | xres_new = matroxfb_test_and_set_rounding(minfo, xres, bpp); |
1da177e4 | 505 | } |
1da177e4 LT |
506 | return xres_new; |
507 | } | |
508 | ||
509 | static int matroxfb_get_cmap_len(struct fb_var_screeninfo *var) { | |
510 | ||
5ae12170 | 511 | DBG(__func__) |
1da177e4 LT |
512 | |
513 | switch (var->bits_per_pixel) { | |
514 | case 4: | |
515 | return 16; /* pseudocolor... 16 entries HW palette */ | |
516 | case 8: | |
517 | return 256; /* pseudocolor... 256 entries HW palette */ | |
518 | case 16: | |
519 | return 16; /* directcolor... 16 entries SW palette */ | |
520 | /* Mystique: truecolor, 16 entries SW palette, HW palette hardwired into 1:1 mapping */ | |
521 | case 24: | |
522 | return 16; /* directcolor... 16 entries SW palette */ | |
523 | /* Mystique: truecolor, 16 entries SW palette, HW palette hardwired into 1:1 mapping */ | |
524 | case 32: | |
525 | return 16; /* directcolor... 16 entries SW palette */ | |
526 | /* Mystique: truecolor, 16 entries SW palette, HW palette hardwired into 1:1 mapping */ | |
527 | } | |
528 | return 16; /* return something reasonable... or panic()? */ | |
529 | } | |
530 | ||
316b4d64 JD |
531 | static int matroxfb_decode_var(const struct matrox_fb_info *minfo, |
532 | struct fb_var_screeninfo *var, int *visual, | |
533 | int *video_cmap_len, unsigned int* ydstorg) | |
534 | { | |
1da177e4 LT |
535 | struct RGBT { |
536 | unsigned char bpp; | |
537 | struct { | |
538 | unsigned char offset, | |
539 | length; | |
540 | } red, | |
541 | green, | |
542 | blue, | |
543 | transp; | |
544 | signed char visual; | |
545 | }; | |
546 | static const struct RGBT table[]= { | |
547 | { 8,{ 0,8},{0,8},{0,8},{ 0,0},MX_VISUAL_PSEUDOCOLOR}, | |
548 | {15,{10,5},{5,5},{0,5},{15,1},MX_VISUAL_DIRECTCOLOR}, | |
549 | {16,{11,5},{5,6},{0,5},{ 0,0},MX_VISUAL_DIRECTCOLOR}, | |
550 | {24,{16,8},{8,8},{0,8},{ 0,0},MX_VISUAL_DIRECTCOLOR}, | |
551 | {32,{16,8},{8,8},{0,8},{24,8},MX_VISUAL_DIRECTCOLOR} | |
552 | }; | |
553 | struct RGBT const *rgbt; | |
554 | unsigned int bpp = var->bits_per_pixel; | |
555 | unsigned int vramlen; | |
556 | unsigned int memlen; | |
557 | ||
5ae12170 | 558 | DBG(__func__) |
1da177e4 LT |
559 | |
560 | switch (bpp) { | |
fc2d10dd | 561 | case 4: if (!minfo->capable.cfb4) return -EINVAL; |
1da177e4 LT |
562 | break; |
563 | case 8: break; | |
564 | case 16: break; | |
565 | case 24: break; | |
566 | case 32: break; | |
567 | default: return -EINVAL; | |
568 | } | |
569 | *ydstorg = 0; | |
fc2d10dd | 570 | vramlen = minfo->video.len_usable; |
1da177e4 LT |
571 | if (var->yres_virtual < var->yres) |
572 | var->yres_virtual = var->yres; | |
573 | if (var->xres_virtual < var->xres) | |
574 | var->xres_virtual = var->xres; | |
575 | ||
316b4d64 | 576 | var->xres_virtual = matroxfb_pitch_adjust(minfo, var->xres_virtual, bpp); |
1da177e4 LT |
577 | memlen = var->xres_virtual * bpp * var->yres_virtual / 8; |
578 | if (memlen > vramlen) { | |
579 | var->yres_virtual = vramlen * 8 / (var->xres_virtual * bpp); | |
580 | memlen = var->xres_virtual * bpp * var->yres_virtual / 8; | |
581 | } | |
582 | /* There is hardware bug that no line can cross 4MB boundary */ | |
583 | /* give up for CFB24, it is impossible to easy workaround it */ | |
584 | /* for other try to do something */ | |
fc2d10dd | 585 | if (!minfo->capable.cross4MB && (memlen > 0x400000)) { |
1da177e4 LT |
586 | if (bpp == 24) { |
587 | /* sorry */ | |
588 | } else { | |
589 | unsigned int linelen; | |
590 | unsigned int m1 = linelen = var->xres_virtual * bpp / 8; | |
591 | unsigned int m2 = PAGE_SIZE; /* or 128 if you do not need PAGE ALIGNED address */ | |
592 | unsigned int max_yres; | |
593 | ||
594 | while (m1) { | |
595 | int t; | |
596 | ||
597 | while (m2 >= m1) m2 -= m1; | |
598 | t = m1; | |
599 | m1 = m2; | |
600 | m2 = t; | |
601 | } | |
602 | m2 = linelen * PAGE_SIZE / m2; | |
603 | *ydstorg = m2 = 0x400000 % m2; | |
604 | max_yres = (vramlen - m2) / linelen; | |
605 | if (var->yres_virtual > max_yres) | |
606 | var->yres_virtual = max_yres; | |
607 | } | |
608 | } | |
609 | /* YDSTLEN contains only signed 16bit value */ | |
610 | if (var->yres_virtual > 32767) | |
611 | var->yres_virtual = 32767; | |
612 | /* we must round yres/xres down, we already rounded y/xres_virtual up | |
613 | if it was possible. We should return -EINVAL, but I disagree */ | |
614 | if (var->yres_virtual < var->yres) | |
615 | var->yres = var->yres_virtual; | |
616 | if (var->xres_virtual < var->xres) | |
617 | var->xres = var->xres_virtual; | |
618 | if (var->xoffset + var->xres > var->xres_virtual) | |
619 | var->xoffset = var->xres_virtual - var->xres; | |
620 | if (var->yoffset + var->yres > var->yres_virtual) | |
621 | var->yoffset = var->yres_virtual - var->yres; | |
622 | ||
623 | if (bpp == 16 && var->green.length == 5) { | |
25985edc | 624 | bpp--; /* an artificial value - 15 */ |
1da177e4 LT |
625 | } |
626 | ||
627 | for (rgbt = table; rgbt->bpp < bpp; rgbt++); | |
628 | #define SETCLR(clr)\ | |
629 | var->clr.offset = rgbt->clr.offset;\ | |
630 | var->clr.length = rgbt->clr.length | |
631 | SETCLR(red); | |
632 | SETCLR(green); | |
633 | SETCLR(blue); | |
634 | SETCLR(transp); | |
635 | #undef SETCLR | |
636 | *visual = rgbt->visual; | |
637 | ||
638 | if (bpp > 8) | |
639 | dprintk("matroxfb: truecolor: " | |
640 | "size=%d:%d:%d:%d, shift=%d:%d:%d:%d\n", | |
641 | var->transp.length, var->red.length, var->green.length, var->blue.length, | |
642 | var->transp.offset, var->red.offset, var->green.offset, var->blue.offset); | |
643 | ||
644 | *video_cmap_len = matroxfb_get_cmap_len(var); | |
645 | dprintk(KERN_INFO "requested %d*%d/%dbpp (%d*%d)\n", var->xres, var->yres, var->bits_per_pixel, | |
646 | var->xres_virtual, var->yres_virtual); | |
647 | return 0; | |
648 | } | |
649 | ||
650 | static int matroxfb_setcolreg(unsigned regno, unsigned red, unsigned green, | |
651 | unsigned blue, unsigned transp, | |
652 | struct fb_info *fb_info) | |
653 | { | |
1da177e4 | 654 | struct matrox_fb_info* minfo = container_of(fb_info, struct matrox_fb_info, fbcon); |
1da177e4 | 655 | |
5ae12170 | 656 | DBG(__func__) |
1da177e4 LT |
657 | |
658 | /* | |
659 | * Set a single color register. The values supplied are | |
660 | * already rounded down to the hardware's capabilities | |
661 | * (according to the entries in the `var' structure). Return | |
662 | * != 0 for invalid regno. | |
663 | */ | |
664 | ||
fc2d10dd | 665 | if (regno >= minfo->curr.cmap_len) |
1da177e4 LT |
666 | return 1; |
667 | ||
fc2d10dd | 668 | if (minfo->fbcon.var.grayscale) { |
1da177e4 LT |
669 | /* gray = 0.30*R + 0.59*G + 0.11*B */ |
670 | red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; | |
671 | } | |
672 | ||
fc2d10dd JD |
673 | red = CNVT_TOHW(red, minfo->fbcon.var.red.length); |
674 | green = CNVT_TOHW(green, minfo->fbcon.var.green.length); | |
675 | blue = CNVT_TOHW(blue, minfo->fbcon.var.blue.length); | |
676 | transp = CNVT_TOHW(transp, minfo->fbcon.var.transp.length); | |
1da177e4 | 677 | |
fc2d10dd | 678 | switch (minfo->fbcon.var.bits_per_pixel) { |
1da177e4 LT |
679 | case 4: |
680 | case 8: | |
681 | mga_outb(M_DAC_REG, regno); | |
682 | mga_outb(M_DAC_VAL, red); | |
683 | mga_outb(M_DAC_VAL, green); | |
684 | mga_outb(M_DAC_VAL, blue); | |
685 | break; | |
686 | case 16: | |
08a498de AD |
687 | if (regno >= 16) |
688 | break; | |
1da177e4 LT |
689 | { |
690 | u_int16_t col = | |
fc2d10dd JD |
691 | (red << minfo->fbcon.var.red.offset) | |
692 | (green << minfo->fbcon.var.green.offset) | | |
693 | (blue << minfo->fbcon.var.blue.offset) | | |
694 | (transp << minfo->fbcon.var.transp.offset); /* for 1:5:5:5 */ | |
695 | minfo->cmap[regno] = col | (col << 16); | |
1da177e4 LT |
696 | } |
697 | break; | |
698 | case 24: | |
699 | case 32: | |
08a498de AD |
700 | if (regno >= 16) |
701 | break; | |
fc2d10dd JD |
702 | minfo->cmap[regno] = |
703 | (red << minfo->fbcon.var.red.offset) | | |
704 | (green << minfo->fbcon.var.green.offset) | | |
705 | (blue << minfo->fbcon.var.blue.offset) | | |
706 | (transp << minfo->fbcon.var.transp.offset); /* 8:8:8:8 */ | |
1da177e4 LT |
707 | break; |
708 | } | |
709 | return 0; | |
710 | } | |
711 | ||
316b4d64 | 712 | static void matroxfb_init_fix(struct matrox_fb_info *minfo) |
1da177e4 | 713 | { |
fc2d10dd | 714 | struct fb_fix_screeninfo *fix = &minfo->fbcon.fix; |
5ae12170 | 715 | DBG(__func__) |
1da177e4 LT |
716 | |
717 | strcpy(fix->id,"MATROX"); | |
718 | ||
719 | fix->xpanstep = 8; /* 8 for 8bpp, 4 for 16bpp, 2 for 32bpp */ | |
720 | fix->ypanstep = 1; | |
721 | fix->ywrapstep = 0; | |
fc2d10dd JD |
722 | fix->mmio_start = minfo->mmio.base; |
723 | fix->mmio_len = minfo->mmio.len; | |
724 | fix->accel = minfo->devflags.accelerator; | |
1da177e4 LT |
725 | } |
726 | ||
316b4d64 | 727 | static void matroxfb_update_fix(struct matrox_fb_info *minfo) |
1da177e4 | 728 | { |
fc2d10dd | 729 | struct fb_fix_screeninfo *fix = &minfo->fbcon.fix; |
5ae12170 | 730 | DBG(__func__) |
1da177e4 | 731 | |
fc2d10dd JD |
732 | mutex_lock(&minfo->fbcon.mm_lock); |
733 | fix->smem_start = minfo->video.base + minfo->curr.ydstorg.bytes; | |
734 | fix->smem_len = minfo->video.len_usable - minfo->curr.ydstorg.bytes; | |
735 | mutex_unlock(&minfo->fbcon.mm_lock); | |
1da177e4 LT |
736 | } |
737 | ||
738 | static int matroxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | |
739 | { | |
740 | int err; | |
741 | int visual; | |
742 | int cmap_len; | |
743 | unsigned int ydstorg; | |
ee5a2749 | 744 | struct matrox_fb_info *minfo = info2minfo(info); |
1da177e4 | 745 | |
fc2d10dd | 746 | if (minfo->dead) { |
1da177e4 LT |
747 | return -ENXIO; |
748 | } | |
316b4d64 | 749 | if ((err = matroxfb_decode_var(minfo, var, &visual, &cmap_len, &ydstorg)) != 0) |
1da177e4 LT |
750 | return err; |
751 | return 0; | |
752 | } | |
753 | ||
754 | static int matroxfb_set_par(struct fb_info *info) | |
755 | { | |
756 | int err; | |
757 | int visual; | |
758 | int cmap_len; | |
759 | unsigned int ydstorg; | |
760 | struct fb_var_screeninfo *var; | |
ee5a2749 | 761 | struct matrox_fb_info *minfo = info2minfo(info); |
1da177e4 | 762 | |
5ae12170 | 763 | DBG(__func__) |
1da177e4 | 764 | |
fc2d10dd | 765 | if (minfo->dead) { |
1da177e4 LT |
766 | return -ENXIO; |
767 | } | |
768 | ||
769 | var = &info->var; | |
316b4d64 | 770 | if ((err = matroxfb_decode_var(minfo, var, &visual, &cmap_len, &ydstorg)) != 0) |
1da177e4 | 771 | return err; |
fc2d10dd | 772 | minfo->fbcon.screen_base = vaddr_va(minfo->video.vbase) + ydstorg; |
316b4d64 | 773 | matroxfb_update_fix(minfo); |
fc2d10dd JD |
774 | minfo->fbcon.fix.visual = visual; |
775 | minfo->fbcon.fix.type = FB_TYPE_PACKED_PIXELS; | |
776 | minfo->fbcon.fix.type_aux = 0; | |
777 | minfo->fbcon.fix.line_length = (var->xres_virtual * var->bits_per_pixel) >> 3; | |
1da177e4 LT |
778 | { |
779 | unsigned int pos; | |
780 | ||
fc2d10dd JD |
781 | minfo->curr.cmap_len = cmap_len; |
782 | ydstorg += minfo->devflags.ydstorg; | |
783 | minfo->curr.ydstorg.bytes = ydstorg; | |
784 | minfo->curr.ydstorg.chunks = ydstorg >> (isInterleave(minfo) ? 3 : 2); | |
1da177e4 | 785 | if (var->bits_per_pixel == 4) |
fc2d10dd | 786 | minfo->curr.ydstorg.pixels = ydstorg; |
1da177e4 | 787 | else |
fc2d10dd | 788 | minfo->curr.ydstorg.pixels = (ydstorg * 8) / var->bits_per_pixel; |
316b4d64 | 789 | minfo->curr.final_bppShift = matroxfb_get_final_bppShift(minfo, var->bits_per_pixel); |
1da177e4 LT |
790 | { struct my_timming mt; |
791 | struct matrox_hw_state* hw; | |
792 | int out; | |
793 | ||
794 | matroxfb_var2my(var, &mt); | |
795 | mt.crtc = MATROXFB_SRC_CRTC1; | |
796 | /* CRTC1 delays */ | |
797 | switch (var->bits_per_pixel) { | |
798 | case 0: mt.delay = 31 + 0; break; | |
799 | case 16: mt.delay = 21 + 8; break; | |
800 | case 24: mt.delay = 17 + 8; break; | |
801 | case 32: mt.delay = 16 + 8; break; | |
802 | default: mt.delay = 31 + 8; break; | |
803 | } | |
804 | ||
fc2d10dd | 805 | hw = &minfo->hw; |
1da177e4 | 806 | |
fc2d10dd | 807 | down_read(&minfo->altout.lock); |
1da177e4 | 808 | for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { |
fc2d10dd JD |
809 | if (minfo->outputs[out].src == MATROXFB_SRC_CRTC1 && |
810 | minfo->outputs[out].output->compute) { | |
811 | minfo->outputs[out].output->compute(minfo->outputs[out].data, &mt); | |
1da177e4 LT |
812 | } |
813 | } | |
fc2d10dd JD |
814 | up_read(&minfo->altout.lock); |
815 | minfo->crtc1.pixclock = mt.pixclock; | |
816 | minfo->crtc1.mnp = mt.mnp; | |
316b4d64 | 817 | minfo->hw_switch->init(minfo, &mt); |
fc2d10dd JD |
818 | pos = (var->yoffset * var->xres_virtual + var->xoffset) * minfo->curr.final_bppShift / 32; |
819 | pos += minfo->curr.ydstorg.chunks; | |
1da177e4 LT |
820 | |
821 | hw->CRTC[0x0D] = pos & 0xFF; | |
822 | hw->CRTC[0x0C] = (pos & 0xFF00) >> 8; | |
823 | hw->CRTCEXT[0] = (hw->CRTCEXT[0] & 0xF0) | ((pos >> 16) & 0x0F) | ((pos >> 14) & 0x40); | |
824 | hw->CRTCEXT[8] = pos >> 21; | |
316b4d64 JD |
825 | minfo->hw_switch->restore(minfo); |
826 | update_crtc2(minfo, pos); | |
fc2d10dd | 827 | down_read(&minfo->altout.lock); |
1da177e4 | 828 | for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { |
fc2d10dd JD |
829 | if (minfo->outputs[out].src == MATROXFB_SRC_CRTC1 && |
830 | minfo->outputs[out].output->program) { | |
831 | minfo->outputs[out].output->program(minfo->outputs[out].data); | |
1da177e4 LT |
832 | } |
833 | } | |
834 | for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { | |
fc2d10dd JD |
835 | if (minfo->outputs[out].src == MATROXFB_SRC_CRTC1 && |
836 | minfo->outputs[out].output->start) { | |
837 | minfo->outputs[out].output->start(minfo->outputs[out].data); | |
1da177e4 LT |
838 | } |
839 | } | |
fc2d10dd | 840 | up_read(&minfo->altout.lock); |
316b4d64 | 841 | matrox_cfbX_init(minfo); |
1da177e4 LT |
842 | } |
843 | } | |
fc2d10dd | 844 | minfo->initialized = 1; |
1da177e4 LT |
845 | return 0; |
846 | } | |
847 | ||
316b4d64 JD |
848 | static int matroxfb_get_vblank(struct matrox_fb_info *minfo, |
849 | struct fb_vblank *vblank) | |
1da177e4 LT |
850 | { |
851 | unsigned int sts1; | |
852 | ||
316b4d64 | 853 | matroxfb_enable_irq(minfo, 0); |
1da177e4 LT |
854 | memset(vblank, 0, sizeof(*vblank)); |
855 | vblank->flags = FB_VBLANK_HAVE_VCOUNT | FB_VBLANK_HAVE_VSYNC | | |
856 | FB_VBLANK_HAVE_VBLANK | FB_VBLANK_HAVE_HBLANK; | |
857 | sts1 = mga_inb(M_INSTS1); | |
858 | vblank->vcount = mga_inl(M_VCOUNT); | |
859 | /* BTW, on my PIII/450 with G400, reading M_INSTS1 | |
860 | byte makes this call about 12% slower (1.70 vs. 2.05 us | |
861 | per ioctl()) */ | |
862 | if (sts1 & 1) | |
863 | vblank->flags |= FB_VBLANK_HBLANKING; | |
864 | if (sts1 & 8) | |
865 | vblank->flags |= FB_VBLANK_VSYNCING; | |
fc2d10dd | 866 | if (vblank->vcount >= minfo->fbcon.var.yres) |
1da177e4 | 867 | vblank->flags |= FB_VBLANK_VBLANKING; |
fc2d10dd | 868 | if (test_bit(0, &minfo->irq_flags)) { |
1da177e4 | 869 | vblank->flags |= FB_VBLANK_HAVE_COUNT; |
a50d913f | 870 | /* Only one writer, aligned int value... |
1da177e4 | 871 | it should work without lock and without atomic_t */ |
fc2d10dd | 872 | vblank->count = minfo->crtc1.vsync.cnt; |
1da177e4 LT |
873 | } |
874 | return 0; | |
875 | } | |
876 | ||
877 | static struct matrox_altout panellink_output = { | |
878 | .name = "Panellink output", | |
879 | }; | |
880 | ||
67a6680d CH |
881 | static int matroxfb_ioctl(struct fb_info *info, |
882 | unsigned int cmd, unsigned long arg) | |
1da177e4 LT |
883 | { |
884 | void __user *argp = (void __user *)arg; | |
ee5a2749 | 885 | struct matrox_fb_info *minfo = info2minfo(info); |
a50d913f | 886 | |
5ae12170 | 887 | DBG(__func__) |
1da177e4 | 888 | |
fc2d10dd | 889 | if (minfo->dead) { |
1da177e4 LT |
890 | return -ENXIO; |
891 | } | |
892 | ||
893 | switch (cmd) { | |
894 | case FBIOGET_VBLANK: | |
895 | { | |
896 | struct fb_vblank vblank; | |
897 | int err; | |
898 | ||
316b4d64 | 899 | err = matroxfb_get_vblank(minfo, &vblank); |
1da177e4 LT |
900 | if (err) |
901 | return err; | |
902 | if (copy_to_user(argp, &vblank, sizeof(vblank))) | |
903 | return -EFAULT; | |
904 | return 0; | |
905 | } | |
906 | case FBIO_WAITFORVSYNC: | |
907 | { | |
908 | u_int32_t crt; | |
909 | ||
910 | if (get_user(crt, (u_int32_t __user *)arg)) | |
911 | return -EFAULT; | |
912 | ||
316b4d64 | 913 | return matroxfb_wait_for_sync(minfo, crt); |
1da177e4 LT |
914 | } |
915 | case MATROXFB_SET_OUTPUT_MODE: | |
916 | { | |
917 | struct matroxioc_output_mode mom; | |
918 | struct matrox_altout *oproc; | |
919 | int val; | |
920 | ||
921 | if (copy_from_user(&mom, argp, sizeof(mom))) | |
922 | return -EFAULT; | |
923 | if (mom.output >= MATROXFB_MAX_OUTPUTS) | |
924 | return -ENXIO; | |
fc2d10dd JD |
925 | down_read(&minfo->altout.lock); |
926 | oproc = minfo->outputs[mom.output].output; | |
1da177e4 LT |
927 | if (!oproc) { |
928 | val = -ENXIO; | |
929 | } else if (!oproc->verifymode) { | |
930 | if (mom.mode == MATROXFB_OUTPUT_MODE_MONITOR) { | |
931 | val = 0; | |
932 | } else { | |
933 | val = -EINVAL; | |
934 | } | |
935 | } else { | |
fc2d10dd | 936 | val = oproc->verifymode(minfo->outputs[mom.output].data, mom.mode); |
1da177e4 LT |
937 | } |
938 | if (!val) { | |
fc2d10dd JD |
939 | if (minfo->outputs[mom.output].mode != mom.mode) { |
940 | minfo->outputs[mom.output].mode = mom.mode; | |
1da177e4 LT |
941 | val = 1; |
942 | } | |
943 | } | |
fc2d10dd | 944 | up_read(&minfo->altout.lock); |
1da177e4 LT |
945 | if (val != 1) |
946 | return val; | |
fc2d10dd | 947 | switch (minfo->outputs[mom.output].src) { |
1da177e4 LT |
948 | case MATROXFB_SRC_CRTC1: |
949 | matroxfb_set_par(info); | |
950 | break; | |
951 | case MATROXFB_SRC_CRTC2: | |
952 | { | |
953 | struct matroxfb_dh_fb_info* crtc2; | |
954 | ||
fc2d10dd JD |
955 | down_read(&minfo->crtc2.lock); |
956 | crtc2 = minfo->crtc2.info; | |
1da177e4 LT |
957 | if (crtc2) |
958 | crtc2->fbcon.fbops->fb_set_par(&crtc2->fbcon); | |
fc2d10dd | 959 | up_read(&minfo->crtc2.lock); |
1da177e4 LT |
960 | } |
961 | break; | |
962 | } | |
963 | return 0; | |
964 | } | |
965 | case MATROXFB_GET_OUTPUT_MODE: | |
966 | { | |
967 | struct matroxioc_output_mode mom; | |
968 | struct matrox_altout *oproc; | |
969 | int val; | |
970 | ||
971 | if (copy_from_user(&mom, argp, sizeof(mom))) | |
972 | return -EFAULT; | |
973 | if (mom.output >= MATROXFB_MAX_OUTPUTS) | |
974 | return -ENXIO; | |
fc2d10dd JD |
975 | down_read(&minfo->altout.lock); |
976 | oproc = minfo->outputs[mom.output].output; | |
1da177e4 LT |
977 | if (!oproc) { |
978 | val = -ENXIO; | |
979 | } else { | |
fc2d10dd | 980 | mom.mode = minfo->outputs[mom.output].mode; |
1da177e4 LT |
981 | val = 0; |
982 | } | |
fc2d10dd | 983 | up_read(&minfo->altout.lock); |
1da177e4 LT |
984 | if (val) |
985 | return val; | |
986 | if (copy_to_user(argp, &mom, sizeof(mom))) | |
987 | return -EFAULT; | |
988 | return 0; | |
989 | } | |
990 | case MATROXFB_SET_OUTPUT_CONNECTION: | |
991 | { | |
992 | u_int32_t tmp; | |
993 | int i; | |
994 | int changes; | |
995 | ||
996 | if (copy_from_user(&tmp, argp, sizeof(tmp))) | |
997 | return -EFAULT; | |
998 | for (i = 0; i < 32; i++) { | |
999 | if (tmp & (1 << i)) { | |
1000 | if (i >= MATROXFB_MAX_OUTPUTS) | |
1001 | return -ENXIO; | |
fc2d10dd | 1002 | if (!minfo->outputs[i].output) |
1da177e4 | 1003 | return -ENXIO; |
fc2d10dd | 1004 | switch (minfo->outputs[i].src) { |
1da177e4 LT |
1005 | case MATROXFB_SRC_NONE: |
1006 | case MATROXFB_SRC_CRTC1: | |
1007 | break; | |
1008 | default: | |
1009 | return -EBUSY; | |
1010 | } | |
1011 | } | |
1012 | } | |
fc2d10dd | 1013 | if (minfo->devflags.panellink) { |
1da177e4 LT |
1014 | if (tmp & MATROXFB_OUTPUT_CONN_DFP) { |
1015 | if (tmp & MATROXFB_OUTPUT_CONN_SECONDARY) | |
1016 | return -EINVAL; | |
1017 | for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) { | |
fc2d10dd | 1018 | if (minfo->outputs[i].src == MATROXFB_SRC_CRTC2) { |
1da177e4 LT |
1019 | return -EBUSY; |
1020 | } | |
1021 | } | |
1022 | } | |
1023 | } | |
1024 | changes = 0; | |
1025 | for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) { | |
1026 | if (tmp & (1 << i)) { | |
fc2d10dd | 1027 | if (minfo->outputs[i].src != MATROXFB_SRC_CRTC1) { |
1da177e4 | 1028 | changes = 1; |
fc2d10dd | 1029 | minfo->outputs[i].src = MATROXFB_SRC_CRTC1; |
1da177e4 | 1030 | } |
fc2d10dd | 1031 | } else if (minfo->outputs[i].src == MATROXFB_SRC_CRTC1) { |
1da177e4 | 1032 | changes = 1; |
fc2d10dd | 1033 | minfo->outputs[i].src = MATROXFB_SRC_NONE; |
1da177e4 LT |
1034 | } |
1035 | } | |
1036 | if (!changes) | |
1037 | return 0; | |
1038 | matroxfb_set_par(info); | |
1039 | return 0; | |
1040 | } | |
1041 | case MATROXFB_GET_OUTPUT_CONNECTION: | |
1042 | { | |
1043 | u_int32_t conn = 0; | |
1044 | int i; | |
1045 | ||
1046 | for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) { | |
fc2d10dd | 1047 | if (minfo->outputs[i].src == MATROXFB_SRC_CRTC1) { |
1da177e4 LT |
1048 | conn |= 1 << i; |
1049 | } | |
1050 | } | |
1051 | if (put_user(conn, (u_int32_t __user *)arg)) | |
1052 | return -EFAULT; | |
1053 | return 0; | |
1054 | } | |
1055 | case MATROXFB_GET_AVAILABLE_OUTPUTS: | |
1056 | { | |
1057 | u_int32_t conn = 0; | |
1058 | int i; | |
1059 | ||
1060 | for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) { | |
fc2d10dd JD |
1061 | if (minfo->outputs[i].output) { |
1062 | switch (minfo->outputs[i].src) { | |
1da177e4 LT |
1063 | case MATROXFB_SRC_NONE: |
1064 | case MATROXFB_SRC_CRTC1: | |
1065 | conn |= 1 << i; | |
1066 | break; | |
1067 | } | |
1068 | } | |
1069 | } | |
fc2d10dd | 1070 | if (minfo->devflags.panellink) { |
1da177e4 LT |
1071 | if (conn & MATROXFB_OUTPUT_CONN_DFP) |
1072 | conn &= ~MATROXFB_OUTPUT_CONN_SECONDARY; | |
1073 | if (conn & MATROXFB_OUTPUT_CONN_SECONDARY) | |
1074 | conn &= ~MATROXFB_OUTPUT_CONN_DFP; | |
1075 | } | |
1076 | if (put_user(conn, (u_int32_t __user *)arg)) | |
1077 | return -EFAULT; | |
1078 | return 0; | |
1079 | } | |
1080 | case MATROXFB_GET_ALL_OUTPUTS: | |
1081 | { | |
1082 | u_int32_t conn = 0; | |
1083 | int i; | |
1084 | ||
1085 | for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) { | |
fc2d10dd | 1086 | if (minfo->outputs[i].output) { |
1da177e4 LT |
1087 | conn |= 1 << i; |
1088 | } | |
1089 | } | |
1090 | if (put_user(conn, (u_int32_t __user *)arg)) | |
1091 | return -EFAULT; | |
1092 | return 0; | |
1093 | } | |
1094 | case VIDIOC_QUERYCAP: | |
1095 | { | |
1096 | struct v4l2_capability r; | |
a50d913f | 1097 | |
1da177e4 LT |
1098 | memset(&r, 0, sizeof(r)); |
1099 | strcpy(r.driver, "matroxfb"); | |
1100 | strcpy(r.card, "Matrox"); | |
fc2d10dd | 1101 | sprintf(r.bus_info, "PCI:%s", pci_name(minfo->pcidev)); |
1da177e4 LT |
1102 | r.version = KERNEL_VERSION(1,0,0); |
1103 | r.capabilities = V4L2_CAP_VIDEO_OUTPUT; | |
1104 | if (copy_to_user(argp, &r, sizeof(r))) | |
1105 | return -EFAULT; | |
1106 | return 0; | |
a50d913f | 1107 | |
1da177e4 LT |
1108 | } |
1109 | case VIDIOC_QUERYCTRL: | |
1110 | { | |
1111 | struct v4l2_queryctrl qctrl; | |
1112 | int err; | |
1113 | ||
1114 | if (copy_from_user(&qctrl, argp, sizeof(qctrl))) | |
1115 | return -EFAULT; | |
1116 | ||
fc2d10dd JD |
1117 | down_read(&minfo->altout.lock); |
1118 | if (!minfo->outputs[1].output) { | |
1da177e4 | 1119 | err = -ENXIO; |
fc2d10dd JD |
1120 | } else if (minfo->outputs[1].output->getqueryctrl) { |
1121 | err = minfo->outputs[1].output->getqueryctrl(minfo->outputs[1].data, &qctrl); | |
1da177e4 LT |
1122 | } else { |
1123 | err = -EINVAL; | |
1124 | } | |
fc2d10dd | 1125 | up_read(&minfo->altout.lock); |
1da177e4 LT |
1126 | if (err >= 0 && |
1127 | copy_to_user(argp, &qctrl, sizeof(qctrl))) | |
1128 | return -EFAULT; | |
1129 | return err; | |
1130 | } | |
1131 | case VIDIOC_G_CTRL: | |
1132 | { | |
1133 | struct v4l2_control ctrl; | |
1134 | int err; | |
1135 | ||
1136 | if (copy_from_user(&ctrl, argp, sizeof(ctrl))) | |
1137 | return -EFAULT; | |
1138 | ||
fc2d10dd JD |
1139 | down_read(&minfo->altout.lock); |
1140 | if (!minfo->outputs[1].output) { | |
1da177e4 | 1141 | err = -ENXIO; |
fc2d10dd JD |
1142 | } else if (minfo->outputs[1].output->getctrl) { |
1143 | err = minfo->outputs[1].output->getctrl(minfo->outputs[1].data, &ctrl); | |
1da177e4 LT |
1144 | } else { |
1145 | err = -EINVAL; | |
1146 | } | |
fc2d10dd | 1147 | up_read(&minfo->altout.lock); |
1da177e4 LT |
1148 | if (err >= 0 && |
1149 | copy_to_user(argp, &ctrl, sizeof(ctrl))) | |
1150 | return -EFAULT; | |
1151 | return err; | |
1152 | } | |
1da177e4 LT |
1153 | case VIDIOC_S_CTRL: |
1154 | { | |
1155 | struct v4l2_control ctrl; | |
1156 | int err; | |
1157 | ||
1158 | if (copy_from_user(&ctrl, argp, sizeof(ctrl))) | |
1159 | return -EFAULT; | |
1160 | ||
fc2d10dd JD |
1161 | down_read(&minfo->altout.lock); |
1162 | if (!minfo->outputs[1].output) { | |
1da177e4 | 1163 | err = -ENXIO; |
fc2d10dd JD |
1164 | } else if (minfo->outputs[1].output->setctrl) { |
1165 | err = minfo->outputs[1].output->setctrl(minfo->outputs[1].data, &ctrl); | |
1da177e4 LT |
1166 | } else { |
1167 | err = -EINVAL; | |
1168 | } | |
fc2d10dd | 1169 | up_read(&minfo->altout.lock); |
1da177e4 LT |
1170 | return err; |
1171 | } | |
1172 | } | |
1173 | return -ENOTTY; | |
1174 | } | |
1175 | ||
1176 | /* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */ | |
1177 | ||
1178 | static int matroxfb_blank(int blank, struct fb_info *info) | |
1179 | { | |
1180 | int seq; | |
1181 | int crtc; | |
1182 | CRITFLAGS | |
ee5a2749 | 1183 | struct matrox_fb_info *minfo = info2minfo(info); |
1da177e4 | 1184 | |
5ae12170 | 1185 | DBG(__func__) |
1da177e4 | 1186 | |
fc2d10dd | 1187 | if (minfo->dead) |
1da177e4 LT |
1188 | return 1; |
1189 | ||
1190 | switch (blank) { | |
1191 | case FB_BLANK_NORMAL: seq = 0x20; crtc = 0x00; break; /* works ??? */ | |
1192 | case FB_BLANK_VSYNC_SUSPEND: seq = 0x20; crtc = 0x10; break; | |
1193 | case FB_BLANK_HSYNC_SUSPEND: seq = 0x20; crtc = 0x20; break; | |
1194 | case FB_BLANK_POWERDOWN: seq = 0x20; crtc = 0x30; break; | |
1195 | default: seq = 0x00; crtc = 0x00; break; | |
1196 | } | |
1197 | ||
1198 | CRITBEGIN | |
1199 | ||
1200 | mga_outb(M_SEQ_INDEX, 1); | |
1201 | mga_outb(M_SEQ_DATA, (mga_inb(M_SEQ_DATA) & ~0x20) | seq); | |
1202 | mga_outb(M_EXTVGA_INDEX, 1); | |
1203 | mga_outb(M_EXTVGA_DATA, (mga_inb(M_EXTVGA_DATA) & ~0x30) | crtc); | |
1204 | ||
1205 | CRITEND | |
1206 | return 0; | |
1207 | } | |
1208 | ||
1209 | static struct fb_ops matroxfb_ops = { | |
1210 | .owner = THIS_MODULE, | |
1211 | .fb_open = matroxfb_open, | |
1212 | .fb_release = matroxfb_release, | |
1213 | .fb_check_var = matroxfb_check_var, | |
1214 | .fb_set_par = matroxfb_set_par, | |
1215 | .fb_setcolreg = matroxfb_setcolreg, | |
1216 | .fb_pan_display =matroxfb_pan_display, | |
1217 | .fb_blank = matroxfb_blank, | |
1218 | .fb_ioctl = matroxfb_ioctl, | |
1219 | /* .fb_fillrect = <set by matrox_cfbX_init>, */ | |
1220 | /* .fb_copyarea = <set by matrox_cfbX_init>, */ | |
1221 | /* .fb_imageblit = <set by matrox_cfbX_init>, */ | |
1222 | /* .fb_cursor = <set by matrox_cfbX_init>, */ | |
1223 | }; | |
1224 | ||
1225 | #define RSDepth(X) (((X) >> 8) & 0x0F) | |
1226 | #define RS8bpp 0x1 | |
1227 | #define RS15bpp 0x2 | |
1228 | #define RS16bpp 0x3 | |
1229 | #define RS32bpp 0x4 | |
1230 | #define RS4bpp 0x5 | |
1231 | #define RS24bpp 0x6 | |
1232 | #define RSText 0x7 | |
1233 | #define RSText8 0x8 | |
1234 | /* 9-F */ | |
1235 | static struct { struct fb_bitfield red, green, blue, transp; int bits_per_pixel; } colors[] = { | |
1236 | { { 0, 8, 0}, { 0, 8, 0}, { 0, 8, 0}, { 0, 0, 0}, 8 }, | |
1237 | { { 10, 5, 0}, { 5, 5, 0}, { 0, 5, 0}, { 15, 1, 0}, 16 }, | |
1238 | { { 11, 5, 0}, { 5, 6, 0}, { 0, 5, 0}, { 0, 0, 0}, 16 }, | |
1239 | { { 16, 8, 0}, { 8, 8, 0}, { 0, 8, 0}, { 24, 8, 0}, 32 }, | |
1240 | { { 0, 8, 0}, { 0, 8, 0}, { 0, 8, 0}, { 0, 0, 0}, 4 }, | |
1241 | { { 16, 8, 0}, { 8, 8, 0}, { 0, 8, 0}, { 0, 0, 0}, 24 }, | |
1242 | { { 0, 6, 0}, { 0, 6, 0}, { 0, 6, 0}, { 0, 0, 0}, 0 }, /* textmode with (default) VGA8x16 */ | |
1243 | { { 0, 6, 0}, { 0, 6, 0}, { 0, 6, 0}, { 0, 0, 0}, 0 }, /* textmode hardwired to VGA8x8 */ | |
1244 | }; | |
1245 | ||
1246 | /* initialized by setup, see explanation at end of file (search for MODULE_PARM_DESC) */ | |
90a48151 | 1247 | static unsigned int mem; /* "matroxfb:mem:xxxxxM" */ |
1da177e4 | 1248 | static int option_precise_width = 1; /* cannot be changed, option_precise_width==0 must imply noaccel */ |
90a48151 VJ |
1249 | static int inv24; /* "matroxfb:inv24" */ |
1250 | static int cross4MB = -1; /* "matroxfb:cross4MB" */ | |
1251 | static int disabled; /* "matroxfb:disabled" */ | |
1252 | static int noaccel; /* "matroxfb:noaccel" */ | |
1253 | static int nopan; /* "matroxfb:nopan" */ | |
1254 | static int no_pci_retry; /* "matroxfb:nopciretry" */ | |
1255 | static int novga; /* "matroxfb:novga" */ | |
1256 | static int nobios; /* "matroxfb:nobios" */ | |
1257 | static int noinit = 1; /* "matroxfb:init" */ | |
1258 | static int inverse; /* "matroxfb:inverse" */ | |
1259 | static int sgram; /* "matroxfb:sgram" */ | |
1da177e4 | 1260 | #ifdef CONFIG_MTRR |
90a48151 | 1261 | static int mtrr = 1; /* "matroxfb:nomtrr" */ |
1da177e4 | 1262 | #endif |
90a48151 VJ |
1263 | static int grayscale; /* "matroxfb:grayscale" */ |
1264 | static int dev = -1; /* "matroxfb:dev:xxxxx" */ | |
1265 | static unsigned int vesa = ~0; /* "matroxfb:vesa:xxxxx" */ | |
1266 | static int depth = -1; /* "matroxfb:depth:xxxxx" */ | |
1267 | static unsigned int xres; /* "matroxfb:xres:xxxxx" */ | |
1268 | static unsigned int yres; /* "matroxfb:yres:xxxxx" */ | |
1269 | static unsigned int upper = ~0; /* "matroxfb:upper:xxxxx" */ | |
1270 | static unsigned int lower = ~0; /* "matroxfb:lower:xxxxx" */ | |
1271 | static unsigned int vslen; /* "matroxfb:vslen:xxxxx" */ | |
1272 | static unsigned int left = ~0; /* "matroxfb:left:xxxxx" */ | |
1273 | static unsigned int right = ~0; /* "matroxfb:right:xxxxx" */ | |
1274 | static unsigned int hslen; /* "matroxfb:hslen:xxxxx" */ | |
1275 | static unsigned int pixclock; /* "matroxfb:pixclock:xxxxx" */ | |
1276 | static int sync = -1; /* "matroxfb:sync:xxxxx" */ | |
1277 | static unsigned int fv; /* "matroxfb:fv:xxxxx" */ | |
1278 | static unsigned int fh; /* "matroxfb:fh:xxxxxk" */ | |
1279 | static unsigned int maxclk; /* "matroxfb:maxclk:xxxxM" */ | |
1280 | static int dfp; /* "matroxfb:dfp */ | |
1281 | static int dfp_type = -1; /* "matroxfb:dfp:xxx */ | |
1282 | static int memtype = -1; /* "matroxfb:memtype:xxx" */ | |
1283 | static char outputs[8]; /* "matroxfb:outputs:xxx" */ | |
1da177e4 LT |
1284 | |
1285 | #ifndef MODULE | |
90a48151 | 1286 | static char videomode[64]; /* "matroxfb:mode:xxxxx" or "matroxfb:xxxxx" */ |
1da177e4 LT |
1287 | #endif |
1288 | ||
316b4d64 JD |
1289 | static int matroxfb_getmemory(struct matrox_fb_info *minfo, |
1290 | unsigned int maxSize, unsigned int *realSize) | |
1291 | { | |
1da177e4 LT |
1292 | vaddr_t vm; |
1293 | unsigned int offs; | |
1294 | unsigned int offs2; | |