3 * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450.
5 * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
7 * Portions Copyright (c) 2001 Matrox Graphics Inc.
9 * Version: 1.65 2002/08/14
13 #include "matroxfb_maven.h"
14 #include "matroxfb_crtc2.h"
15 #include "matroxfb_misc.h"
16 #include "matroxfb_DAC1064.h"
17 #include <linux/matroxfb.h>
18 #include <asm/uaccess.h>
20 /* **************************************************** */
22 static int mem
= 8192;
24 module_param(mem
, int, 0);
25 MODULE_PARM_DESC(mem
, "Memory size reserved for dualhead (default=8MB)");
27 /* **************************************************** */
29 static int matroxfb_dh_setcolreg(unsigned regno
, unsigned red
, unsigned green
,
30 unsigned blue
, unsigned transp
, struct fb_info
* info
) {
32 #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
36 if (m2info
->fbcon
.var
.grayscale
) {
37 /* gray = 0.30*R + 0.59*G + 0.11*B */
38 red
= green
= blue
= (red
* 77 + green
* 151 + blue
* 28) >> 8;
40 red
= CNVT_TOHW(red
, m2info
->fbcon
.var
.red
.length
);
41 green
= CNVT_TOHW(green
, m2info
->fbcon
.var
.green
.length
);
42 blue
= CNVT_TOHW(blue
, m2info
->fbcon
.var
.blue
.length
);
43 transp
= CNVT_TOHW(transp
, m2info
->fbcon
.var
.transp
.length
);
45 col
= (red
<< m2info
->fbcon
.var
.red
.offset
) |
46 (green
<< m2info
->fbcon
.var
.green
.offset
) |
47 (blue
<< m2info
->fbcon
.var
.blue
.offset
) |
48 (transp
<< m2info
->fbcon
.var
.transp
.offset
);
50 switch (m2info
->fbcon
.var
.bits_per_pixel
) {
52 m2info
->cmap
[regno
] = col
| (col
<< 16);
55 m2info
->cmap
[regno
] = col
;
62 static void matroxfb_dh_restore(struct matroxfb_dh_fb_info
* m2info
,
63 struct my_timming
* mt
,
68 MINFO_FROM(m2info
->primary_dev
);
82 tmp
|= 0x00000001; /* enable CRTC2 */
84 if (ACCESS_FBINFO(outputs
[1]).src
== MATROXFB_SRC_CRTC2
) {
85 if (ACCESS_FBINFO(devflags
.g450dac
)) {
86 tmp
|= 0x00000006; /* source from secondary pixel PLL */
87 /* no vidrst when in monitor mode */
88 if (ACCESS_FBINFO(outputs
[1]).mode
!= MATROXFB_OUTPUT_MODE_MONITOR
) {
89 tmp
|= 0xC0001000; /* Enable H/V vidrst */
92 tmp
|= 0x00000002; /* source from VDOCLK */
93 tmp
|= 0xC0000000; /* enable vvidrst & hvidrst */
94 /* MGA TVO is our clock source */
96 } else if (ACCESS_FBINFO(outputs
[0]).src
== MATROXFB_SRC_CRTC2
) {
97 tmp
|= 0x00000004; /* source from pixclock */
98 /* PIXPLL is our clock source */
100 if (ACCESS_FBINFO(outputs
[0]).src
== MATROXFB_SRC_CRTC2
) {
101 tmp
|= 0x00100000; /* connect CRTC2 to DAC */
103 if (mt
->interlaced
) {
104 tmp
|= 0x02000000; /* interlaced, second field is bigger, as G450 apparently ignores it */
106 mt
->VSyncStart
>>= 1;
110 if ((mt
->HTotal
& 7) == 2) {
111 datactl
|= 0x00000010;
114 tmp
|= 0x10000000; /* 0x10000000 is VIDRST polarity */
115 mga_outl(0x3C14, ((mt
->HDisplay
- 8) << 16) | (mt
->HTotal
- 8));
116 mga_outl(0x3C18, ((mt
->HSyncEnd
- 8) << 16) | (mt
->HSyncStart
- 8));
117 mga_outl(0x3C1C, ((mt
->VDisplay
- 1) << 16) | (mt
->VTotal
- 1));
118 mga_outl(0x3C20, ((mt
->VSyncEnd
- 1) << 16) | (mt
->VSyncStart
- 1));
119 mga_outl(0x3C24, ((mt
->VSyncStart
) << 16) | (mt
->HSyncStart
)); /* preload */
121 u_int32_t linelen
= m2info
->fbcon
.var
.xres_virtual
* (m2info
->fbcon
.var
.bits_per_pixel
>> 3);
122 if (tmp
& 0x02000000) {
123 /* field #0 is smaller, so... */
124 mga_outl(0x3C2C, pos
); /* field #1 vmemory start */
125 mga_outl(0x3C28, pos
+ linelen
); /* field #0 vmemory start */
127 m2info
->interlaced
= 1;
129 mga_outl(0x3C28, pos
); /* vmemory start */
130 m2info
->interlaced
= 0;
132 mga_outl(0x3C40, linelen
);
134 mga_outl(0x3C4C, datactl
); /* data control */
135 if (tmp
& 0x02000000) {
138 mga_outl(0x3C10, tmp
& ~0x02000000);
139 for (i
= 0; i
< 2; i
++) {
141 unsigned int lastl
= 0;
143 while ((nl
= mga_inl(0x3C48) & 0xFFF) >= lastl
) {
148 mga_outl(0x3C10, tmp
);
149 ACCESS_FBINFO(hw
).crtc2
.ctl
= tmp
;
151 tmp
= mt
->VDisplay
<< 16; /* line compare */
152 if (mt
->sync
& FB_SYNC_HOR_HIGH_ACT
)
154 if (mt
->sync
& FB_SYNC_VERT_HIGH_ACT
)
156 mga_outl(0x3C44, tmp
);
159 static void matroxfb_dh_disable(struct matroxfb_dh_fb_info
* m2info
) {
160 MINFO_FROM(m2info
->primary_dev
);
162 mga_outl(0x3C10, 0x00000004); /* disable CRTC2, CRTC1->DAC1, PLL as clock source */
163 ACCESS_FBINFO(hw
).crtc2
.ctl
= 0x00000004;
166 static void matroxfb_dh_cfbX_init(struct matroxfb_dh_fb_info
* m2info
) {
167 /* no acceleration for secondary head... */
168 m2info
->cmap
[16] = 0xFFFFFFFF;
171 static void matroxfb_dh_pan_var(struct matroxfb_dh_fb_info
* m2info
,
172 struct fb_var_screeninfo
* var
) {
174 unsigned int linelen
;
175 unsigned int pixelsize
;
176 MINFO_FROM(m2info
->primary_dev
);
178 m2info
->fbcon
.var
.xoffset
= var
->xoffset
;
179 m2info
->fbcon
.var
.yoffset
= var
->yoffset
;
180 pixelsize
= m2info
->fbcon
.var
.bits_per_pixel
>> 3;
181 linelen
= m2info
->fbcon
.var
.xres_virtual
* pixelsize
;
182 pos
= m2info
->fbcon
.var
.yoffset
* linelen
+ m2info
->fbcon
.var
.xoffset
* pixelsize
;
183 pos
+= m2info
->video
.offbase
;
184 if (m2info
->interlaced
) {
185 mga_outl(0x3C2C, pos
);
186 mga_outl(0x3C28, pos
+ linelen
);
188 mga_outl(0x3C28, pos
);
192 static int matroxfb_dh_decode_var(struct matroxfb_dh_fb_info
* m2info
,
193 struct fb_var_screeninfo
* var
,
199 unsigned int vramlen
;
201 switch (var
->bits_per_pixel
) {
202 case 16: mask
= 0x1F;
204 case 32: mask
= 0x0F;
206 default: return -EINVAL
;
208 vramlen
= m2info
->video
.len_usable
;
209 if (var
->yres_virtual
< var
->yres
)
210 var
->yres_virtual
= var
->yres
;
211 if (var
->xres_virtual
< var
->xres
)
212 var
->xres_virtual
= var
->xres
;
213 var
->xres_virtual
= (var
->xres_virtual
+ mask
) & ~mask
;
214 if (var
->yres_virtual
> 32767)
216 memlen
= var
->xres_virtual
* var
->yres_virtual
* (var
->bits_per_pixel
>> 3);
217 if (memlen
> vramlen
)
219 if (var
->xoffset
+ var
->xres
> var
->xres_virtual
)
220 var
->xoffset
= var
->xres_virtual
- var
->xres
;
221 if (var
->yoffset
+ var
->yres
> var
->yres_virtual
)
222 var
->yoffset
= var
->yres_virtual
- var
->yres
;
225 var
->left_margin
&= ~7;
226 var
->right_margin
&= ~7;
227 var
->hsync_len
&= ~7;
229 *mode
= var
->bits_per_pixel
;
230 if (var
->bits_per_pixel
== 16) {
231 if (var
->green
.length
== 5) {
232 var
->red
.offset
= 10;
234 var
->green
.offset
= 5;
235 var
->green
.length
= 5;
236 var
->blue
.offset
= 0;
237 var
->blue
.length
= 5;
238 var
->transp
.offset
= 15;
239 var
->transp
.length
= 1;
242 var
->red
.offset
= 11;
244 var
->green
.offset
= 5;
245 var
->green
.length
= 6;
246 var
->blue
.offset
= 0;
247 var
->blue
.length
= 5;
248 var
->transp
.offset
= 0;
249 var
->transp
.length
= 0;
252 var
->red
.offset
= 16;
254 var
->green
.offset
= 8;
255 var
->green
.length
= 8;
256 var
->blue
.offset
= 0;
257 var
->blue
.length
= 8;
258 var
->transp
.offset
= 24;
259 var
->transp
.length
= 8;
261 *visual
= FB_VISUAL_TRUECOLOR
;
262 *video_cmap_len
= 16;
266 static int matroxfb_dh_open(struct fb_info
* info
, int user
) {
267 #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
268 MINFO_FROM(m2info
->primary_dev
);
273 if (ACCESS_FBINFO(dead
)) {
276 err
= ACCESS_FBINFO(fbops
).fb_open(&ACCESS_FBINFO(fbcon
), user
);
285 static int matroxfb_dh_release(struct fb_info
* info
, int user
) {
286 #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
288 MINFO_FROM(m2info
->primary_dev
);
291 err
= ACCESS_FBINFO(fbops
).fb_release(&ACCESS_FBINFO(fbcon
), user
);
297 static void matroxfb_dh_init_fix(struct matroxfb_dh_fb_info
*m2info
) {
298 struct fb_fix_screeninfo
*fix
= &m2info
->fbcon
.fix
;
300 strcpy(fix
->id
, "MATROX DH");
302 fix
->smem_start
= m2info
->video
.base
;
303 fix
->smem_len
= m2info
->video
.len_usable
;
306 fix
->xpanstep
= 8; /* TBD */
307 fix
->mmio_start
= m2info
->mmio
.base
;
308 fix
->mmio_len
= m2info
->mmio
.len
;
309 fix
->accel
= 0; /* no accel... */
312 static int matroxfb_dh_check_var(struct fb_var_screeninfo
* var
, struct fb_info
* info
) {
313 #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
318 return matroxfb_dh_decode_var(m2info
, var
, &visual
, &cmap_len
, &mode
);
322 static int matroxfb_dh_set_par(struct fb_info
* info
) {
323 #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
328 struct fb_var_screeninfo
* var
= &info
->var
;
329 MINFO_FROM(m2info
->primary_dev
);
331 if ((err
= matroxfb_dh_decode_var(m2info
, var
, &visual
, &cmap_len
, &mode
)) != 0)
335 m2info
->fbcon
.screen_base
= vaddr_va(m2info
->video
.vbase
);
336 m2info
->fbcon
.fix
.visual
= visual
;
337 m2info
->fbcon
.fix
.type
= FB_TYPE_PACKED_PIXELS
;
338 m2info
->fbcon
.fix
.type_aux
= 0;
339 m2info
->fbcon
.fix
.line_length
= (var
->xres_virtual
* var
->bits_per_pixel
) >> 3;
342 struct my_timming mt
;
347 matroxfb_var2my(&m2info
->fbcon
.var
, &mt
);
348 mt
.crtc
= MATROXFB_SRC_CRTC2
;
352 pos
= (m2info
->fbcon
.var
.yoffset
* m2info
->fbcon
.var
.xres_virtual
+ m2info
->fbcon
.var
.xoffset
) * m2info
->fbcon
.var
.bits_per_pixel
>> 3;
353 pos
+= m2info
->video
.offbase
;
355 down_read(&ACCESS_FBINFO(altout
).lock
);
356 for (out
= 0; out
< MATROXFB_MAX_OUTPUTS
; out
++) {
357 if (ACCESS_FBINFO(outputs
[out
]).src
== MATROXFB_SRC_CRTC2
) {
359 if (ACCESS_FBINFO(outputs
[out
]).output
->compute
) {
360 ACCESS_FBINFO(outputs
[out
]).output
->compute(ACCESS_FBINFO(outputs
[out
]).data
, &mt
);
364 ACCESS_FBINFO(crtc2
).pixclock
= mt
.pixclock
;
365 ACCESS_FBINFO(crtc2
).mnp
= mt
.mnp
;
366 up_read(&ACCESS_FBINFO(altout
).lock
);
368 matroxfb_dh_restore(m2info
, &mt
, mode
, pos
);
370 matroxfb_dh_disable(m2info
);
372 DAC1064_global_init(PMINFO2
);
373 DAC1064_global_restore(PMINFO2
);
374 down_read(&ACCESS_FBINFO(altout
).lock
);
375 for (out
= 0; out
< MATROXFB_MAX_OUTPUTS
; out
++) {
376 if (ACCESS_FBINFO(outputs
[out
]).src
== MATROXFB_SRC_CRTC2
&&
377 ACCESS_FBINFO(outputs
[out
]).output
->program
) {
378 ACCESS_FBINFO(outputs
[out
]).output
->program(ACCESS_FBINFO(outputs
[out
]).data
);
381 for (out
= 0; out
< MATROXFB_MAX_OUTPUTS
; out
++) {
382 if (ACCESS_FBINFO(outputs
[out
]).src
== MATROXFB_SRC_CRTC2
&&
383 ACCESS_FBINFO(outputs
[out
]).output
->start
) {
384 ACCESS_FBINFO(outputs
[out
]).output
->start(ACCESS_FBINFO(outputs
[out
]).data
);
387 up_read(&ACCESS_FBINFO(altout
).lock
);
388 matroxfb_dh_cfbX_init(m2info
);
390 m2info
->initialized
= 1;
395 static int matroxfb_dh_pan_display(struct fb_var_screeninfo
* var
, struct fb_info
* info
) {
396 #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
397 matroxfb_dh_pan_var(m2info
, var
);
402 static int matroxfb_dh_get_vblank(const struct matroxfb_dh_fb_info
* m2info
, struct fb_vblank
* vblank
) {
403 MINFO_FROM(m2info
->primary_dev
);
405 matroxfb_enable_irq(PMINFO
0);
406 memset(vblank
, 0, sizeof(*vblank
));
407 vblank
->flags
= FB_VBLANK_HAVE_VCOUNT
| FB_VBLANK_HAVE_VBLANK
;
408 /* mask out reserved bits + field number (odd/even) */
409 vblank
->vcount
= mga_inl(0x3C48) & 0x000007FF;
410 /* compatibility stuff */
411 if (vblank
->vcount
>= m2info
->fbcon
.var
.yres
)
412 vblank
->flags
|= FB_VBLANK_VBLANKING
;
413 if (test_bit(0, &ACCESS_FBINFO(irq_flags
))) {
414 vblank
->flags
|= FB_VBLANK_HAVE_COUNT
;
415 /* Only one writer, aligned int value...
416 it should work without lock and without atomic_t */
417 vblank
->count
= ACCESS_FBINFO(crtc2
).vsync
.cnt
;
422 static int matroxfb_dh_ioctl(struct inode
* inode
,
426 struct fb_info
* info
) {
427 #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
428 MINFO_FROM(m2info
->primary_dev
);
435 struct fb_vblank vblank
;
438 err
= matroxfb_dh_get_vblank(m2info
, &vblank
);
441 if (copy_to_user((void __user
*)arg
, &vblank
, sizeof(vblank
)))
445 case FBIO_WAITFORVSYNC
:
449 if (get_user(crt
, (u_int32_t __user
*)arg
))
454 return matroxfb_wait_for_sync(PMINFO
1);
456 case MATROXFB_SET_OUTPUT_MODE
:
457 case MATROXFB_GET_OUTPUT_MODE
:
458 case MATROXFB_GET_ALL_OUTPUTS
:
460 return ACCESS_FBINFO(fbcon
.fbops
)->fb_ioctl(inode
, file
, cmd
, arg
, &ACCESS_FBINFO(fbcon
));
462 case MATROXFB_SET_OUTPUT_CONNECTION
:
468 if (get_user(tmp
, (u_int32_t __user
*)arg
))
470 for (out
= 0; out
< 32; out
++) {
471 if (tmp
& (1 << out
)) {
472 if (out
>= MATROXFB_MAX_OUTPUTS
)
474 if (!ACCESS_FBINFO(outputs
[out
]).output
)
476 switch (ACCESS_FBINFO(outputs
[out
]).src
) {
477 case MATROXFB_SRC_NONE
:
478 case MATROXFB_SRC_CRTC2
:
485 if (ACCESS_FBINFO(devflags
.panellink
)) {
486 if (tmp
& MATROXFB_OUTPUT_CONN_DFP
)
488 if ((ACCESS_FBINFO(outputs
[2]).src
== MATROXFB_SRC_CRTC1
) && tmp
)
492 for (out
= 0; out
< MATROXFB_MAX_OUTPUTS
; out
++) {
493 if (tmp
& (1 << out
)) {
494 if (ACCESS_FBINFO(outputs
[out
]).src
!= MATROXFB_SRC_CRTC2
) {
496 ACCESS_FBINFO(outputs
[out
]).src
= MATROXFB_SRC_CRTC2
;
498 } else if (ACCESS_FBINFO(outputs
[out
]).src
== MATROXFB_SRC_CRTC2
) {
500 ACCESS_FBINFO(outputs
[out
]).src
= MATROXFB_SRC_NONE
;
505 matroxfb_dh_set_par(info
);
508 case MATROXFB_GET_OUTPUT_CONNECTION
:
513 for (out
= 0; out
< MATROXFB_MAX_OUTPUTS
; out
++) {
514 if (ACCESS_FBINFO(outputs
[out
]).src
== MATROXFB_SRC_CRTC2
) {
518 if (put_user(conn
, (u_int32_t __user
*)arg
))
522 case MATROXFB_GET_AVAILABLE_OUTPUTS
:
527 for (out
= 0; out
< MATROXFB_MAX_OUTPUTS
; out
++) {
528 if (ACCESS_FBINFO(outputs
[out
]).output
) {
529 switch (ACCESS_FBINFO(outputs
[out
]).src
) {
530 case MATROXFB_SRC_NONE
:
531 case MATROXFB_SRC_CRTC2
:
537 if (ACCESS_FBINFO(devflags
.panellink
)) {
538 tmp
&= ~MATROXFB_OUTPUT_CONN_DFP
;
539 if (ACCESS_FBINFO(outputs
[2]).src
== MATROXFB_SRC_CRTC1
) {
543 if (put_user(tmp
, (u_int32_t __user
*)arg
))
552 static int matroxfb_dh_blank(int blank
, struct fb_info
* info
) {
553 #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
561 /* do something... */
566 static struct fb_ops matroxfb_dh_ops
= {
567 .owner
= THIS_MODULE
,
568 .fb_open
= matroxfb_dh_open
,
569 .fb_release
= matroxfb_dh_release
,
570 .fb_check_var
= matroxfb_dh_check_var
,
571 .fb_set_par
= matroxfb_dh_set_par
,
572 .fb_setcolreg
= matroxfb_dh_setcolreg
,
573 .fb_pan_display
=matroxfb_dh_pan_display
,
574 .fb_blank
= matroxfb_dh_blank
,
575 .fb_ioctl
= matroxfb_dh_ioctl
,
576 .fb_fillrect
= cfb_fillrect
,
577 .fb_copyarea
= cfb_copyarea
,
578 .fb_imageblit
= cfb_imageblit
,
579 .fb_cursor
= soft_cursor
,
582 static struct fb_var_screeninfo matroxfb_dh_defined
= {
583 640,480,640,480,/* W,H, virtual W,H */
593 -1,-1, /* display size */
595 39721L,48L,16L,33L,10L,
596 96L,2,0, /* no sync info */
597 FB_VMODE_NONINTERLACED
,
601 static int matroxfb_dh_regit(CPMINFO
struct matroxfb_dh_fb_info
* m2info
) {
602 #define minfo (m2info->primary_dev)
605 m2info
->fbcon
.fbops
= &matroxfb_dh_ops
;
606 m2info
->fbcon
.flags
= FBINFO_FLAG_DEFAULT
;
607 m2info
->fbcon
.flags
|= FBINFO_HWACCEL_XPAN
|
609 m2info
->fbcon
.pseudo_palette
= m2info
->cmap
;
610 fb_alloc_cmap(&m2info
->fbcon
.cmap
, 256, 1);
616 mem
&= ~0x00000FFF; /* PAGE_MASK? */
617 if (ACCESS_FBINFO(video
.len_usable
) + mem
<= ACCESS_FBINFO(video
.len
))
618 m2info
->video
.offbase
= ACCESS_FBINFO(video
.len
) - mem
;
619 else if (ACCESS_FBINFO(video
.len
) < mem
) {
621 } else { /* check yres on first head... */
622 m2info
->video
.borrowed
= mem
;
623 ACCESS_FBINFO(video
.len_usable
) -= mem
;
624 m2info
->video
.offbase
= ACCESS_FBINFO(video
.len_usable
);
626 m2info
->video
.base
= ACCESS_FBINFO(video
.base
) + m2info
->video
.offbase
;
627 m2info
->video
.len
= m2info
->video
.len_usable
= m2info
->video
.len_maximum
= mem
;
628 m2info
->video
.vbase
.vaddr
= vaddr_va(ACCESS_FBINFO(video
.vbase
)) + m2info
->video
.offbase
;
629 m2info
->mmio
.base
= ACCESS_FBINFO(mmio
.base
);
630 m2info
->mmio
.vbase
= ACCESS_FBINFO(mmio
.vbase
);
631 m2info
->mmio
.len
= ACCESS_FBINFO(mmio
.len
);
633 matroxfb_dh_init_fix(m2info
);
634 if (register_framebuffer(&m2info
->fbcon
)) {
637 if (!m2info
->initialized
)
638 fb_set_var(&m2info
->fbcon
, &matroxfb_dh_defined
);
639 down_write(&ACCESS_FBINFO(crtc2
.lock
));
640 oldcrtc2
= ACCESS_FBINFO(crtc2
.info
);
641 ACCESS_FBINFO(crtc2
.info
) = m2info
;
642 up_write(&ACCESS_FBINFO(crtc2
.lock
));
644 printk(KERN_ERR
"matroxfb_crtc2: Internal consistency check failed: crtc2 already present: %p\n",
651 /* ************************** */
653 static int matroxfb_dh_registerfb(struct matroxfb_dh_fb_info
* m2info
) {
654 #define minfo (m2info->primary_dev)
655 if (matroxfb_dh_regit(PMINFO m2info
)) {
656 printk(KERN_ERR
"matroxfb_crtc2: secondary head failed to register\n");
659 printk(KERN_INFO
"matroxfb_crtc2: secondary head of fb%u was registered as fb%u\n",
660 ACCESS_FBINFO(fbcon
.node
), m2info
->fbcon
.node
);
661 m2info
->fbcon_registered
= 1;
666 static void matroxfb_dh_deregisterfb(struct matroxfb_dh_fb_info
* m2info
) {
667 #define minfo (m2info->primary_dev)
668 if (m2info
->fbcon_registered
) {
670 struct matroxfb_dh_fb_info
* crtc2
;
672 down_write(&ACCESS_FBINFO(crtc2
.lock
));
673 crtc2
= ACCESS_FBINFO(crtc2
.info
);
675 ACCESS_FBINFO(crtc2
.info
) = NULL
;
676 up_write(&ACCESS_FBINFO(crtc2
.lock
));
677 if (crtc2
!= m2info
) {
678 printk(KERN_ERR
"matroxfb_crtc2: Internal consistency check failed: crtc2 mismatch at unload: %p != %p\n",
680 printk(KERN_ERR
"matroxfb_crtc2: Expect kernel crash after module unload.\n");
683 id
= m2info
->fbcon
.node
;
684 unregister_framebuffer(&m2info
->fbcon
);
685 /* return memory back to primary head */
686 ACCESS_FBINFO(video
.len_usable
) += m2info
->video
.borrowed
;
687 printk(KERN_INFO
"matroxfb_crtc2: fb%u unregistered\n", id
);
688 m2info
->fbcon_registered
= 0;
693 static void* matroxfb_crtc2_probe(struct matrox_fb_info
* minfo
) {
694 struct matroxfb_dh_fb_info
* m2info
;
696 /* hardware is CRTC2 incapable... */
697 if (!ACCESS_FBINFO(devflags
.crtc2
))
699 m2info
= (struct matroxfb_dh_fb_info
*)kmalloc(sizeof(*m2info
), GFP_KERNEL
);
701 printk(KERN_ERR
"matroxfb_crtc2: Not enough memory for CRTC2 control structs\n");
704 memset(m2info
, 0, sizeof(*m2info
));
705 m2info
->primary_dev
= MINFO
;
706 if (matroxfb_dh_registerfb(m2info
)) {
708 printk(KERN_ERR
"matroxfb_crtc2: CRTC2 framebuffer failed to register\n");
714 static void matroxfb_crtc2_remove(struct matrox_fb_info
* minfo
, void* crtc2
) {
715 matroxfb_dh_deregisterfb(crtc2
);
719 static struct matroxfb_driver crtc2
= {
720 .name
= "Matrox G400 CRTC2",
721 .probe
= matroxfb_crtc2_probe
,
722 .remove
= matroxfb_crtc2_remove
};
724 static int matroxfb_crtc2_init(void) {
725 if (fb_get_options("matrox_crtc2fb", NULL
))
728 matroxfb_register_driver(&crtc2
);
732 static void matroxfb_crtc2_exit(void) {
733 matroxfb_unregister_driver(&crtc2
);
736 MODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec <vandrove@vc.cvut.cz>");
737 MODULE_DESCRIPTION("Matrox G400 CRTC2 driver");
738 MODULE_LICENSE("GPL");
739 module_init(matroxfb_crtc2_init
);
740 module_exit(matroxfb_crtc2_exit
);
741 /* we do not have __setup() yet */