File: /usr/src/linux/drivers/video/matrox/matroxfb_crtc2.c
1 /*
2 *
3 * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450.
4 *
5 * (c) 1998-2001 Petr Vandrovec <vandrove@vc.cvut.cz>
6 *
7 * Version: 1.52 2001/05/25
8 *
9 */
10
11 #include "matroxfb_maven.h"
12 #include "matroxfb_crtc2.h"
13 #include "matroxfb_misc.h"
14 #include "matroxfb_DAC1064.h"
15 #include <linux/matroxfb.h>
16 #include <asm/uaccess.h>
17
18 /* **************************************************** */
19
20 static int mem = 8192;
21
22 MODULE_PARM(mem, "i");
23 MODULE_PARM_DESC(mem, "Memory size reserved for dualhead (default=8MB)");
24
25 /* **************************************************** */
26
27 static int matroxfb_dh_getcolreg(unsigned regno, unsigned *red, unsigned *green,
28 unsigned *blue, unsigned *transp, struct fb_info* info) {
29 #define m2info ((struct matroxfb_dh_fb_info*)info)
30 if (regno > 16)
31 return 1;
32 *red = m2info->palette[regno].red;
33 *blue = m2info->palette[regno].blue;
34 *green = m2info->palette[regno].green;
35 *transp = m2info->palette[regno].transp;
36 return 0;
37 #undef m2info
38 }
39
40 static int matroxfb_dh_setcolreg(unsigned regno, unsigned red, unsigned green,
41 unsigned blue, unsigned transp, struct fb_info* info) {
42 #define m2info ((struct matroxfb_dh_fb_info*)info)
43 struct display* p;
44
45 if (regno > 16)
46 return 1;
47 m2info->palette[regno].red = red;
48 m2info->palette[regno].blue = blue;
49 m2info->palette[regno].green = green;
50 m2info->palette[regno].transp = transp;
51 p = m2info->currcon_display;
52 if (p->var.grayscale) {
53 /* gray = 0.30*R + 0.59*G + 0.11*B */
54 red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
55 }
56 red = CNVT_TOHW(red, p->var.red.length);
57 green = CNVT_TOHW(green, p->var.green.length);
58 blue = CNVT_TOHW(blue, p->var.blue.length);
59 transp = CNVT_TOHW(transp, p->var.transp.length);
60
61 switch (p->var.bits_per_pixel) {
62 #ifdef FBCON_HAS_CFB16
63 case 16:
64 m2info->cmap.cfb16[regno] =
65 (red << p->var.red.offset) |
66 (green << p->var.green.offset) |
67 (blue << p->var.blue.offset) |
68 (transp << p->var.transp.offset);
69 break;
70 #endif
71 #ifdef FBCON_HAS_CFB32
72 case 32:
73 m2info->cmap.cfb32[regno] =
74 (red << p->var.red.offset) |
75 (green << p->var.green.offset) |
76 (blue << p->var.blue.offset) |
77 (transp << p->var.transp.offset);
78 break;
79 #endif
80 }
81 return 0;
82 #undef m2info
83 }
84
85 static void do_install_cmap(struct matroxfb_dh_fb_info* m2info, struct display* p) {
86 if (p->cmap.len)
87 fb_set_cmap(&p->cmap, 1, matroxfb_dh_setcolreg, &m2info->fbcon);
88 else
89 fb_set_cmap(fb_default_cmap(16), 1, matroxfb_dh_setcolreg, &m2info->fbcon);
90 }
91
92 static void matroxfb_dh_restore(struct matroxfb_dh_fb_info* m2info,
93 struct my_timming* mt,
94 struct display* p,
95 int mode,
96 unsigned int pos) {
97 u_int32_t tmp;
98 MINFO_FROM(m2info->primary_dev);
99
100 switch (mode) {
101 case 15:
102 tmp = 0x00200000;
103 break;
104 case 16:
105 tmp = 0x00400000;
106 break;
107 /* case 32: */
108 default:
109 tmp = 0x00800000;
110 break;
111 }
112
113 if (ACCESS_FBINFO(output.sh)) {
114 tmp |= 0x00000001; /* enable CRTC2 */
115
116 if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_SECONDARY) {
117 if (ACCESS_FBINFO(devflags.g450dac)) {
118 tmp |= 0x00000006; /* source from secondary pixel PLL */
119 /* no vidrst */
120 } else {
121 tmp |= 0x00000002; /* source from VDOCLK */
122 tmp |= 0xC0000000; /* enable vvidrst & hvidrst */
123 /* MGA TVO is our clock source */
124 }
125 } else if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_PRIMARY) {
126 tmp |= 0x00000004; /* source from pixclock */
127 /* PIXPLL is our clock source */
128 }
129
130 if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_PRIMARY)
131 tmp |= 0x00100000; /* connect CRTC2 to DAC */
132 }
133 mga_outl(0x3C10, tmp | 0x10000000); /* depth and so on... 0x10000000 is VIDRST polarity */
134 mga_outl(0x3C14, ((mt->HDisplay - 8) << 16) | (mt->HTotal - 8));
135 mga_outl(0x3C18, ((mt->HSyncEnd - 8) << 16) | (mt->HSyncStart - 8));
136 mga_outl(0x3C1C, ((mt->VDisplay - 1) << 16) | (mt->VTotal - 1));
137 mga_outl(0x3C20, ((mt->VSyncEnd - 1) << 16) | (mt->VSyncStart - 1));
138 mga_outl(0x3C24, ((mt->VSyncStart) << 16) | (mt->HSyncStart)); /* preload */
139 mga_outl(0x3C28, pos); /* vmemory start */
140 mga_outl(0x3C40, p->var.xres_virtual * (p->var.bits_per_pixel >> 3));
141 tmp = 0x0FFF0000; /* line compare */
142 if (mt->sync & FB_SYNC_HOR_HIGH_ACT)
143 tmp |= 0x00000100;
144 if (mt->sync & FB_SYNC_VERT_HIGH_ACT)
145 tmp |= 0x00000200;
146 mga_outl(0x3C44, tmp);
147 mga_outl(0x3C4C, 0); /* data control */
148 }
149
150 static void matroxfb_dh_cfbX_init(struct matroxfb_dh_fb_info* m2info,
151 struct display* p) {
152 /* no acceleration for secondary head... */
153 }
154
155 static void matroxfb_dh_pan_var(struct matroxfb_dh_fb_info* m2info,
156 struct fb_var_screeninfo* var) {
157 unsigned int pos;
158
159 #define minfo (m2info->primary_dev)
160 pos = (var->yoffset * var->xres_virtual + var->xoffset) * var->bits_per_pixel >> 3;
161 pos += m2info->video.offbase;
162 mga_outl(0x3C28, pos);
163 #undef minfo
164 }
165
166 static int matroxfb_dh_decode_var(struct matroxfb_dh_fb_info* m2info,
167 struct display* p,
168 struct fb_var_screeninfo* var,
169 int *visual,
170 int *video_cmap_len,
171 int *mode) {
172 unsigned int mask;
173 unsigned int memlen;
174 unsigned int vramlen;
175
176 switch (var->bits_per_pixel) {
177 #ifdef FBCON_HAS_CFB16
178 case 16: mask = 0x1F;
179 break;
180 #endif
181 #ifdef FBCON_HAS_CFB32
182 case 32: mask = 0x0F;
183 break;
184 #endif
185 default: return -EINVAL;
186 }
187 vramlen = m2info->video.len_usable;
188 if (var->yres_virtual < var->yres)
189 var->yres_virtual = var->yres;
190 if (var->xres_virtual < var->xres)
191 var->xres_virtual = var->xres;
192 var->xres_virtual = (var->xres_virtual + mask) & ~mask;
193 if (var->yres_virtual > 32767)
194 return -EINVAL;
195 memlen = var->xres_virtual * var->yres_virtual * (var->bits_per_pixel >> 3);
196 if (memlen > vramlen)
197 return -EINVAL;
198 if (var->xoffset + var->xres > var->xres_virtual)
199 var->xoffset = var->xres_virtual - var->xres;
200 if (var->yoffset + var->yres > var->yres_virtual)
201 var->yoffset = var->yres_virtual - var->yres;
202
203 var->xres &= ~7;
204 var->left_margin &= ~7;
205 var->right_margin &= ~7;
206 var->hsync_len &= ~7;
207
208 *mode = var->bits_per_pixel;
209 if (var->bits_per_pixel == 16) {
210 if (var->green.length == 5) {
211 var->red.offset = 10;
212 var->red.length = 5;
213 var->green.offset = 5;
214 var->green.length = 5;
215 var->blue.offset = 0;
216 var->blue.length = 5;
217 var->transp.offset = 15;
218 var->transp.length = 1;
219 *mode = 15;
220 } else {
221 var->red.offset = 11;
222 var->red.length = 5;
223 var->green.offset = 5;
224 var->green.length = 6;
225 var->blue.offset = 0;
226 var->blue.length = 5;
227 var->transp.offset = 0;
228 var->transp.length = 0;
229 }
230 } else {
231 var->red.offset = 16;
232 var->red.length = 8;
233 var->green.offset = 8;
234 var->green.length = 8;
235 var->blue.offset = 0;
236 var->blue.length = 8;
237 var->transp.offset = 24;
238 var->transp.length = 8;
239 }
240 *visual = FB_VISUAL_TRUECOLOR;
241 *video_cmap_len = 16;
242 return 0;
243 }
244
245 static void initMatroxDH(struct matroxfb_dh_fb_info* m2info, struct display* p) {
246 switch (p->var.bits_per_pixel) {
247 #ifdef FBCON_HAS_CFB16
248 case 16:
249 p->dispsw_data = m2info->cmap.cfb16;
250 p->dispsw = &fbcon_cfb16;
251 break;
252 #endif
253 #ifdef FBCON_HAS_CFB32
254 case 32:
255 p->dispsw_data = m2info->cmap.cfb32;
256 p->dispsw = &fbcon_cfb32;
257 break;
258 #endif
259 default:
260 p->dispsw_data = NULL;
261 p->dispsw = &fbcon_dummy;
262 break;
263 }
264 }
265
266 static int matroxfb_dh_open(struct fb_info* info, int user) {
267 #define m2info ((struct matroxfb_dh_fb_info*)info)
268 MINFO_FROM(m2info->primary_dev);
269
270 if (MINFO) {
271 if (ACCESS_FBINFO(dead)) {
272 return -ENXIO;
273 }
274 }
275 return 0;
276 #undef m2info
277 }
278
279 static int matroxfb_dh_release(struct fb_info* info, int user) {
280 #define m2info ((struct matroxfb_dh_fb_info*)info)
281 MINFO_FROM(m2info->primary_dev);
282
283 if (MINFO) {
284 }
285 return 0;
286 #undef m2info
287 }
288
289 static int matroxfb_dh_get_fix(struct fb_fix_screeninfo* fix, int con,
290 struct fb_info* info) {
291 #define m2info ((struct matroxfb_dh_fb_info*)info)
292 struct display* p;
293
294 if (con >= 0)
295 p = fb_display + con;
296 else
297 p = m2info->fbcon.disp;
298
299 memset(fix, 0, sizeof(*fix));
300 strcpy(fix->id, "MATROX DH");
301
302 fix->smem_start = m2info->video.base;
303 fix->smem_len = m2info->video.len_usable;
304 fix->type = p->type;
305 fix->type_aux = p->type_aux;
306 fix->visual = p->visual;
307 fix->xpanstep = 8; /* TBD */
308 fix->ypanstep = 1;
309 fix->ywrapstep = 0;
310 fix->line_length = p->line_length;
311 fix->mmio_start = m2info->mmio.base;
312 fix->mmio_len = m2info->mmio.len;
313 fix->accel = 0; /* no accel... */
314 return 0;
315 #undef m2info
316 }
317
318 static int matroxfb_dh_get_var(struct fb_var_screeninfo* var, int con,
319 struct fb_info* info) {
320 #define m2info ((struct matroxfb_dh_fb_info*)info)
321 if (con < 0)
322 *var = m2info->fbcon.disp->var;
323 else
324 *var = fb_display[con].var;
325 return 0;
326 #undef m2info
327 }
328
329 static int matroxfb_dh_set_var(struct fb_var_screeninfo* var, int con,
330 struct fb_info* info) {
331 #define m2info ((struct matroxfb_dh_fb_info*)info)
332 struct display* p;
333 int chgvar;
334 int visual;
335 int cmap_len;
336 int mode;
337 int err;
338 MINFO_FROM(m2info->primary_dev);
339
340 if (con < 0)
341 p = m2info->fbcon.disp;
342 else
343 p = fb_display + con;
344 if ((err = matroxfb_dh_decode_var(m2info, p, var, &visual, &cmap_len, &mode)) != 0)
345 return err;
346 switch (var->activate & FB_ACTIVATE_MASK) {
347 case FB_ACTIVATE_TEST: return 0;
348 case FB_ACTIVATE_NXTOPEN:
349 case FB_ACTIVATE_NOW: break;
350 default: return -EINVAL;
351 }
352 if (con >= 0) {
353 chgvar = (p->var.xres != var->xres) ||
354 (p->var.yres != var->yres) ||
355 (p->var.xres_virtual != var->xres_virtual) ||
356 (p->var.yres_virtual != var->yres_virtual) ||
357 (p->var.bits_per_pixel != var->bits_per_pixel) ||
358 memcmp(&p->var.red, &var->red, sizeof(var->red)) ||
359 memcmp(&p->var.green, &var->green, sizeof(var->green)) ||
360 memcmp(&p->var.blue, &var->blue, sizeof(var->blue));
361 } else
362 chgvar = 0;
363 p->var = *var;
364 /* cmap */
365 p->screen_base = vaddr_va(m2info->video.vbase);
366 p->visual = visual;
367 p->ypanstep = 1;
368 p->ywrapstep = 0;
369 p->type = FB_TYPE_PACKED_PIXELS;
370 p->type_aux = 0;
371 p->next_line = p->line_length = (var->xres_virtual * var->bits_per_pixel) >> 3;
372 p->can_soft_blank = 0;
373 p->inverse = 0; /* TBD */
374 initMatroxDH(m2info, p);
375 if (chgvar && info && info->changevar)
376 info->changevar(con);
377 if (con == m2info->currcon) {
378 struct my_timming mt;
379 struct matrox_hw_state* hw;
380 struct matrox_hw_state* ohw;
381 unsigned int pos;
382
383 matroxfb_var2my(var, &mt);
384 /* CRTC2 delay */
385 mt.delay = 34;
386
387 hw = ACCESS_FBINFO(newhw);
388 ohw = ACCESS_FBINFO(currenthw);
389
390 /* copy last setting... */
391 memcpy(hw, ohw, sizeof(*hw));
392
393 pos = (var->yoffset * var->xres_virtual + var->xoffset) * var->bits_per_pixel >> 3;
394 pos += m2info->video.offbase;
395 DAC1064_global_init(PMINFO hw);
396 if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_PRIMARY) {
397 if (ACCESS_FBINFO(primout))
398 ACCESS_FBINFO(primout)->compute(MINFO, &mt, hw);
399 }
400 if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_SECONDARY) {
401 down_read(&ACCESS_FBINFO(altout.lock));
402 if (ACCESS_FBINFO(altout.output))
403 ACCESS_FBINFO(altout.output)->compute(ACCESS_FBINFO(altout.device), &mt, hw);
404 up_read(&ACCESS_FBINFO(altout.lock));
405 }
406 matroxfb_dh_restore(m2info, &mt, p, mode, pos);
407 DAC1064_global_restore(PMINFO hw);
408 if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_PRIMARY) {
409 if (ACCESS_FBINFO(primout))
410 ACCESS_FBINFO(primout)->program(MINFO, hw);
411 }
412 if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_SECONDARY) {
413 down_read(&ACCESS_FBINFO(altout.lock));
414 if (ACCESS_FBINFO(altout.output))
415 ACCESS_FBINFO(altout.output)->program(ACCESS_FBINFO(altout.device), hw);
416 up_read(&ACCESS_FBINFO(altout.lock));
417 }
418 if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_PRIMARY) {
419 if (ACCESS_FBINFO(primout))
420 ACCESS_FBINFO(primout)->start(MINFO);
421 }
422 if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_SECONDARY) {
423 down_read(&ACCESS_FBINFO(altout.lock));
424 if (ACCESS_FBINFO(altout.output))
425 ACCESS_FBINFO(altout.output)->start(ACCESS_FBINFO(altout.device));
426 up_read(&ACCESS_FBINFO(altout.lock));
427 }
428 matroxfb_dh_cfbX_init(m2info, p);
429 do_install_cmap(m2info, p);
430 }
431 return 0;
432 #undef m2info
433 }
434
435 static int matroxfb_dh_get_cmap(struct fb_cmap* cmap, int kspc, int con,
436 struct fb_info* info) {
437 #define m2info ((struct matroxfb_dh_fb_info*)info)
438 struct display* dsp;
439
440 if (con < 0)
441 dsp = m2info->fbcon.disp;
442 else
443 dsp = fb_display + con;
444 if (con == m2info->currcon)
445 return fb_get_cmap(cmap, kspc, matroxfb_dh_getcolreg, info);
446 else if (dsp->cmap.len)
447 fb_copy_cmap(&dsp->cmap, cmap, kspc ? 0 : 2);
448 else
449 fb_copy_cmap(fb_default_cmap(16), cmap, kspc ? 0 : 2);
450 return 0;
451 #undef m2info
452 }
453
454 static int matroxfb_dh_set_cmap(struct fb_cmap* cmap, int kspc, int con,
455 struct fb_info* info) {
456 #define m2info ((struct matroxfb_dh_fb_info*)info)
457 struct display* dsp;
458
459 if (con < 0)
460 dsp = m2info->fbcon.disp;
461 else
462 dsp = fb_display + con;
463 if (dsp->cmap.len != 16) {
464 int err;
465
466 err = fb_alloc_cmap(&dsp->cmap, 16, 0);
467 if (err)
468 return err;
469 }
470 if (con == m2info->currcon)
471 return fb_set_cmap(cmap, kspc, matroxfb_dh_setcolreg, info);
472 else
473 fb_copy_cmap(cmap, &dsp->cmap, kspc ? 0 : 1);
474 return 0;
475 #undef m2info
476 }
477
478 static int matroxfb_dh_pan_display(struct fb_var_screeninfo* var, int con,
479 struct fb_info* info) {
480 #define m2info ((struct matroxfb_dh_fb_info*)info)
481 if (var->xoffset + fb_display[con].var.xres > fb_display[con].var.xres_virtual ||
482 var->yoffset + fb_display[con].var.yres > fb_display[con].var.yres_virtual)
483 return -EINVAL;
484 if (con == m2info->currcon)
485 matroxfb_dh_pan_var(m2info, var);
486 fb_display[con].var.xoffset = var->xoffset;
487 fb_display[con].var.yoffset = var->yoffset;
488 return 0;
489 #undef m2info
490 }
491
492 static int matroxfb_dh_switch(int con, struct fb_info* info);
493
494 static int matroxfb_dh_get_vblank(const struct matroxfb_dh_fb_info* m2info, struct fb_vblank* vblank) {
495 MINFO_FROM(m2info->primary_dev);
496
497 memset(vblank, 0, sizeof(*vblank));
498 vblank->flags = FB_VBLANK_HAVE_VCOUNT | FB_VBLANK_HAVE_VBLANK;
499 /* mask out reserved bits + field number (odd/even) */
500 vblank->vcount = mga_inl(0x3C48) & 0x000007FF;
501 /* compatibility stuff */
502 if (vblank->vcount >= m2info->currcon_display->var.yres)
503 vblank->flags |= FB_VBLANK_VBLANKING;
504 return 0;
505 }
506
507 static int matroxfb_dh_ioctl(struct inode* inode,
508 struct file* file,
509 unsigned int cmd,
510 unsigned long arg,
511 int con,
512 struct fb_info* info) {
513 #define m2info ((struct matroxfb_dh_fb_info*)info)
514 MINFO_FROM(m2info->primary_dev);
515
516 DBG("matroxfb_crtc2_ioctl")
517
518 switch (cmd) {
519 case FBIOGET_VBLANK:
520 {
521 struct fb_vblank vblank;
522 int err;
523
524 err = matroxfb_dh_get_vblank(m2info, &vblank);
525 if (err)
526 return err;
527 if (copy_to_user((struct fb_vblank*)arg, &vblank, sizeof(vblank)))
528 return -EFAULT;
529 return 0;
530 }
531 case MATROXFB_SET_OUTPUT_MODE:
532 case MATROXFB_GET_OUTPUT_MODE:
533 case MATROXFB_GET_ALL_OUTPUTS:
534 {
535 return ACCESS_FBINFO(fbcon.fbops)->fb_ioctl(inode, file, cmd, arg, con, &ACCESS_FBINFO(fbcon));
536 }
537 case MATROXFB_SET_OUTPUT_CONNECTION:
538 {
539 u_int32_t tmp;
540
541 if (get_user(tmp, (u_int32_t*)arg))
542 return -EFAULT;
543 if (tmp & ~ACCESS_FBINFO(output.all))
544 return -EINVAL;
545 if (tmp & ACCESS_FBINFO(output.ph))
546 return -EINVAL;
547 if (tmp & MATROXFB_OUTPUT_CONN_DFP)
548 return -EINVAL;
549 if ((ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_DFP) && tmp)
550 return -EINVAL;
551 if (tmp == ACCESS_FBINFO(output.sh))
552 return 0;
553 ACCESS_FBINFO(output.sh) = tmp;
554 matroxfb_dh_switch(m2info->currcon, info);
555 return 0;
556 }
557 case MATROXFB_GET_OUTPUT_CONNECTION:
558 {
559 if (put_user(ACCESS_FBINFO(output.sh), (u_int32_t*)arg))
560 return -EFAULT;
561 return 0;
562 }
563 case MATROXFB_GET_AVAILABLE_OUTPUTS:
564 {
565 u_int32_t tmp;
566
567 /* we do not support DFP from CRTC2 */
568 tmp = ACCESS_FBINFO(output.all) & ~ACCESS_FBINFO(output.ph) & ~MATROXFB_OUTPUT_CONN_DFP;
569 /* CRTC1 in DFP mode disables CRTC2 at all (I know, I'm lazy) */
570 if (ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_DFP)
571 tmp = 0;
572 if (put_user(tmp, (u_int32_t*)arg))
573 return -EFAULT;
574 return 0;
575 }
576 }
577 return -EINVAL;
578 #undef m2info
579 }
580
581 static struct fb_ops matroxfb_dh_ops = {
582 owner: THIS_MODULE,
583 fb_open: matroxfb_dh_open,
584 fb_release: matroxfb_dh_release,
585 fb_get_fix: matroxfb_dh_get_fix,
586 fb_get_var: matroxfb_dh_get_var,
587 fb_set_var: matroxfb_dh_set_var,
588 fb_get_cmap: matroxfb_dh_get_cmap,
589 fb_set_cmap: matroxfb_dh_set_cmap,
590 fb_pan_display: matroxfb_dh_pan_display,
591 fb_ioctl: matroxfb_dh_ioctl,
592 };
593
594 static int matroxfb_dh_switch(int con, struct fb_info* info) {
595 #define m2info ((struct matroxfb_dh_fb_info*)info)
596 struct fb_cmap* cmap;
597 struct display* p;
598
599 if (m2info->currcon >= 0) {
600 cmap = &m2info->currcon_display->cmap;
601 if (cmap->len) {
602 fb_get_cmap(cmap, 1, matroxfb_dh_getcolreg, info);
603 }
604 }
605 m2info->currcon = con;
606 if (con < 0)
607 p = m2info->fbcon.disp;
608 else
609 p = fb_display + con;
610 m2info->currcon_display = p;
611 p->var.activate = FB_ACTIVATE_NOW;
612 matroxfb_dh_set_var(&p->var, con, info);
613 return 0;
614 #undef m2info
615 }
616
617 static int matroxfb_dh_updatevar(int con, struct fb_info* info) {
618 #define m2info ((struct matroxfb_dh_fb_info*)info)
619 matroxfb_dh_pan_var(m2info, &fb_display[con].var);
620 return 0;
621 #undef m2info
622 }
623
624 static void matroxfb_dh_blank(int blank, struct fb_info* info) {
625 #define m2info ((struct matroxfb_dh_fb_info*)info)
626 switch (blank) {
627 case 1:
628 case 2:
629 case 3:
630 case 4:
631 default:;
632 }
633 /* do something... */
634 #undef m2info
635 }
636
637 static struct fb_var_screeninfo matroxfb_dh_defined = {
638 640,480,640,480,/* W,H, virtual W,H */
639 0,0, /* offset */
640 32, /* depth */
641 0, /* gray */
642 {0,0,0}, /* R */
643 {0,0,0}, /* G */
644 {0,0,0}, /* B */
645 {0,0,0}, /* alpha */
646 0, /* nonstd */
647 FB_ACTIVATE_NOW,
648 -1,-1, /* display size */
649 0, /* accel flags */
650 39721L,48L,16L,33L,10L,
651 96L,2,0, /* no sync info */
652 FB_VMODE_NONINTERLACED,
653 {0,0,0,0,0,0}
654 };
655
656 static int matroxfb_dh_regit(CPMINFO struct matroxfb_dh_fb_info* m2info) {
657 #define minfo (m2info->primary_dev)
658 struct display* d;
659 void* oldcrtc2;
660
661 d = kmalloc(sizeof(*d), GFP_KERNEL);
662 if (!d) {
663 return -ENOMEM;
664 }
665
666 memset(d, 0, sizeof(*d));
667
668 strcpy(m2info->fbcon.modename, "MATROX CRTC2");
669 m2info->fbcon.changevar = NULL;
670 m2info->fbcon.node = -1;
671 m2info->fbcon.fbops = &matroxfb_dh_ops;
672 m2info->fbcon.disp = d;
673 m2info->fbcon.switch_con = &matroxfb_dh_switch;
674 m2info->fbcon.updatevar = &matroxfb_dh_updatevar;
675 m2info->fbcon.blank = &matroxfb_dh_blank;
676 m2info->fbcon.flags = FBINFO_FLAG_DEFAULT;
677 m2info->currcon = -1;
678 m2info->currcon_display = d;
679
680 if (mem < 64)
681 mem *= 1024;
682 if (mem < 64*1024)
683 mem *= 1024;
684 mem &= ~0x00000FFF; /* PAGE_MASK? */
685 if (ACCESS_FBINFO(video.len_usable) + mem <= ACCESS_FBINFO(video.len))
686 m2info->video.offbase = ACCESS_FBINFO(video.len) - mem;
687 else if (ACCESS_FBINFO(video.len) < mem) {
688 kfree(d);
689 return -ENOMEM;
690 } else { /* check yres on first head... */
691 m2info->video.borrowed = mem;
692 ACCESS_FBINFO(video.len_usable) -= mem;
693 m2info->video.offbase = ACCESS_FBINFO(video.len_usable);
694 }
695 m2info->video.base = ACCESS_FBINFO(video.base) + m2info->video.offbase;
696 m2info->video.len = m2info->video.len_usable = m2info->video.len_maximum = mem;
697 m2info->video.vbase.vaddr = vaddr_va(ACCESS_FBINFO(video.vbase)) + m2info->video.offbase;
698 m2info->mmio.base = ACCESS_FBINFO(mmio.base);
699 m2info->mmio.vbase = ACCESS_FBINFO(mmio.vbase);
700 m2info->mmio.len = ACCESS_FBINFO(mmio.len);
701
702 /*
703 * If we have two outputs, connect CRTC2 to it...
704 */
705 if (ACCESS_FBINFO(output.all) & MATROXFB_OUTPUT_CONN_SECONDARY) {
706 ACCESS_FBINFO(output.sh) |= MATROXFB_OUTPUT_CONN_SECONDARY;
707 ACCESS_FBINFO(output.ph) &= ~MATROXFB_OUTPUT_CONN_SECONDARY;
708 if (ACCESS_FBINFO(output.all) & MATROXFB_OUTPUT_CONN_DFP) {
709 ACCESS_FBINFO(output.sh) &= ~MATROXFB_OUTPUT_CONN_DFP;
710 ACCESS_FBINFO(output.ph) &= ~MATROXFB_OUTPUT_CONN_DFP;
711 }
712 }
713
714 matroxfb_dh_set_var(&matroxfb_dh_defined, -2, &m2info->fbcon);
715 if (register_framebuffer(&m2info->fbcon)) {
716 kfree(d);
717 return -ENXIO;
718 }
719 if (m2info->currcon < 0) {
720 matroxfb_dh_set_var(&matroxfb_dh_defined, -1, &m2info->fbcon);
721 }
722 down_write(&ACCESS_FBINFO(crtc2.lock));
723 oldcrtc2 = ACCESS_FBINFO(crtc2.info);
724 ACCESS_FBINFO(crtc2.info) = &m2info->fbcon;
725 up_write(&ACCESS_FBINFO(crtc2.lock));
726 if (oldcrtc2) {
727 printk(KERN_ERR "matroxfb_crtc2: Internal consistency check failed: crtc2 already present: %p\n",
728 oldcrtc2);
729 }
730 return 0;
731 #undef minfo
732 }
733
734 /* ************************** */
735
736 static int matroxfb_dh_registerfb(struct matroxfb_dh_fb_info* m2info) {
737 #define minfo (m2info->primary_dev)
738 if (matroxfb_dh_regit(PMINFO m2info)) {
739 printk(KERN_ERR "matroxfb_crtc2: secondary head failed to register\n");
740 return -1;
741 }
742 printk(KERN_INFO "matroxfb_crtc2: secondary head of fb%u was registered as fb%u\n",
743 GET_FB_IDX(ACCESS_FBINFO(fbcon.node)), GET_FB_IDX(m2info->fbcon.node));
744 m2info->fbcon_registered = 1;
745 return 0;
746 #undef minfo
747 }
748
749 static void matroxfb_dh_deregisterfb(struct matroxfb_dh_fb_info* m2info) {
750 #define minfo (m2info->primary_dev)
751 if (m2info->fbcon_registered) {
752 int id;
753 struct fb_info* crtc2;
754
755 down_write(&ACCESS_FBINFO(crtc2.lock));
756 crtc2 = ACCESS_FBINFO(crtc2.info);
757 if (crtc2 == &m2info->fbcon)
758 ACCESS_FBINFO(crtc2.info) = NULL;
759 up_write(&ACCESS_FBINFO(crtc2.lock));
760 if (crtc2 != &m2info->fbcon) {
761 printk(KERN_ERR "matroxfb_crtc2: Internal consistency check failed: crtc2 mismatch at unload: %p != %p\n",
762 crtc2, &m2info->fbcon);
763 printk(KERN_ERR "matroxfb_crtc2: Expect kernel crash after module unload.\n");
764 return;
765 }
766 id = GET_FB_IDX(m2info->fbcon.node);
767 unregister_framebuffer(&m2info->fbcon);
768 kfree(m2info->fbcon.disp);
769 /* return memory back to primary head */
770 ACCESS_FBINFO(video.len_usable) += m2info->video.borrowed;
771 printk(KERN_INFO "matroxfb_crtc2: fb%u unregistered\n", id);
772 m2info->fbcon_registered = 0;
773 }
774 #undef minfo
775 }
776
777 static void* matroxfb_crtc2_probe(struct matrox_fb_info* minfo) {
778 struct matroxfb_dh_fb_info* m2info;
779
780 /* hardware is CRTC2 incapable... */
781 if (!ACCESS_FBINFO(devflags.crtc2))
782 return NULL;
783 m2info = (struct matroxfb_dh_fb_info*)kmalloc(sizeof(*m2info), GFP_KERNEL);
784 if (!m2info) {
785 printk(KERN_ERR "matroxfb_crtc2: Not enough memory for CRTC2 control structs\n");
786 return NULL;
787 }
788 memset(m2info, 0, sizeof(*m2info));
789 m2info->primary_dev = MINFO;
790 if (matroxfb_dh_registerfb(m2info)) {
791 kfree(m2info);
792 printk(KERN_ERR "matroxfb_crtc2: CRTC2 framebuffer failed to register\n");
793 return NULL;
794 }
795 return m2info;
796 }
797
798 static void matroxfb_crtc2_remove(struct matrox_fb_info* minfo, void* crtc2) {
799 matroxfb_dh_deregisterfb(crtc2);
800 }
801
802 static struct matroxfb_driver crtc2 = {
803 name: "Matrox G400 CRTC2",
804 probe: matroxfb_crtc2_probe,
805 remove: matroxfb_crtc2_remove };
806
807 static int matroxfb_crtc2_init(void) {
808 matroxfb_register_driver(&crtc2);
809 return 0;
810 }
811
812 static void matroxfb_crtc2_exit(void) {
813 matroxfb_unregister_driver(&crtc2);
814 }
815
816 MODULE_AUTHOR("(c) 1999-2001 Petr Vandrovec <vandrove@vc.cvut.cz>");
817 MODULE_DESCRIPTION("Matrox G400 CRTC2 driver");
818 module_init(matroxfb_crtc2_init);
819 module_exit(matroxfb_crtc2_exit);
820 /* we do not have __setup() yet */
821