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