• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450.
4  *
5  * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
6  *
7  * Portions Copyright (c) 2001 Matrox Graphics Inc.
8  *
9  * Version: 1.65 2002/08/14
10  *
11  * See matroxfb_base.c for contributors.
12  *
13  */
14 
15 #include "matroxfb_base.h"
16 #include "matroxfb_misc.h"
17 #include "matroxfb_DAC1064.h"
18 #include "g450_pll.h"
19 #include <linux/matroxfb.h>
20 #include <asm/div64.h>
21 
22 #include "matroxfb_g450.h"
23 
24 /* Definition of the various controls */
25 struct mctl {
26 	struct v4l2_queryctrl desc;
27 	size_t control;
28 };
29 
30 #define BLMIN	0xF3
31 #define WLMAX	0x3FF
32 
33 static const struct mctl g450_controls[] =
34 {	{ { V4L2_CID_BRIGHTNESS, V4L2_CTRL_TYPE_INTEGER,
35 	  "brightness",
36 	  0, WLMAX-BLMIN, 1, 370-BLMIN,
37 	  0,
38 	}, offsetof(struct matrox_fb_info, altout.tvo_params.brightness) },
39 	{ { V4L2_CID_CONTRAST, V4L2_CTRL_TYPE_INTEGER,
40 	  "contrast",
41 	  0, 1023, 1, 127,
42 	  0,
43 	}, offsetof(struct matrox_fb_info, altout.tvo_params.contrast) },
44 	{ { V4L2_CID_SATURATION, V4L2_CTRL_TYPE_INTEGER,
45 	  "saturation",
46 	  0, 255, 1, 165,
47 	  0,
48 	}, offsetof(struct matrox_fb_info, altout.tvo_params.saturation) },
49 	{ { V4L2_CID_HUE, V4L2_CTRL_TYPE_INTEGER,
50 	  "hue",
51 	  0, 255, 1, 0,
52 	  0,
53 	}, offsetof(struct matrox_fb_info, altout.tvo_params.hue) },
54 	{ { MATROXFB_CID_TESTOUT, V4L2_CTRL_TYPE_BOOLEAN,
55 	  "test output",
56 	  0, 1, 1, 0,
57 	  0,
58 	}, offsetof(struct matrox_fb_info, altout.tvo_params.testout) },
59 };
60 
61 #define G450CTRLS ARRAY_SIZE(g450_controls)
62 
63 /* Return: positive number: id found
64            -EINVAL:         id not found, return failure
65 	   -ENOENT:         id not found, create fake disabled control */
get_ctrl_id(__u32 v4l2_id)66 static int get_ctrl_id(__u32 v4l2_id) {
67 	int i;
68 
69 	for (i = 0; i < G450CTRLS; i++) {
70 		if (v4l2_id < g450_controls[i].desc.id) {
71 			if (g450_controls[i].desc.id == 0x08000000) {
72 				return -EINVAL;
73 			}
74 			return -ENOENT;
75 		}
76 		if (v4l2_id == g450_controls[i].desc.id) {
77 			return i;
78 		}
79 	}
80 	return -EINVAL;
81 }
82 
get_ctrl_ptr(WPMINFO unsigned int idx)83 static inline int* get_ctrl_ptr(WPMINFO unsigned int idx) {
84 	return (int*)((char*)MINFO + g450_controls[idx].control);
85 }
86 
tvo_fill_defaults(WPMINFO2)87 static void tvo_fill_defaults(WPMINFO2) {
88 	unsigned int i;
89 
90 	for (i = 0; i < G450CTRLS; i++) {
91 		*get_ctrl_ptr(PMINFO i) = g450_controls[i].desc.default_value;
92 	}
93 }
94 
cve2_get_reg(WPMINFO int reg)95 static int cve2_get_reg(WPMINFO int reg) {
96 	unsigned long flags;
97 	int val;
98 
99 	matroxfb_DAC_lock_irqsave(flags);
100 	matroxfb_DAC_out(PMINFO 0x87, reg);
101 	val = matroxfb_DAC_in(PMINFO 0x88);
102 	matroxfb_DAC_unlock_irqrestore(flags);
103 	return val;
104 }
105 
cve2_set_reg(WPMINFO int reg,int val)106 static void cve2_set_reg(WPMINFO int reg, int val) {
107 	unsigned long flags;
108 
109 	matroxfb_DAC_lock_irqsave(flags);
110 	matroxfb_DAC_out(PMINFO 0x87, reg);
111 	matroxfb_DAC_out(PMINFO 0x88, val);
112 	matroxfb_DAC_unlock_irqrestore(flags);
113 }
114 
cve2_set_reg10(WPMINFO int reg,int val)115 static void cve2_set_reg10(WPMINFO int reg, int val) {
116 	unsigned long flags;
117 
118 	matroxfb_DAC_lock_irqsave(flags);
119 	matroxfb_DAC_out(PMINFO 0x87, reg);
120 	matroxfb_DAC_out(PMINFO 0x88, val >> 2);
121 	matroxfb_DAC_out(PMINFO 0x87, reg + 1);
122 	matroxfb_DAC_out(PMINFO 0x88, val & 3);
123 	matroxfb_DAC_unlock_irqrestore(flags);
124 }
125 
g450_compute_bwlevel(CPMINFO int * bl,int * wl)126 static void g450_compute_bwlevel(CPMINFO int *bl, int *wl) {
127 	const int b = ACCESS_FBINFO(altout.tvo_params.brightness) + BLMIN;
128 	const int c = ACCESS_FBINFO(altout.tvo_params.contrast);
129 
130 	*bl = max(b - c, BLMIN);
131 	*wl = min(b + c, WLMAX);
132 }
133 
g450_query_ctrl(void * md,struct v4l2_queryctrl * p)134 static int g450_query_ctrl(void* md, struct v4l2_queryctrl *p) {
135 	int i;
136 
137 	i = get_ctrl_id(p->id);
138 	if (i >= 0) {
139 		*p = g450_controls[i].desc;
140 		return 0;
141 	}
142 	if (i == -ENOENT) {
143 		static const struct v4l2_queryctrl disctrl =
144 			{ .flags = V4L2_CTRL_FLAG_DISABLED };
145 
146 		i = p->id;
147 		*p = disctrl;
148 		p->id = i;
149 		sprintf(p->name, "Ctrl #%08X", i);
150 		return 0;
151 	}
152 	return -EINVAL;
153 }
154 
g450_set_ctrl(void * md,struct v4l2_control * p)155 static int g450_set_ctrl(void* md, struct v4l2_control *p) {
156 	int i;
157 	MINFO_FROM(md);
158 
159 	i = get_ctrl_id(p->id);
160 	if (i < 0) return -EINVAL;
161 
162 	/*
163 	 * Check if changed.
164 	 */
165 	if (p->value == *get_ctrl_ptr(PMINFO i)) return 0;
166 
167 	/*
168 	 * Check limits.
169 	 */
170 	if (p->value > g450_controls[i].desc.maximum) return -EINVAL;
171 	if (p->value < g450_controls[i].desc.minimum) return -EINVAL;
172 
173 	/*
174 	 * Store new value.
175 	 */
176 	*get_ctrl_ptr(PMINFO i) = p->value;
177 
178 	switch (p->id) {
179 		case V4L2_CID_BRIGHTNESS:
180 		case V4L2_CID_CONTRAST:
181 			{
182 				int blacklevel, whitelevel;
183 				g450_compute_bwlevel(PMINFO &blacklevel, &whitelevel);
184 				cve2_set_reg10(PMINFO 0x0e, blacklevel);
185 				cve2_set_reg10(PMINFO 0x1e, whitelevel);
186 			}
187 			break;
188 		case V4L2_CID_SATURATION:
189 			cve2_set_reg(PMINFO 0x20, p->value);
190 			cve2_set_reg(PMINFO 0x22, p->value);
191 			break;
192 		case V4L2_CID_HUE:
193 			cve2_set_reg(PMINFO 0x25, p->value);
194 			break;
195 		case MATROXFB_CID_TESTOUT:
196 			{
197 				unsigned char val = cve2_get_reg (PMINFO 0x05);
198 				if (p->value) val |=  0x02;
199 				else          val &= ~0x02;
200 				cve2_set_reg(PMINFO 0x05, val);
201 			}
202 			break;
203 	}
204 
205 
206 	return 0;
207 }
208 
g450_get_ctrl(void * md,struct v4l2_control * p)209 static int g450_get_ctrl(void* md, struct v4l2_control *p) {
210 	int i;
211 	MINFO_FROM(md);
212 
213 	i = get_ctrl_id(p->id);
214 	if (i < 0) return -EINVAL;
215 	p->value = *get_ctrl_ptr(PMINFO i);
216 	return 0;
217 }
218 
219 struct output_desc {
220 	unsigned int	h_vis;
221 	unsigned int	h_f_porch;
222 	unsigned int	h_sync;
223 	unsigned int	h_b_porch;
224 	unsigned long long int	chromasc;
225 	unsigned int	burst;
226 	unsigned int	v_total;
227 };
228 
computeRegs(WPMINFO struct mavenregs * r,struct my_timming * mt,const struct output_desc * outd)229 static void computeRegs(WPMINFO struct mavenregs* r, struct my_timming* mt, const struct output_desc* outd) {
230 	u_int32_t chromasc;
231 	u_int32_t hlen;
232 	u_int32_t hsl;
233 	u_int32_t hbp;
234 	u_int32_t hfp;
235 	u_int32_t hvis;
236 	unsigned int pixclock;
237 	unsigned long long piic;
238 	int mnp;
239 	int over;
240 
241 	r->regs[0x80] = 0x03;	/* | 0x40 for SCART */
242 
243 	hvis = ((mt->HDisplay << 1) + 3) & ~3;
244 
245 	if (hvis >= 2048) {
246 		hvis = 2044;
247 	}
248 
249 	piic = 1000000000ULL * hvis;
250 	do_div(piic, outd->h_vis);
251 
252 	dprintk(KERN_DEBUG "Want %u kHz pixclock\n", (unsigned int)piic);
253 
254 	mnp = matroxfb_g450_setclk(PMINFO piic, M_VIDEO_PLL);
255 
256 	mt->mnp = mnp;
257 	mt->pixclock = g450_mnp2f(PMINFO mnp);
258 
259 	dprintk(KERN_DEBUG "MNP=%08X\n", mnp);
260 
261 	pixclock = 1000000000U / mt->pixclock;
262 
263 	dprintk(KERN_DEBUG "Got %u ps pixclock\n", pixclock);
264 
265 	piic = outd->chromasc;
266 	do_div(piic, mt->pixclock);
267 	chromasc = piic;
268 
269 	dprintk(KERN_DEBUG "Chroma is %08X\n", chromasc);
270 
271 	r->regs[0] = piic >> 24;
272 	r->regs[1] = piic >> 16;
273 	r->regs[2] = piic >>  8;
274 	r->regs[3] = piic >>  0;
275 	hbp = (((outd->h_b_porch + pixclock) / pixclock)) & ~1;
276 	hfp = (((outd->h_f_porch + pixclock) / pixclock)) & ~1;
277 	hsl = (((outd->h_sync + pixclock) / pixclock)) & ~1;
278 	hlen = hvis + hfp + hsl + hbp;
279 	over = hlen & 0x0F;
280 
281 	dprintk(KERN_DEBUG "WL: vis=%u, hf=%u, hs=%u, hb=%u, total=%u\n", hvis, hfp, hsl, hbp, hlen);
282 
283 	if (over) {
284 		hfp -= over;
285 		hlen -= over;
286 		if (over <= 2) {
287 		} else if (over < 10) {
288 			hfp += 4;
289 			hlen += 4;
290 		} else {
291 			hfp += 16;
292 			hlen += 16;
293 		}
294 	}
295 
296 	/* maybe cve2 has requirement 800 < hlen < 1184 */
297 	r->regs[0x08] = hsl;
298 	r->regs[0x09] = (outd->burst + pixclock - 1) / pixclock;	/* burst length */
299 	r->regs[0x0A] = hbp;
300 	r->regs[0x2C] = hfp;
301 	r->regs[0x31] = hvis / 8;
302 	r->regs[0x32] = hvis & 7;
303 
304 	dprintk(KERN_DEBUG "PG: vis=%04X, hf=%02X, hs=%02X, hb=%02X, total=%04X\n", hvis, hfp, hsl, hbp, hlen);
305 
306 	r->regs[0x84] = 1;	/* x sync point */
307 	r->regs[0x85] = 0;
308 	hvis = hvis >> 1;
309 	hlen = hlen >> 1;
310 
311 	dprintk(KERN_DEBUG "hlen=%u hvis=%u\n", hlen, hvis);
312 
313 	mt->interlaced = 1;
314 
315 	mt->HDisplay = hvis & ~7;
316 	mt->HSyncStart = mt->HDisplay + 8;
317 	mt->HSyncEnd = (hlen & ~7) - 8;
318 	mt->HTotal = hlen;
319 
320 	{
321 		int upper;
322 		unsigned int vtotal;
323 		unsigned int vsyncend;
324 		unsigned int vdisplay;
325 
326 		vtotal = mt->VTotal;
327 		vsyncend = mt->VSyncEnd;
328 		vdisplay = mt->VDisplay;
329 		if (vtotal < outd->v_total) {
330 			unsigned int yovr = outd->v_total - vtotal;
331 
332 			vsyncend += yovr >> 1;
333 		} else if (vtotal > outd->v_total) {
334 			vdisplay = outd->v_total - 4;
335 			vsyncend = outd->v_total;
336 		}
337 		upper = (outd->v_total - vsyncend) >> 1;	/* in field lines */
338 		r->regs[0x17] = outd->v_total / 4;
339 		r->regs[0x18] = outd->v_total & 3;
340 		r->regs[0x33] = upper - 1;	/* upper blanking */
341 		r->regs[0x82] = upper;		/* y sync point */
342 		r->regs[0x83] = upper >> 8;
343 
344 		mt->VDisplay = vdisplay;
345 		mt->VSyncStart = outd->v_total - 2;
346 		mt->VSyncEnd = outd->v_total;
347 		mt->VTotal = outd->v_total;
348 	}
349 }
350 
cve2_init_TVdata(int norm,struct mavenregs * data,const struct output_desc ** outd)351 static void cve2_init_TVdata(int norm, struct mavenregs* data, const struct output_desc** outd) {
352 	static const struct output_desc paloutd = {
353 		.h_vis	   = 52148148,	// ps
354 		.h_f_porch =  1407407,	// ps
355 		.h_sync    =  4666667,	// ps
356 		.h_b_porch =  5777778,	// ps
357 		.chromasc  = 19042247534182ULL,	// 4433618.750 Hz
358 		.burst     =  2518518,	// ps
359 		.v_total   =      625,
360 	};
361 	static const struct output_desc ntscoutd = {
362 		.h_vis     = 52888889,	// ps
363 		.h_f_porch =  1333333,	// ps
364 		.h_sync    =  4666667,	// ps
365 		.h_b_porch =  4666667,	// ps
366 		.chromasc  = 15374030659475ULL,	// 3579545.454 Hz
367 		.burst     =  2418418,	// ps
368 		.v_total   =      525,	// lines
369 	};
370 
371 	static const struct mavenregs palregs = { {
372 		0x2A, 0x09, 0x8A, 0xCB,	/* 00: chroma subcarrier */
373 		0x00,
374 		0x00,	/* test */
375 		0xF9,	/* modified by code (F9 written...) */
376 		0x00,	/* ? not written */
377 		0x7E,	/* 08 */
378 		0x44,	/* 09 */
379 		0x9C,	/* 0A */
380 		0x2E,	/* 0B */
381 		0x21,	/* 0C */
382 		0x00,	/* ? not written */
383 //		0x3F, 0x03, /* 0E-0F */
384 		0x3C, 0x03,
385 		0x3C, 0x03, /* 10-11 */
386 		0x1A,	/* 12 */
387 		0x2A,	/* 13 */
388 		0x1C, 0x3D, 0x14, /* 14-16 */
389 		0x9C, 0x01, /* 17-18 */
390 		0x00,	/* 19 */
391 		0xFE,	/* 1A */
392 		0x7E,	/* 1B */
393 		0x60,	/* 1C */
394 		0x05,	/* 1D */
395 //		0x89, 0x03, /* 1E-1F */
396 		0xAD, 0x03,
397 //		0x72,	/* 20 */
398 		0xA5,
399 		0x07,	/* 21 */
400 //		0x72,	/* 22 */
401 		0xA5,
402 		0x00,	/* 23 */
403 		0x00,	/* 24 */
404 		0x00,	/* 25 */
405 		0x08,	/* 26 */
406 		0x04,	/* 27 */
407 		0x00,	/* 28 */
408 		0x1A,	/* 29 */
409 		0x55, 0x01, /* 2A-2B */
410 		0x26,	/* 2C */
411 		0x07, 0x7E, /* 2D-2E */
412 		0x02, 0x54, /* 2F-30 */
413 		0xB0, 0x00, /* 31-32 */
414 		0x14,	/* 33 */
415 		0x49,	/* 34 */
416 		0x00,	/* 35 written multiple times */
417 		0x00,	/* 36 not written */
418 		0xA3,	/* 37 */
419 		0xC8,	/* 38 */
420 		0x22,	/* 39 */
421 		0x02,	/* 3A */
422 		0x22,	/* 3B */
423 		0x3F, 0x03, /* 3C-3D */
424 		0x00,	/* 3E written multiple times */
425 		0x00,	/* 3F not written */
426 	} };
427 	static struct mavenregs ntscregs = { {
428 		0x21, 0xF0, 0x7C, 0x1F,	/* 00: chroma subcarrier */
429 		0x00,
430 		0x00,	/* test */
431 		0xF9,	/* modified by code (F9 written...) */
432 		0x00,	/* ? not written */
433 		0x7E,	/* 08 */
434 		0x43,	/* 09 */
435 		0x7E,	/* 0A */
436 		0x3D,	/* 0B */
437 		0x00,	/* 0C */
438 		0x00,	/* ? not written */
439 		0x41, 0x00, /* 0E-0F */
440 		0x3C, 0x00, /* 10-11 */
441 		0x17,	/* 12 */
442 		0x21,	/* 13 */
443 		0x1B, 0x1B, 0x24, /* 14-16 */
444 		0x83, 0x01, /* 17-18 */
445 		0x00,	/* 19 */
446 		0x0F,	/* 1A */
447 		0x0F,	/* 1B */
448 		0x60,	/* 1C */
449 		0x05,	/* 1D */
450 		//0x89, 0x02, /* 1E-1F */
451 		0xC0, 0x02, /* 1E-1F */
452 		//0x5F,	/* 20 */
453 		0x9C,	/* 20 */
454 		0x04,	/* 21 */
455 		//0x5F,	/* 22 */
456 		0x9C,	/* 22 */
457 		0x01,	/* 23 */
458 		0x02,	/* 24 */
459 		0x00,	/* 25 */
460 		0x0A,	/* 26 */
461 		0x05,	/* 27 */
462 		0x00,	/* 28 */
463 		0x10,	/* 29 */
464 		0xFF, 0x03, /* 2A-2B */
465 		0x24,	/* 2C */
466 		0x0F, 0x78, /* 2D-2E */
467 		0x00, 0x00, /* 2F-30 */
468 		0xB2, 0x04, /* 31-32 */
469 		0x14,	/* 33 */
470 		0x02,	/* 34 */
471 		0x00,	/* 35 written multiple times */
472 		0x00,	/* 36 not written */
473 		0xA3,	/* 37 */
474 		0xC8,	/* 38 */
475 		0x15,	/* 39 */
476 		0x05,	/* 3A */
477 		0x3B,	/* 3B */
478 		0x3C, 0x00, /* 3C-3D */
479 		0x00,	/* 3E written multiple times */
480 		0x00,	/* never written */
481 	} };
482 
483 	if (norm == MATROXFB_OUTPUT_MODE_PAL) {
484 		*data = palregs;
485 		*outd = &paloutd;
486 	} else {
487   		*data = ntscregs;
488 		*outd = &ntscoutd;
489 	}
490  	return;
491 }
492 
493 #define LR(x) cve2_set_reg(PMINFO (x), m->regs[(x)])
cve2_init_TV(WPMINFO const struct mavenregs * m)494 static void cve2_init_TV(WPMINFO const struct mavenregs* m) {
495 	int i;
496 
497 	LR(0x80);
498 	LR(0x82); LR(0x83);
499 	LR(0x84); LR(0x85);
500 
501 	cve2_set_reg(PMINFO 0x3E, 0x01);
502 
503 	for (i = 0; i < 0x3E; i++) {
504 		LR(i);
505 	}
506 	cve2_set_reg(PMINFO 0x3E, 0x00);
507 }
508 
matroxfb_g450_compute(void * md,struct my_timming * mt)509 static int matroxfb_g450_compute(void* md, struct my_timming* mt) {
510 	MINFO_FROM(md);
511 
512 	dprintk(KERN_DEBUG "Computing, mode=%u\n", ACCESS_FBINFO(outputs[1]).mode);
513 
514 	if (mt->crtc == MATROXFB_SRC_CRTC2 &&
515 	    ACCESS_FBINFO(outputs[1]).mode != MATROXFB_OUTPUT_MODE_MONITOR) {
516 		const struct output_desc* outd;
517 
518 		cve2_init_TVdata(ACCESS_FBINFO(outputs[1]).mode, &ACCESS_FBINFO(hw).maven, &outd);
519 		{
520 			int blacklevel, whitelevel;
521 			g450_compute_bwlevel(PMINFO &blacklevel, &whitelevel);
522 			ACCESS_FBINFO(hw).maven.regs[0x0E] = blacklevel >> 2;
523 			ACCESS_FBINFO(hw).maven.regs[0x0F] = blacklevel & 3;
524 			ACCESS_FBINFO(hw).maven.regs[0x1E] = whitelevel >> 2;
525 			ACCESS_FBINFO(hw).maven.regs[0x1F] = whitelevel & 3;
526 
527 			ACCESS_FBINFO(hw).maven.regs[0x20] =
528 			ACCESS_FBINFO(hw).maven.regs[0x22] = ACCESS_FBINFO(altout.tvo_params.saturation);
529 
530 			ACCESS_FBINFO(hw).maven.regs[0x25] = ACCESS_FBINFO(altout.tvo_params.hue);
531 
532 			if (ACCESS_FBINFO(altout.tvo_params.testout)) {
533 				ACCESS_FBINFO(hw).maven.regs[0x05] |= 0x02;
534 			}
535 		}
536 		computeRegs(PMINFO &ACCESS_FBINFO(hw).maven, mt, outd);
537 	} else if (mt->mnp < 0) {
538 		/* We must program clocks before CRTC2, otherwise interlaced mode
539 		   startup may fail */
540 		mt->mnp = matroxfb_g450_setclk(PMINFO mt->pixclock, (mt->crtc == MATROXFB_SRC_CRTC1) ? M_PIXEL_PLL_C : M_VIDEO_PLL);
541 		mt->pixclock = g450_mnp2f(PMINFO mt->mnp);
542 	}
543 	dprintk(KERN_DEBUG "Pixclock = %u\n", mt->pixclock);
544 	return 0;
545 }
546 
matroxfb_g450_program(void * md)547 static int matroxfb_g450_program(void* md) {
548 	MINFO_FROM(md);
549 
550 	if (ACCESS_FBINFO(outputs[1]).mode != MATROXFB_OUTPUT_MODE_MONITOR) {
551 		cve2_init_TV(PMINFO &ACCESS_FBINFO(hw).maven);
552 	}
553 	return 0;
554 }
555 
matroxfb_g450_verify_mode(void * md,u_int32_t arg)556 static int matroxfb_g450_verify_mode(void* md, u_int32_t arg) {
557 	switch (arg) {
558 		case MATROXFB_OUTPUT_MODE_PAL:
559 		case MATROXFB_OUTPUT_MODE_NTSC:
560 		case MATROXFB_OUTPUT_MODE_MONITOR:
561 			return 0;
562 	}
563 	return -EINVAL;
564 }
565 
g450_dvi_compute(void * md,struct my_timming * mt)566 static int g450_dvi_compute(void* md, struct my_timming* mt) {
567 	MINFO_FROM(md);
568 
569 	if (mt->mnp < 0) {
570 		mt->mnp = matroxfb_g450_setclk(PMINFO mt->pixclock, (mt->crtc == MATROXFB_SRC_CRTC1) ? M_PIXEL_PLL_C : M_VIDEO_PLL);
571 		mt->pixclock = g450_mnp2f(PMINFO mt->mnp);
572 	}
573 	return 0;
574 }
575 
576 static struct matrox_altout matroxfb_g450_altout = {
577 	.name		= "Secondary output",
578 	.compute	= matroxfb_g450_compute,
579 	.program	= matroxfb_g450_program,
580 	.verifymode	= matroxfb_g450_verify_mode,
581 	.getqueryctrl	= g450_query_ctrl,
582 	.getctrl	= g450_get_ctrl,
583 	.setctrl	= g450_set_ctrl,
584 };
585 
586 static struct matrox_altout matroxfb_g450_dvi = {
587 	.name		= "DVI output",
588 	.compute	= g450_dvi_compute,
589 };
590 
matroxfb_g450_connect(WPMINFO2)591 void matroxfb_g450_connect(WPMINFO2) {
592 	if (ACCESS_FBINFO(devflags.g450dac)) {
593 		down_write(&ACCESS_FBINFO(altout.lock));
594 		tvo_fill_defaults(PMINFO2);
595 		ACCESS_FBINFO(outputs[1]).src = ACCESS_FBINFO(outputs[1]).default_src;
596 		ACCESS_FBINFO(outputs[1]).data = MINFO;
597 		ACCESS_FBINFO(outputs[1]).output = &matroxfb_g450_altout;
598 		ACCESS_FBINFO(outputs[1]).mode = MATROXFB_OUTPUT_MODE_MONITOR;
599 		ACCESS_FBINFO(outputs[2]).src = ACCESS_FBINFO(outputs[2]).default_src;
600 		ACCESS_FBINFO(outputs[2]).data = MINFO;
601 		ACCESS_FBINFO(outputs[2]).output = &matroxfb_g450_dvi;
602 		ACCESS_FBINFO(outputs[2]).mode = MATROXFB_OUTPUT_MODE_MONITOR;
603 		up_write(&ACCESS_FBINFO(altout.lock));
604 	}
605 }
606 
matroxfb_g450_shutdown(WPMINFO2)607 void matroxfb_g450_shutdown(WPMINFO2) {
608 	if (ACCESS_FBINFO(devflags.g450dac)) {
609 		down_write(&ACCESS_FBINFO(altout.lock));
610 		ACCESS_FBINFO(outputs[1]).src = MATROXFB_SRC_NONE;
611 		ACCESS_FBINFO(outputs[1]).output = NULL;
612 		ACCESS_FBINFO(outputs[1]).data = NULL;
613 		ACCESS_FBINFO(outputs[1]).mode = MATROXFB_OUTPUT_MODE_MONITOR;
614 		ACCESS_FBINFO(outputs[2]).src = MATROXFB_SRC_NONE;
615 		ACCESS_FBINFO(outputs[2]).output = NULL;
616 		ACCESS_FBINFO(outputs[2]).data = NULL;
617 		ACCESS_FBINFO(outputs[2]).mode = MATROXFB_OUTPUT_MODE_MONITOR;
618 		up_write(&ACCESS_FBINFO(altout.lock));
619 	}
620 }
621 
622 EXPORT_SYMBOL(matroxfb_g450_connect);
623 EXPORT_SYMBOL(matroxfb_g450_shutdown);
624 
625 MODULE_AUTHOR("(c) 2000-2002 Petr Vandrovec <vandrove@vc.cvut.cz>");
626 MODULE_DESCRIPTION("Matrox G450/G550 output driver");
627 MODULE_LICENSE("GPL");
628