xref: /openbmc/linux/drivers/video/fbdev/matrox/matroxfb_crtc2.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f7018c21STomi Valkeinen /*
3f7018c21STomi Valkeinen  *
4f7018c21STomi Valkeinen  * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450.
5f7018c21STomi Valkeinen  *
6f7018c21STomi Valkeinen  * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
7f7018c21STomi Valkeinen  *
8f7018c21STomi Valkeinen  * Portions Copyright (c) 2001 Matrox Graphics Inc.
9f7018c21STomi Valkeinen  *
10f7018c21STomi Valkeinen  * Version: 1.65 2002/08/14
11f7018c21STomi Valkeinen  *
12f7018c21STomi Valkeinen  */
13f7018c21STomi Valkeinen 
14f7018c21STomi Valkeinen #include "matroxfb_maven.h"
15f7018c21STomi Valkeinen #include "matroxfb_crtc2.h"
16f7018c21STomi Valkeinen #include "matroxfb_misc.h"
17f7018c21STomi Valkeinen #include "matroxfb_DAC1064.h"
18f7018c21STomi Valkeinen #include <linux/matroxfb.h>
19f7018c21STomi Valkeinen #include <linux/slab.h>
20f7018c21STomi Valkeinen #include <linux/uaccess.h>
21f7018c21STomi Valkeinen 
22f7018c21STomi Valkeinen /* **************************************************** */
23f7018c21STomi Valkeinen 
24f7018c21STomi Valkeinen static int mem = 8192;
25f7018c21STomi Valkeinen 
26f7018c21STomi Valkeinen module_param(mem, int, 0);
27f7018c21STomi Valkeinen MODULE_PARM_DESC(mem, "Memory size reserved for dualhead (default=8MB)");
28f7018c21STomi Valkeinen 
29f7018c21STomi Valkeinen /* **************************************************** */
30f7018c21STomi Valkeinen 
matroxfb_dh_setcolreg(unsigned regno,unsigned red,unsigned green,unsigned blue,unsigned transp,struct fb_info * info)31f7018c21STomi Valkeinen static int matroxfb_dh_setcolreg(unsigned regno, unsigned red, unsigned green,
32f7018c21STomi Valkeinen 		unsigned blue, unsigned transp, struct fb_info* info) {
33f7018c21STomi Valkeinen 	u_int32_t col;
34f7018c21STomi Valkeinen #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
35f7018c21STomi Valkeinen 
36f7018c21STomi Valkeinen 	if (regno >= 16)
37f7018c21STomi Valkeinen 		return 1;
38f7018c21STomi Valkeinen 	if (m2info->fbcon.var.grayscale) {
39f7018c21STomi Valkeinen 		/* gray = 0.30*R + 0.59*G + 0.11*B */
40f7018c21STomi Valkeinen 		red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
41f7018c21STomi Valkeinen 	}
42f7018c21STomi Valkeinen 	red = CNVT_TOHW(red, m2info->fbcon.var.red.length);
43f7018c21STomi Valkeinen 	green = CNVT_TOHW(green, m2info->fbcon.var.green.length);
44f7018c21STomi Valkeinen 	blue = CNVT_TOHW(blue, m2info->fbcon.var.blue.length);
45f7018c21STomi Valkeinen 	transp = CNVT_TOHW(transp, m2info->fbcon.var.transp.length);
46f7018c21STomi Valkeinen 
47f7018c21STomi Valkeinen 	col = (red << m2info->fbcon.var.red.offset)     |
48f7018c21STomi Valkeinen 	      (green << m2info->fbcon.var.green.offset) |
49f7018c21STomi Valkeinen 	      (blue << m2info->fbcon.var.blue.offset)   |
50f7018c21STomi Valkeinen 	      (transp << m2info->fbcon.var.transp.offset);
51f7018c21STomi Valkeinen 
52f7018c21STomi Valkeinen 	switch (m2info->fbcon.var.bits_per_pixel) {
53f7018c21STomi Valkeinen 		case 16:
54f7018c21STomi Valkeinen 			m2info->cmap[regno] = col | (col << 16);
55f7018c21STomi Valkeinen 			break;
56f7018c21STomi Valkeinen 		case 32:
57f7018c21STomi Valkeinen 			m2info->cmap[regno] = col;
58f7018c21STomi Valkeinen 			break;
59f7018c21STomi Valkeinen 	}
60f7018c21STomi Valkeinen 	return 0;
61f7018c21STomi Valkeinen #undef m2info
62f7018c21STomi Valkeinen }
63f7018c21STomi Valkeinen 
matroxfb_dh_restore(struct matroxfb_dh_fb_info * m2info,struct my_timming * mt,int mode,unsigned int pos)64f7018c21STomi Valkeinen static void matroxfb_dh_restore(struct matroxfb_dh_fb_info* m2info,
65f7018c21STomi Valkeinen 		struct my_timming* mt,
66f7018c21STomi Valkeinen 		int mode,
67f7018c21STomi Valkeinen 		unsigned int pos) {
68f7018c21STomi Valkeinen 	u_int32_t tmp;
69f7018c21STomi Valkeinen 	u_int32_t datactl;
70f7018c21STomi Valkeinen 	struct matrox_fb_info *minfo = m2info->primary_dev;
71f7018c21STomi Valkeinen 
72f7018c21STomi Valkeinen 	switch (mode) {
73f7018c21STomi Valkeinen 		case 15:
74f7018c21STomi Valkeinen 			tmp = 0x00200000;
75f7018c21STomi Valkeinen 			break;
76f7018c21STomi Valkeinen 		case 16:
77f7018c21STomi Valkeinen 			tmp = 0x00400000;
78f7018c21STomi Valkeinen 			break;
79f7018c21STomi Valkeinen /*		case 32: */
80f7018c21STomi Valkeinen 		default:
81f7018c21STomi Valkeinen 			tmp = 0x00800000;
82f7018c21STomi Valkeinen 			break;
83f7018c21STomi Valkeinen 	}
84f7018c21STomi Valkeinen 	tmp |= 0x00000001;	/* enable CRTC2 */
85f7018c21STomi Valkeinen 	datactl = 0;
86f7018c21STomi Valkeinen 	if (minfo->outputs[1].src == MATROXFB_SRC_CRTC2) {
87f7018c21STomi Valkeinen 		if (minfo->devflags.g450dac) {
88f7018c21STomi Valkeinen 			tmp |= 0x00000006; /* source from secondary pixel PLL */
89f7018c21STomi Valkeinen 			/* no vidrst when in monitor mode */
90f7018c21STomi Valkeinen 			if (minfo->outputs[1].mode != MATROXFB_OUTPUT_MODE_MONITOR) {
91f7018c21STomi Valkeinen 				tmp |=  0xC0001000; /* Enable H/V vidrst */
92f7018c21STomi Valkeinen 			}
93f7018c21STomi Valkeinen 		} else {
94f7018c21STomi Valkeinen 			tmp |= 0x00000002; /* source from VDOCLK */
95f7018c21STomi Valkeinen 			tmp |= 0xC0000000; /* enable vvidrst & hvidrst */
96f7018c21STomi Valkeinen 			/* MGA TVO is our clock source */
97f7018c21STomi Valkeinen 		}
98f7018c21STomi Valkeinen 	} else if (minfo->outputs[0].src == MATROXFB_SRC_CRTC2) {
99f7018c21STomi Valkeinen 		tmp |= 0x00000004; /* source from pixclock */
100f7018c21STomi Valkeinen 		/* PIXPLL is our clock source */
101f7018c21STomi Valkeinen 	}
102f7018c21STomi Valkeinen 	if (minfo->outputs[0].src == MATROXFB_SRC_CRTC2) {
103f7018c21STomi Valkeinen 		tmp |= 0x00100000;	/* connect CRTC2 to DAC */
104f7018c21STomi Valkeinen 	}
105f7018c21STomi Valkeinen 	if (mt->interlaced) {
106f7018c21STomi Valkeinen 		tmp |= 0x02000000;	/* interlaced, second field is bigger, as G450 apparently ignores it */
107f7018c21STomi Valkeinen 		mt->VDisplay >>= 1;
108f7018c21STomi Valkeinen 		mt->VSyncStart >>= 1;
109f7018c21STomi Valkeinen 		mt->VSyncEnd >>= 1;
110f7018c21STomi Valkeinen 		mt->VTotal >>= 1;
111f7018c21STomi Valkeinen 	}
112f7018c21STomi Valkeinen 	if ((mt->HTotal & 7) == 2) {
113f7018c21STomi Valkeinen 		datactl |= 0x00000010;
114f7018c21STomi Valkeinen 		mt->HTotal &= ~7;
115f7018c21STomi Valkeinen 	}
116f7018c21STomi Valkeinen 	tmp |= 0x10000000;	/* 0x10000000 is VIDRST polarity */
117f7018c21STomi Valkeinen 	mga_outl(0x3C14, ((mt->HDisplay - 8) << 16) | (mt->HTotal - 8));
118f7018c21STomi Valkeinen 	mga_outl(0x3C18, ((mt->HSyncEnd - 8) << 16) | (mt->HSyncStart - 8));
119f7018c21STomi Valkeinen 	mga_outl(0x3C1C, ((mt->VDisplay - 1) << 16) | (mt->VTotal - 1));
120f7018c21STomi Valkeinen 	mga_outl(0x3C20, ((mt->VSyncEnd - 1) << 16) | (mt->VSyncStart - 1));
121f7018c21STomi Valkeinen 	mga_outl(0x3C24, ((mt->VSyncStart) << 16) | (mt->HSyncStart));	/* preload */
122f7018c21STomi Valkeinen 	{
123f7018c21STomi Valkeinen 		u_int32_t linelen = m2info->fbcon.var.xres_virtual * (m2info->fbcon.var.bits_per_pixel >> 3);
124f7018c21STomi Valkeinen 		if (tmp & 0x02000000) {
125f7018c21STomi Valkeinen 			/* field #0 is smaller, so... */
126f7018c21STomi Valkeinen 			mga_outl(0x3C2C, pos);			/* field #1 vmemory start */
127f7018c21STomi Valkeinen 			mga_outl(0x3C28, pos + linelen);	/* field #0 vmemory start */
128f7018c21STomi Valkeinen 			linelen <<= 1;
129f7018c21STomi Valkeinen 			m2info->interlaced = 1;
130f7018c21STomi Valkeinen 		} else {
131f7018c21STomi Valkeinen 			mga_outl(0x3C28, pos);		/* vmemory start */
132f7018c21STomi Valkeinen 			m2info->interlaced = 0;
133f7018c21STomi Valkeinen 		}
134f7018c21STomi Valkeinen 		mga_outl(0x3C40, linelen);
135f7018c21STomi Valkeinen 	}
136f7018c21STomi Valkeinen 	mga_outl(0x3C4C, datactl);	/* data control */
137f7018c21STomi Valkeinen 	if (tmp & 0x02000000) {
138f7018c21STomi Valkeinen 		int i;
139f7018c21STomi Valkeinen 
140f7018c21STomi Valkeinen 		mga_outl(0x3C10, tmp & ~0x02000000);
141f7018c21STomi Valkeinen 		for (i = 0; i < 2; i++) {
142f7018c21STomi Valkeinen 			unsigned int nl;
143f7018c21STomi Valkeinen 			unsigned int lastl = 0;
144f7018c21STomi Valkeinen 
145f7018c21STomi Valkeinen 			while ((nl = mga_inl(0x3C48) & 0xFFF) >= lastl) {
146f7018c21STomi Valkeinen 				lastl = nl;
147f7018c21STomi Valkeinen 			}
148f7018c21STomi Valkeinen 		}
149f7018c21STomi Valkeinen 	}
150f7018c21STomi Valkeinen 	mga_outl(0x3C10, tmp);
151f7018c21STomi Valkeinen 	minfo->hw.crtc2.ctl = tmp;
152f7018c21STomi Valkeinen 
153f7018c21STomi Valkeinen 	tmp = mt->VDisplay << 16;	/* line compare */
154f7018c21STomi Valkeinen 	if (mt->sync & FB_SYNC_HOR_HIGH_ACT)
155f7018c21STomi Valkeinen 		tmp |= 0x00000100;
156f7018c21STomi Valkeinen 	if (mt->sync & FB_SYNC_VERT_HIGH_ACT)
157f7018c21STomi Valkeinen 		tmp |= 0x00000200;
158f7018c21STomi Valkeinen 	mga_outl(0x3C44, tmp);
159f7018c21STomi Valkeinen }
160f7018c21STomi Valkeinen 
matroxfb_dh_disable(struct matroxfb_dh_fb_info * m2info)161f7018c21STomi Valkeinen static void matroxfb_dh_disable(struct matroxfb_dh_fb_info* m2info) {
162f7018c21STomi Valkeinen 	struct matrox_fb_info *minfo = m2info->primary_dev;
163f7018c21STomi Valkeinen 
164f7018c21STomi Valkeinen 	mga_outl(0x3C10, 0x00000004);	/* disable CRTC2, CRTC1->DAC1, PLL as clock source */
165f7018c21STomi Valkeinen 	minfo->hw.crtc2.ctl = 0x00000004;
166f7018c21STomi Valkeinen }
167f7018c21STomi Valkeinen 
matroxfb_dh_pan_var(struct matroxfb_dh_fb_info * m2info,struct fb_var_screeninfo * var)168f7018c21STomi Valkeinen static void matroxfb_dh_pan_var(struct matroxfb_dh_fb_info* m2info,
169f7018c21STomi Valkeinen 		struct fb_var_screeninfo* var) {
170f7018c21STomi Valkeinen 	unsigned int pos;
171f7018c21STomi Valkeinen 	unsigned int linelen;
172f7018c21STomi Valkeinen 	unsigned int pixelsize;
173f7018c21STomi Valkeinen 	struct matrox_fb_info *minfo = m2info->primary_dev;
174f7018c21STomi Valkeinen 
175f7018c21STomi Valkeinen 	m2info->fbcon.var.xoffset = var->xoffset;
176f7018c21STomi Valkeinen 	m2info->fbcon.var.yoffset = var->yoffset;
177f7018c21STomi Valkeinen 	pixelsize = m2info->fbcon.var.bits_per_pixel >> 3;
178f7018c21STomi Valkeinen 	linelen = m2info->fbcon.var.xres_virtual * pixelsize;
179f7018c21STomi Valkeinen 	pos = m2info->fbcon.var.yoffset * linelen + m2info->fbcon.var.xoffset * pixelsize;
180f7018c21STomi Valkeinen 	pos += m2info->video.offbase;
181f7018c21STomi Valkeinen 	if (m2info->interlaced) {
182f7018c21STomi Valkeinen 		mga_outl(0x3C2C, pos);
183f7018c21STomi Valkeinen 		mga_outl(0x3C28, pos + linelen);
184f7018c21STomi Valkeinen 	} else {
185f7018c21STomi Valkeinen 		mga_outl(0x3C28, pos);
186f7018c21STomi Valkeinen 	}
187f7018c21STomi Valkeinen }
188f7018c21STomi Valkeinen 
matroxfb_dh_decode_var(struct matroxfb_dh_fb_info * m2info,struct fb_var_screeninfo * var,int * visual,int * video_cmap_len,int * mode)189f7018c21STomi Valkeinen static int matroxfb_dh_decode_var(struct matroxfb_dh_fb_info* m2info,
190f7018c21STomi Valkeinen 		struct fb_var_screeninfo* var,
191f7018c21STomi Valkeinen 		int *visual,
192f7018c21STomi Valkeinen 		int *video_cmap_len,
193f7018c21STomi Valkeinen 		int *mode) {
194f7018c21STomi Valkeinen 	unsigned int mask;
195f7018c21STomi Valkeinen 	unsigned int memlen;
196f7018c21STomi Valkeinen 	unsigned int vramlen;
197f7018c21STomi Valkeinen 
198f7018c21STomi Valkeinen 	switch (var->bits_per_pixel) {
199f7018c21STomi Valkeinen 		case 16:	mask = 0x1F;
200f7018c21STomi Valkeinen 				break;
201f7018c21STomi Valkeinen 		case 32:	mask = 0x0F;
202f7018c21STomi Valkeinen 				break;
203f7018c21STomi Valkeinen 		default:	return -EINVAL;
204f7018c21STomi Valkeinen 	}
205f7018c21STomi Valkeinen 	vramlen = m2info->video.len_usable;
206f7018c21STomi Valkeinen 	if (var->yres_virtual < var->yres)
207f7018c21STomi Valkeinen 		var->yres_virtual = var->yres;
208f7018c21STomi Valkeinen 	if (var->xres_virtual < var->xres)
209f7018c21STomi Valkeinen 		var->xres_virtual = var->xres;
210f7018c21STomi Valkeinen 	var->xres_virtual = (var->xres_virtual + mask) & ~mask;
211f7018c21STomi Valkeinen 	if (var->yres_virtual > 32767)
212f7018c21STomi Valkeinen 		return -EINVAL;
213f7018c21STomi Valkeinen 	memlen = var->xres_virtual * var->yres_virtual * (var->bits_per_pixel >> 3);
214f7018c21STomi Valkeinen 	if (memlen > vramlen)
215f7018c21STomi Valkeinen 		return -EINVAL;
216f7018c21STomi Valkeinen 	if (var->xoffset + var->xres > var->xres_virtual)
217f7018c21STomi Valkeinen 		var->xoffset = var->xres_virtual - var->xres;
218f7018c21STomi Valkeinen 	if (var->yoffset + var->yres > var->yres_virtual)
219f7018c21STomi Valkeinen 		var->yoffset = var->yres_virtual - var->yres;
220f7018c21STomi Valkeinen 
221f7018c21STomi Valkeinen 	var->xres &= ~7;
222f7018c21STomi Valkeinen 	var->left_margin &= ~7;
223f7018c21STomi Valkeinen 	var->right_margin &= ~7;
224f7018c21STomi Valkeinen 	var->hsync_len &= ~7;
225f7018c21STomi Valkeinen 
226f7018c21STomi Valkeinen 	*mode = var->bits_per_pixel;
227f7018c21STomi Valkeinen 	if (var->bits_per_pixel == 16) {
228f7018c21STomi Valkeinen 		if (var->green.length == 5) {
229f7018c21STomi Valkeinen 			var->red.offset = 10;
230f7018c21STomi Valkeinen 			var->red.length = 5;
231f7018c21STomi Valkeinen 			var->green.offset = 5;
232f7018c21STomi Valkeinen 			var->green.length = 5;
233f7018c21STomi Valkeinen 			var->blue.offset = 0;
234f7018c21STomi Valkeinen 			var->blue.length = 5;
235f7018c21STomi Valkeinen 			var->transp.offset = 15;
236f7018c21STomi Valkeinen 			var->transp.length = 1;
237f7018c21STomi Valkeinen 			*mode = 15;
238f7018c21STomi Valkeinen 		} else {
239f7018c21STomi Valkeinen 			var->red.offset = 11;
240f7018c21STomi Valkeinen 			var->red.length = 5;
241f7018c21STomi Valkeinen 			var->green.offset = 5;
242f7018c21STomi Valkeinen 			var->green.length = 6;
243f7018c21STomi Valkeinen 			var->blue.offset = 0;
244f7018c21STomi Valkeinen 			var->blue.length = 5;
245f7018c21STomi Valkeinen 			var->transp.offset = 0;
246f7018c21STomi Valkeinen 			var->transp.length = 0;
247f7018c21STomi Valkeinen 		}
248f7018c21STomi Valkeinen 	} else {
249f7018c21STomi Valkeinen 			var->red.offset = 16;
250f7018c21STomi Valkeinen 			var->red.length = 8;
251f7018c21STomi Valkeinen 			var->green.offset = 8;
252f7018c21STomi Valkeinen 			var->green.length = 8;
253f7018c21STomi Valkeinen 			var->blue.offset = 0;
254f7018c21STomi Valkeinen 			var->blue.length = 8;
255f7018c21STomi Valkeinen 			var->transp.offset = 24;
256f7018c21STomi Valkeinen 			var->transp.length = 8;
257f7018c21STomi Valkeinen 	}
258f7018c21STomi Valkeinen 	*visual = FB_VISUAL_TRUECOLOR;
259f7018c21STomi Valkeinen 	*video_cmap_len = 16;
260f7018c21STomi Valkeinen 	return 0;
261f7018c21STomi Valkeinen }
262f7018c21STomi Valkeinen 
matroxfb_dh_open(struct fb_info * info,int user)263f7018c21STomi Valkeinen static int matroxfb_dh_open(struct fb_info* info, int user) {
264f7018c21STomi Valkeinen #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
265f7018c21STomi Valkeinen 	struct matrox_fb_info *minfo = m2info->primary_dev;
266f7018c21STomi Valkeinen 
267f7018c21STomi Valkeinen 	if (minfo) {
268f7018c21STomi Valkeinen 		int err;
269f7018c21STomi Valkeinen 
270f7018c21STomi Valkeinen 		if (minfo->dead) {
271f7018c21STomi Valkeinen 			return -ENXIO;
272f7018c21STomi Valkeinen 		}
273f7018c21STomi Valkeinen 		err = minfo->fbops.fb_open(&minfo->fbcon, user);
274f7018c21STomi Valkeinen 		if (err) {
275f7018c21STomi Valkeinen 			return err;
276f7018c21STomi Valkeinen 		}
277f7018c21STomi Valkeinen 	}
278f7018c21STomi Valkeinen 	return 0;
279f7018c21STomi Valkeinen #undef m2info
280f7018c21STomi Valkeinen }
281f7018c21STomi Valkeinen 
matroxfb_dh_release(struct fb_info * info,int user)282f7018c21STomi Valkeinen static int matroxfb_dh_release(struct fb_info* info, int user) {
283f7018c21STomi Valkeinen #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
284f7018c21STomi Valkeinen 	int err = 0;
285f7018c21STomi Valkeinen 	struct matrox_fb_info *minfo = m2info->primary_dev;
286f7018c21STomi Valkeinen 
287f7018c21STomi Valkeinen 	if (minfo) {
288f7018c21STomi Valkeinen 		err = minfo->fbops.fb_release(&minfo->fbcon, user);
289f7018c21STomi Valkeinen 	}
290f7018c21STomi Valkeinen 	return err;
291f7018c21STomi Valkeinen #undef m2info
292f7018c21STomi Valkeinen }
293f7018c21STomi Valkeinen 
294f7018c21STomi Valkeinen /*
295f7018c21STomi Valkeinen  * This function is called before the register_framebuffer so
296f7018c21STomi Valkeinen  * no locking is needed.
297f7018c21STomi Valkeinen  */
matroxfb_dh_init_fix(struct matroxfb_dh_fb_info * m2info)298f7018c21STomi Valkeinen static void matroxfb_dh_init_fix(struct matroxfb_dh_fb_info *m2info)
299f7018c21STomi Valkeinen {
300f7018c21STomi Valkeinen 	struct fb_fix_screeninfo *fix = &m2info->fbcon.fix;
301f7018c21STomi Valkeinen 
302f7018c21STomi Valkeinen 	strcpy(fix->id, "MATROX DH");
303f7018c21STomi Valkeinen 
304f7018c21STomi Valkeinen 	fix->smem_start = m2info->video.base;
305f7018c21STomi Valkeinen 	fix->smem_len = m2info->video.len_usable;
306f7018c21STomi Valkeinen 	fix->ypanstep = 1;
307f7018c21STomi Valkeinen 	fix->ywrapstep = 0;
308f7018c21STomi Valkeinen 	fix->xpanstep = 8;	/* TBD */
309f7018c21STomi Valkeinen 	fix->mmio_start = m2info->mmio.base;
310f7018c21STomi Valkeinen 	fix->mmio_len = m2info->mmio.len;
311f7018c21STomi Valkeinen 	fix->accel = 0;		/* no accel... */
312f7018c21STomi Valkeinen }
313f7018c21STomi Valkeinen 
matroxfb_dh_check_var(struct fb_var_screeninfo * var,struct fb_info * info)314f7018c21STomi Valkeinen static int matroxfb_dh_check_var(struct fb_var_screeninfo* var, struct fb_info* info) {
315f7018c21STomi Valkeinen #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
316f7018c21STomi Valkeinen 	int visual;
317f7018c21STomi Valkeinen 	int cmap_len;
318f7018c21STomi Valkeinen 	int mode;
319f7018c21STomi Valkeinen 
320f7018c21STomi Valkeinen 	return matroxfb_dh_decode_var(m2info, var, &visual, &cmap_len, &mode);
321f7018c21STomi Valkeinen #undef m2info
322f7018c21STomi Valkeinen }
323f7018c21STomi Valkeinen 
matroxfb_dh_set_par(struct fb_info * info)324f7018c21STomi Valkeinen static int matroxfb_dh_set_par(struct fb_info* info) {
325f7018c21STomi Valkeinen #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
326f7018c21STomi Valkeinen 	int visual;
327f7018c21STomi Valkeinen 	int cmap_len;
328f7018c21STomi Valkeinen 	int mode;
329f7018c21STomi Valkeinen 	int err;
330f7018c21STomi Valkeinen 	struct fb_var_screeninfo* var = &info->var;
331f7018c21STomi Valkeinen 	struct matrox_fb_info *minfo = m2info->primary_dev;
332f7018c21STomi Valkeinen 
333f7018c21STomi Valkeinen 	if ((err = matroxfb_dh_decode_var(m2info, var, &visual, &cmap_len, &mode)) != 0)
334f7018c21STomi Valkeinen 		return err;
335f7018c21STomi Valkeinen 	/* cmap */
336f7018c21STomi Valkeinen 	{
337f7018c21STomi Valkeinen 		m2info->fbcon.screen_base = vaddr_va(m2info->video.vbase);
338f7018c21STomi Valkeinen 		m2info->fbcon.fix.visual = visual;
339f7018c21STomi Valkeinen 		m2info->fbcon.fix.type = FB_TYPE_PACKED_PIXELS;
340f7018c21STomi Valkeinen 		m2info->fbcon.fix.type_aux = 0;
341f7018c21STomi Valkeinen 		m2info->fbcon.fix.line_length = (var->xres_virtual * var->bits_per_pixel) >> 3;
342f7018c21STomi Valkeinen 	}
343f7018c21STomi Valkeinen 	{
344f7018c21STomi Valkeinen 		struct my_timming mt;
345f7018c21STomi Valkeinen 		unsigned int pos;
346f7018c21STomi Valkeinen 		int out;
347f7018c21STomi Valkeinen 		int cnt;
348f7018c21STomi Valkeinen 
349f7018c21STomi Valkeinen 		matroxfb_var2my(&m2info->fbcon.var, &mt);
350f7018c21STomi Valkeinen 		mt.crtc = MATROXFB_SRC_CRTC2;
351f7018c21STomi Valkeinen 		/* CRTC2 delay */
352f7018c21STomi Valkeinen 		mt.delay = 34;
353f7018c21STomi Valkeinen 
354f7018c21STomi Valkeinen 		pos = (m2info->fbcon.var.yoffset * m2info->fbcon.var.xres_virtual + m2info->fbcon.var.xoffset) * m2info->fbcon.var.bits_per_pixel >> 3;
355f7018c21STomi Valkeinen 		pos += m2info->video.offbase;
356f7018c21STomi Valkeinen 		cnt = 0;
357f7018c21STomi Valkeinen 		down_read(&minfo->altout.lock);
358f7018c21STomi Valkeinen 		for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
359f7018c21STomi Valkeinen 			if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2) {
360f7018c21STomi Valkeinen 				cnt++;
361f7018c21STomi Valkeinen 				if (minfo->outputs[out].output->compute) {
362f7018c21STomi Valkeinen 					minfo->outputs[out].output->compute(minfo->outputs[out].data, &mt);
363f7018c21STomi Valkeinen 				}
364f7018c21STomi Valkeinen 			}
365f7018c21STomi Valkeinen 		}
366f7018c21STomi Valkeinen 		minfo->crtc2.pixclock = mt.pixclock;
367f7018c21STomi Valkeinen 		minfo->crtc2.mnp = mt.mnp;
368f7018c21STomi Valkeinen 		up_read(&minfo->altout.lock);
369f7018c21STomi Valkeinen 		if (cnt) {
370f7018c21STomi Valkeinen 			matroxfb_dh_restore(m2info, &mt, mode, pos);
371f7018c21STomi Valkeinen 		} else {
372f7018c21STomi Valkeinen 			matroxfb_dh_disable(m2info);
373f7018c21STomi Valkeinen 		}
374f7018c21STomi Valkeinen 		DAC1064_global_init(minfo);
375f7018c21STomi Valkeinen 		DAC1064_global_restore(minfo);
376f7018c21STomi Valkeinen 		down_read(&minfo->altout.lock);
377f7018c21STomi Valkeinen 		for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
378f7018c21STomi Valkeinen 			if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2 &&
379f7018c21STomi Valkeinen 			    minfo->outputs[out].output->program) {
380f7018c21STomi Valkeinen 				minfo->outputs[out].output->program(minfo->outputs[out].data);
381f7018c21STomi Valkeinen 			}
382f7018c21STomi Valkeinen 		}
383f7018c21STomi Valkeinen 		for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
384f7018c21STomi Valkeinen 			if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2 &&
385f7018c21STomi Valkeinen 			    minfo->outputs[out].output->start) {
386f7018c21STomi Valkeinen 				minfo->outputs[out].output->start(minfo->outputs[out].data);
387f7018c21STomi Valkeinen 			}
388f7018c21STomi Valkeinen 		}
389f7018c21STomi Valkeinen 		up_read(&minfo->altout.lock);
390f7018c21STomi Valkeinen 	}
391f7018c21STomi Valkeinen 	m2info->initialized = 1;
392f7018c21STomi Valkeinen 	return 0;
393f7018c21STomi Valkeinen #undef m2info
394f7018c21STomi Valkeinen }
395f7018c21STomi Valkeinen 
matroxfb_dh_pan_display(struct fb_var_screeninfo * var,struct fb_info * info)396f7018c21STomi Valkeinen static int matroxfb_dh_pan_display(struct fb_var_screeninfo* var, struct fb_info* info) {
397f7018c21STomi Valkeinen #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
398f7018c21STomi Valkeinen 	matroxfb_dh_pan_var(m2info, var);
399f7018c21STomi Valkeinen 	return 0;
400f7018c21STomi Valkeinen #undef m2info
401f7018c21STomi Valkeinen }
402f7018c21STomi Valkeinen 
matroxfb_dh_get_vblank(const struct matroxfb_dh_fb_info * m2info,struct fb_vblank * vblank)403f7018c21STomi Valkeinen static int matroxfb_dh_get_vblank(const struct matroxfb_dh_fb_info* m2info, struct fb_vblank* vblank) {
404f7018c21STomi Valkeinen 	struct matrox_fb_info *minfo = m2info->primary_dev;
405f7018c21STomi Valkeinen 
406f7018c21STomi Valkeinen 	matroxfb_enable_irq(minfo, 0);
407f7018c21STomi Valkeinen 	memset(vblank, 0, sizeof(*vblank));
408f7018c21STomi Valkeinen 	vblank->flags = FB_VBLANK_HAVE_VCOUNT | FB_VBLANK_HAVE_VBLANK;
409f7018c21STomi Valkeinen 	/* mask out reserved bits + field number (odd/even) */
410f7018c21STomi Valkeinen 	vblank->vcount = mga_inl(0x3C48) & 0x000007FF;
411f7018c21STomi Valkeinen 	/* compatibility stuff */
412f7018c21STomi Valkeinen 	if (vblank->vcount >= m2info->fbcon.var.yres)
413f7018c21STomi Valkeinen 		vblank->flags |= FB_VBLANK_VBLANKING;
414f7018c21STomi Valkeinen 	if (test_bit(0, &minfo->irq_flags)) {
415f7018c21STomi Valkeinen                 vblank->flags |= FB_VBLANK_HAVE_COUNT;
416f7018c21STomi Valkeinen                 /* Only one writer, aligned int value...
417f7018c21STomi Valkeinen                    it should work without lock and without atomic_t */
418f7018c21STomi Valkeinen 		vblank->count = minfo->crtc2.vsync.cnt;
419f7018c21STomi Valkeinen         }
420f7018c21STomi Valkeinen 	return 0;
421f7018c21STomi Valkeinen }
422f7018c21STomi Valkeinen 
matroxfb_dh_ioctl(struct fb_info * info,unsigned int cmd,unsigned long arg)423f7018c21STomi Valkeinen static int matroxfb_dh_ioctl(struct fb_info *info,
424f7018c21STomi Valkeinen 		unsigned int cmd,
425f7018c21STomi Valkeinen 		unsigned long arg)
426f7018c21STomi Valkeinen {
427f7018c21STomi Valkeinen #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
428f7018c21STomi Valkeinen 	struct matrox_fb_info *minfo = m2info->primary_dev;
429f7018c21STomi Valkeinen 
430f7018c21STomi Valkeinen 	DBG(__func__)
431f7018c21STomi Valkeinen 
432f7018c21STomi Valkeinen 	switch (cmd) {
433f7018c21STomi Valkeinen 		case FBIOGET_VBLANK:
434f7018c21STomi Valkeinen 			{
435f7018c21STomi Valkeinen 				struct fb_vblank vblank;
436f7018c21STomi Valkeinen 				int err;
437f7018c21STomi Valkeinen 
438f7018c21STomi Valkeinen 				err = matroxfb_dh_get_vblank(m2info, &vblank);
439f7018c21STomi Valkeinen 				if (err)
440f7018c21STomi Valkeinen 					return err;
441f7018c21STomi Valkeinen 				if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank)))
442f7018c21STomi Valkeinen 					return -EFAULT;
443f7018c21STomi Valkeinen 				return 0;
444f7018c21STomi Valkeinen 			}
445f7018c21STomi Valkeinen 		case FBIO_WAITFORVSYNC:
446f7018c21STomi Valkeinen 			{
447f7018c21STomi Valkeinen 				u_int32_t crt;
448f7018c21STomi Valkeinen 
449f7018c21STomi Valkeinen 				if (get_user(crt, (u_int32_t __user *)arg))
450f7018c21STomi Valkeinen 					return -EFAULT;
451f7018c21STomi Valkeinen 
452f7018c21STomi Valkeinen 				if (crt != 0)
453f7018c21STomi Valkeinen 					return -ENODEV;
454f7018c21STomi Valkeinen 				return matroxfb_wait_for_sync(minfo, 1);
455f7018c21STomi Valkeinen 			}
456f7018c21STomi Valkeinen 		case MATROXFB_SET_OUTPUT_MODE:
457f7018c21STomi Valkeinen 		case MATROXFB_GET_OUTPUT_MODE:
458f7018c21STomi Valkeinen 		case MATROXFB_GET_ALL_OUTPUTS:
459f7018c21STomi Valkeinen 			{
460f7018c21STomi Valkeinen 				return minfo->fbcon.fbops->fb_ioctl(&minfo->fbcon, cmd, arg);
461f7018c21STomi Valkeinen 			}
462f7018c21STomi Valkeinen 		case MATROXFB_SET_OUTPUT_CONNECTION:
463f7018c21STomi Valkeinen 			{
464f7018c21STomi Valkeinen 				u_int32_t tmp;
465f7018c21STomi Valkeinen 				int out;
466f7018c21STomi Valkeinen 				int changes;
467f7018c21STomi Valkeinen 
468f7018c21STomi Valkeinen 				if (get_user(tmp, (u_int32_t __user *)arg))
469f7018c21STomi Valkeinen 					return -EFAULT;
470f7018c21STomi Valkeinen 				for (out = 0; out < 32; out++) {
471f7018c21STomi Valkeinen 					if (tmp & (1 << out)) {
472f7018c21STomi Valkeinen 						if (out >= MATROXFB_MAX_OUTPUTS)
473f7018c21STomi Valkeinen 							return -ENXIO;
474f7018c21STomi Valkeinen 						if (!minfo->outputs[out].output)
475f7018c21STomi Valkeinen 							return -ENXIO;
476f7018c21STomi Valkeinen 						switch (minfo->outputs[out].src) {
477f7018c21STomi Valkeinen 							case MATROXFB_SRC_NONE:
478f7018c21STomi Valkeinen 							case MATROXFB_SRC_CRTC2:
479f7018c21STomi Valkeinen 								break;
480f7018c21STomi Valkeinen 							default:
481f7018c21STomi Valkeinen 								return -EBUSY;
482f7018c21STomi Valkeinen 						}
483f7018c21STomi Valkeinen 					}
484f7018c21STomi Valkeinen 				}
485f7018c21STomi Valkeinen 				if (minfo->devflags.panellink) {
486f7018c21STomi Valkeinen 					if (tmp & MATROXFB_OUTPUT_CONN_DFP)
487f7018c21STomi Valkeinen 						return -EINVAL;
488f7018c21STomi Valkeinen 					if ((minfo->outputs[2].src == MATROXFB_SRC_CRTC1) && tmp)
489f7018c21STomi Valkeinen 						return -EBUSY;
490f7018c21STomi Valkeinen 				}
491f7018c21STomi Valkeinen 				changes = 0;
492f7018c21STomi Valkeinen 				for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
493f7018c21STomi Valkeinen 					if (tmp & (1 << out)) {
494f7018c21STomi Valkeinen 						if (minfo->outputs[out].src != MATROXFB_SRC_CRTC2) {
495f7018c21STomi Valkeinen 							changes = 1;
496f7018c21STomi Valkeinen 							minfo->outputs[out].src = MATROXFB_SRC_CRTC2;
497f7018c21STomi Valkeinen 						}
498f7018c21STomi Valkeinen 					} else if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2) {
499f7018c21STomi Valkeinen 						changes = 1;
500f7018c21STomi Valkeinen 						minfo->outputs[out].src = MATROXFB_SRC_NONE;
501f7018c21STomi Valkeinen 					}
502f7018c21STomi Valkeinen 				}
503f7018c21STomi Valkeinen 				if (!changes)
504f7018c21STomi Valkeinen 					return 0;
505f7018c21STomi Valkeinen 				matroxfb_dh_set_par(info);
506f7018c21STomi Valkeinen 				return 0;
507f7018c21STomi Valkeinen 			}
508f7018c21STomi Valkeinen 		case MATROXFB_GET_OUTPUT_CONNECTION:
509f7018c21STomi Valkeinen 			{
510f7018c21STomi Valkeinen 				u_int32_t conn = 0;
511f7018c21STomi Valkeinen 				int out;
512f7018c21STomi Valkeinen 
513f7018c21STomi Valkeinen 				for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
514f7018c21STomi Valkeinen 					if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2) {
515f7018c21STomi Valkeinen 						conn |= 1 << out;
516f7018c21STomi Valkeinen 					}
517f7018c21STomi Valkeinen 				}
518f7018c21STomi Valkeinen 				if (put_user(conn, (u_int32_t __user *)arg))
519f7018c21STomi Valkeinen 					return -EFAULT;
520f7018c21STomi Valkeinen 				return 0;
521f7018c21STomi Valkeinen 			}
522f7018c21STomi Valkeinen 		case MATROXFB_GET_AVAILABLE_OUTPUTS:
523f7018c21STomi Valkeinen 			{
524f7018c21STomi Valkeinen 				u_int32_t tmp = 0;
525f7018c21STomi Valkeinen 				int out;
526f7018c21STomi Valkeinen 
527f7018c21STomi Valkeinen 				for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
528f7018c21STomi Valkeinen 					if (minfo->outputs[out].output) {
529f7018c21STomi Valkeinen 						switch (minfo->outputs[out].src) {
530f7018c21STomi Valkeinen 							case MATROXFB_SRC_NONE:
531f7018c21STomi Valkeinen 							case MATROXFB_SRC_CRTC2:
532f7018c21STomi Valkeinen 								tmp |= 1 << out;
533f7018c21STomi Valkeinen 								break;
534f7018c21STomi Valkeinen 						}
535f7018c21STomi Valkeinen 					}
536f7018c21STomi Valkeinen 				}
537f7018c21STomi Valkeinen 				if (minfo->devflags.panellink) {
538f7018c21STomi Valkeinen 					tmp &= ~MATROXFB_OUTPUT_CONN_DFP;
539f7018c21STomi Valkeinen 					if (minfo->outputs[2].src == MATROXFB_SRC_CRTC1) {
540f7018c21STomi Valkeinen 						tmp = 0;
541f7018c21STomi Valkeinen 					}
542f7018c21STomi Valkeinen 				}
543f7018c21STomi Valkeinen 				if (put_user(tmp, (u_int32_t __user *)arg))
544f7018c21STomi Valkeinen 					return -EFAULT;
545f7018c21STomi Valkeinen 				return 0;
546f7018c21STomi Valkeinen 			}
547f7018c21STomi Valkeinen 	}
548f7018c21STomi Valkeinen 	return -ENOTTY;
549f7018c21STomi Valkeinen #undef m2info
550f7018c21STomi Valkeinen }
551f7018c21STomi Valkeinen 
matroxfb_dh_blank(int blank,struct fb_info * info)552f7018c21STomi Valkeinen static int matroxfb_dh_blank(int blank, struct fb_info* info) {
553f7018c21STomi Valkeinen #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
554f7018c21STomi Valkeinen 	switch (blank) {
555f7018c21STomi Valkeinen 		case 1:
556f7018c21STomi Valkeinen 		case 2:
557f7018c21STomi Valkeinen 		case 3:
558f7018c21STomi Valkeinen 		case 4:
559f7018c21STomi Valkeinen 		default:;
560f7018c21STomi Valkeinen 	}
561f7018c21STomi Valkeinen 	/* do something... */
562f7018c21STomi Valkeinen 	return 0;
563f7018c21STomi Valkeinen #undef m2info
564f7018c21STomi Valkeinen }
565f7018c21STomi Valkeinen 
5668a48ac33SJani Nikula static const struct fb_ops matroxfb_dh_ops = {
567f7018c21STomi Valkeinen 	.owner =	THIS_MODULE,
568f7018c21STomi Valkeinen 	.fb_open =	matroxfb_dh_open,
569f7018c21STomi Valkeinen 	.fb_release =	matroxfb_dh_release,
570f7018c21STomi Valkeinen 	.fb_check_var =	matroxfb_dh_check_var,
571f7018c21STomi Valkeinen 	.fb_set_par =	matroxfb_dh_set_par,
572f7018c21STomi Valkeinen 	.fb_setcolreg =	matroxfb_dh_setcolreg,
573f7018c21STomi Valkeinen 	.fb_pan_display =matroxfb_dh_pan_display,
574f7018c21STomi Valkeinen 	.fb_blank =	matroxfb_dh_blank,
575f7018c21STomi Valkeinen 	.fb_ioctl =	matroxfb_dh_ioctl,
576f7018c21STomi Valkeinen 	.fb_fillrect =	cfb_fillrect,
577f7018c21STomi Valkeinen 	.fb_copyarea =	cfb_copyarea,
578f7018c21STomi Valkeinen 	.fb_imageblit =	cfb_imageblit,
579f7018c21STomi Valkeinen };
580f7018c21STomi Valkeinen 
581f7018c21STomi Valkeinen static struct fb_var_screeninfo matroxfb_dh_defined = {
582f7018c21STomi Valkeinen 		640,480,640,480,/* W,H, virtual W,H */
583f7018c21STomi Valkeinen 		0,0,		/* offset */
584f7018c21STomi Valkeinen 		32,		/* depth */
585f7018c21STomi Valkeinen 		0,		/* gray */
586f7018c21STomi Valkeinen 		{0,0,0},	/* R */
587f7018c21STomi Valkeinen 		{0,0,0},	/* G */
588f7018c21STomi Valkeinen 		{0,0,0},	/* B */
589f7018c21STomi Valkeinen 		{0,0,0},	/* alpha */
590f7018c21STomi Valkeinen 		0,		/* nonstd */
591f7018c21STomi Valkeinen 		FB_ACTIVATE_NOW,
592f7018c21STomi Valkeinen 		-1,-1,		/* display size */
593f7018c21STomi Valkeinen 		0,		/* accel flags */
594f7018c21STomi Valkeinen 		39721L,48L,16L,33L,10L,
595f7018c21STomi Valkeinen 		96L,2,0,	/* no sync info */
596f7018c21STomi Valkeinen 		FB_VMODE_NONINTERLACED,
597f7018c21STomi Valkeinen };
598f7018c21STomi Valkeinen 
matroxfb_dh_regit(const struct matrox_fb_info * minfo,struct matroxfb_dh_fb_info * m2info)599f7018c21STomi Valkeinen static int matroxfb_dh_regit(const struct matrox_fb_info *minfo,
600f7018c21STomi Valkeinen 			     struct matroxfb_dh_fb_info *m2info)
601f7018c21STomi Valkeinen {
602f7018c21STomi Valkeinen #define minfo (m2info->primary_dev)
603f7018c21STomi Valkeinen 	void* oldcrtc2;
604f7018c21STomi Valkeinen 
605f7018c21STomi Valkeinen 	m2info->fbcon.fbops = &matroxfb_dh_ops;
606*252b7b14SThomas Zimmermann 	m2info->fbcon.flags = FBINFO_HWACCEL_XPAN |
607f7018c21STomi Valkeinen 			      FBINFO_HWACCEL_YPAN;
608f7018c21STomi Valkeinen 	m2info->fbcon.pseudo_palette = m2info->cmap;
609f7018c21STomi Valkeinen 	fb_alloc_cmap(&m2info->fbcon.cmap, 256, 1);
610f7018c21STomi Valkeinen 
611f7018c21STomi Valkeinen 	if (mem < 64)
612f7018c21STomi Valkeinen 		mem *= 1024;
613f7018c21STomi Valkeinen 	if (mem < 64*1024)
614f7018c21STomi Valkeinen 		mem *= 1024;
615f7018c21STomi Valkeinen 	mem &= ~0x00000FFF;	/* PAGE_MASK? */
616f7018c21STomi Valkeinen 	if (minfo->video.len_usable + mem <= minfo->video.len)
617f7018c21STomi Valkeinen 		m2info->video.offbase = minfo->video.len - mem;
618f7018c21STomi Valkeinen 	else if (minfo->video.len < mem) {
619f7018c21STomi Valkeinen 		return -ENOMEM;
620f7018c21STomi Valkeinen 	} else { /* check yres on first head... */
621f7018c21STomi Valkeinen 		m2info->video.borrowed = mem;
622f7018c21STomi Valkeinen 		minfo->video.len_usable -= mem;
623f7018c21STomi Valkeinen 		m2info->video.offbase = minfo->video.len_usable;
624f7018c21STomi Valkeinen 	}
625f7018c21STomi Valkeinen 	m2info->video.base = minfo->video.base + m2info->video.offbase;
626f7018c21STomi Valkeinen 	m2info->video.len = m2info->video.len_usable = m2info->video.len_maximum = mem;
627f7018c21STomi Valkeinen 	m2info->video.vbase.vaddr = vaddr_va(minfo->video.vbase) + m2info->video.offbase;
628f7018c21STomi Valkeinen 	m2info->mmio.base = minfo->mmio.base;
629f7018c21STomi Valkeinen 	m2info->mmio.vbase = minfo->mmio.vbase;
630f7018c21STomi Valkeinen 	m2info->mmio.len = minfo->mmio.len;
631f7018c21STomi Valkeinen 
632f7018c21STomi Valkeinen 	matroxfb_dh_init_fix(m2info);
633f7018c21STomi Valkeinen 	if (register_framebuffer(&m2info->fbcon)) {
634f7018c21STomi Valkeinen 		return -ENXIO;
635f7018c21STomi Valkeinen 	}
636f7018c21STomi Valkeinen 	if (!m2info->initialized)
637f7018c21STomi Valkeinen 		fb_set_var(&m2info->fbcon, &matroxfb_dh_defined);
638f7018c21STomi Valkeinen 	down_write(&minfo->crtc2.lock);
639f7018c21STomi Valkeinen 	oldcrtc2 = minfo->crtc2.info;
640f7018c21STomi Valkeinen 	minfo->crtc2.info = m2info;
641f7018c21STomi Valkeinen 	up_write(&minfo->crtc2.lock);
642f7018c21STomi Valkeinen 	if (oldcrtc2) {
643f7018c21STomi Valkeinen 		printk(KERN_ERR "matroxfb_crtc2: Internal consistency check failed: crtc2 already present: %p\n",
644f7018c21STomi Valkeinen 			oldcrtc2);
645f7018c21STomi Valkeinen 	}
646f7018c21STomi Valkeinen 	return 0;
647f7018c21STomi Valkeinen #undef minfo
648f7018c21STomi Valkeinen }
649f7018c21STomi Valkeinen 
650f7018c21STomi Valkeinen /* ************************** */
651f7018c21STomi Valkeinen 
matroxfb_dh_registerfb(struct matroxfb_dh_fb_info * m2info)652f7018c21STomi Valkeinen static int matroxfb_dh_registerfb(struct matroxfb_dh_fb_info* m2info) {
653f7018c21STomi Valkeinen #define minfo (m2info->primary_dev)
654f7018c21STomi Valkeinen 	if (matroxfb_dh_regit(minfo, m2info)) {
655f7018c21STomi Valkeinen 		printk(KERN_ERR "matroxfb_crtc2: secondary head failed to register\n");
656f7018c21STomi Valkeinen 		return -1;
657f7018c21STomi Valkeinen 	}
658f7018c21STomi Valkeinen 	printk(KERN_INFO "matroxfb_crtc2: secondary head of fb%u was registered as fb%u\n",
659f7018c21STomi Valkeinen 		minfo->fbcon.node, m2info->fbcon.node);
660f7018c21STomi Valkeinen 	m2info->fbcon_registered = 1;
661f7018c21STomi Valkeinen 	return 0;
662f7018c21STomi Valkeinen #undef minfo
663f7018c21STomi Valkeinen }
664f7018c21STomi Valkeinen 
matroxfb_dh_deregisterfb(struct matroxfb_dh_fb_info * m2info)665f7018c21STomi Valkeinen static void matroxfb_dh_deregisterfb(struct matroxfb_dh_fb_info* m2info) {
666f7018c21STomi Valkeinen #define minfo (m2info->primary_dev)
667f7018c21STomi Valkeinen 	if (m2info->fbcon_registered) {
668f7018c21STomi Valkeinen 		int id;
669f7018c21STomi Valkeinen 		struct matroxfb_dh_fb_info* crtc2;
670f7018c21STomi Valkeinen 
671f7018c21STomi Valkeinen 		down_write(&minfo->crtc2.lock);
672f7018c21STomi Valkeinen 		crtc2 = minfo->crtc2.info;
673f7018c21STomi Valkeinen 		if (crtc2 == m2info)
674f7018c21STomi Valkeinen 			minfo->crtc2.info = NULL;
675f7018c21STomi Valkeinen 		up_write(&minfo->crtc2.lock);
676f7018c21STomi Valkeinen 		if (crtc2 != m2info) {
677f7018c21STomi Valkeinen 			printk(KERN_ERR "matroxfb_crtc2: Internal consistency check failed: crtc2 mismatch at unload: %p != %p\n",
678f7018c21STomi Valkeinen 				crtc2, m2info);
679f7018c21STomi Valkeinen 			printk(KERN_ERR "matroxfb_crtc2: Expect kernel crash after module unload.\n");
680f7018c21STomi Valkeinen 			return;
681f7018c21STomi Valkeinen 		}
682f7018c21STomi Valkeinen 		id = m2info->fbcon.node;
683f7018c21STomi Valkeinen 		unregister_framebuffer(&m2info->fbcon);
684f7018c21STomi Valkeinen 		/* return memory back to primary head */
685f7018c21STomi Valkeinen 		minfo->video.len_usable += m2info->video.borrowed;
686f7018c21STomi Valkeinen 		printk(KERN_INFO "matroxfb_crtc2: fb%u unregistered\n", id);
687f7018c21STomi Valkeinen 		m2info->fbcon_registered = 0;
688f7018c21STomi Valkeinen 	}
689f7018c21STomi Valkeinen #undef minfo
690f7018c21STomi Valkeinen }
691f7018c21STomi Valkeinen 
matroxfb_crtc2_probe(struct matrox_fb_info * minfo)692f7018c21STomi Valkeinen static void* matroxfb_crtc2_probe(struct matrox_fb_info* minfo) {
693f7018c21STomi Valkeinen 	struct matroxfb_dh_fb_info* m2info;
694f7018c21STomi Valkeinen 
695f7018c21STomi Valkeinen 	/* hardware is CRTC2 incapable... */
696f7018c21STomi Valkeinen 	if (!minfo->devflags.crtc2)
697f7018c21STomi Valkeinen 		return NULL;
698f7018c21STomi Valkeinen 	m2info = kzalloc(sizeof(*m2info), GFP_KERNEL);
6992b797d33SMarkus Elfring 	if (!m2info)
700f7018c21STomi Valkeinen 		return NULL;
7012b797d33SMarkus Elfring 
702f7018c21STomi Valkeinen 	m2info->primary_dev = minfo;
703f7018c21STomi Valkeinen 	if (matroxfb_dh_registerfb(m2info)) {
704f7018c21STomi Valkeinen 		kfree(m2info);
705f7018c21STomi Valkeinen 		printk(KERN_ERR "matroxfb_crtc2: CRTC2 framebuffer failed to register\n");
706f7018c21STomi Valkeinen 		return NULL;
707f7018c21STomi Valkeinen 	}
708f7018c21STomi Valkeinen 	return m2info;
709f7018c21STomi Valkeinen }
710f7018c21STomi Valkeinen 
matroxfb_crtc2_remove(struct matrox_fb_info * minfo,void * crtc2)711f7018c21STomi Valkeinen static void matroxfb_crtc2_remove(struct matrox_fb_info* minfo, void* crtc2) {
712f7018c21STomi Valkeinen 	matroxfb_dh_deregisterfb(crtc2);
713f7018c21STomi Valkeinen 	kfree(crtc2);
714f7018c21STomi Valkeinen }
715f7018c21STomi Valkeinen 
716f7018c21STomi Valkeinen static struct matroxfb_driver crtc2 = {
717f7018c21STomi Valkeinen 		.name =		"Matrox G400 CRTC2",
718f7018c21STomi Valkeinen 		.probe =	matroxfb_crtc2_probe,
719f7018c21STomi Valkeinen 		.remove =	matroxfb_crtc2_remove };
720f7018c21STomi Valkeinen 
matroxfb_crtc2_init(void)721f7018c21STomi Valkeinen static int matroxfb_crtc2_init(void) {
722f7018c21STomi Valkeinen 	if (fb_get_options("matrox_crtc2fb", NULL))
723f7018c21STomi Valkeinen 		return -ENODEV;
724f7018c21STomi Valkeinen 
725f7018c21STomi Valkeinen 	matroxfb_register_driver(&crtc2);
726f7018c21STomi Valkeinen 	return 0;
727f7018c21STomi Valkeinen }
728f7018c21STomi Valkeinen 
matroxfb_crtc2_exit(void)729f7018c21STomi Valkeinen static void matroxfb_crtc2_exit(void) {
730f7018c21STomi Valkeinen 	matroxfb_unregister_driver(&crtc2);
731f7018c21STomi Valkeinen }
732f7018c21STomi Valkeinen 
733f7018c21STomi Valkeinen MODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec <vandrove@vc.cvut.cz>");
734f7018c21STomi Valkeinen MODULE_DESCRIPTION("Matrox G400 CRTC2 driver");
735f7018c21STomi Valkeinen MODULE_LICENSE("GPL");
736f7018c21STomi Valkeinen module_init(matroxfb_crtc2_init);
737f7018c21STomi Valkeinen module_exit(matroxfb_crtc2_exit);
738f7018c21STomi Valkeinen /* we do not have __setup() yet */
739