xref: /haiku/src/add-ons/accelerants/matrox/engine/mga_maventv.c (revision 850f2d1e58cc443f77353c7fc0ce0c158c1fd328)
1 /* Authors:
2    Mark Watson 2000,
3    Rudolf Cornelissen 1/2003-5/2006
4 
5    Thanx to Petr Vandrovec for writing matroxfb.
6 */
7 
8 #define MODULE_BIT 0x00100000
9 
10 #include "mga_std.h"
11 
12 typedef struct {
13 	uint32 h_total;
14 	uint32 h_display;
15 	uint32 h_sync_length;
16 	uint32 front_porch;
17 	uint32 back_porch;
18 	uint32 color_burst;
19 	uint32 v_total;
20 	float chroma_subcarrier;
21 } gx50_maven_timing;
22 
23 
24 // FIXME: try to implement 'fast' and 'slow' settings for all modes, so buffer
25 // duplication or skipping won't be neccesary for realtime video.
26 // FIXME: try to setup the CRTC2 in interlaced mode for the video modes
27 // on <= G400MAX cards.
28 
29 /* find 'exact' valid video PLL setting */
30 status_t g100_g400max_maventv_vid_pll_find(
31 	display_mode target, unsigned int * ht_new, unsigned int * ht_last_line,
32 	uint8 * m_result, uint8 * n_result, uint8 * p_result)
33 {
34 	int m = 0, n = 0, p = 0, m_max;
35 	float diff, diff_smallest = INFINITY;
36 	int best[5] = {0, 0, 0, 0, 0};
37 	int h_total_mod;
38 	float fields_sec, f_vco;
39 	/* We need to be exact, so work with clockperiods per field instead of with frequency.
40 	 * Make sure however we truncate these clocks to be integers!
41 	 * (The NTSC field frequency would otherwise prevent the 'whole number of clocks per field'
42 	 *  check done in this routine later on...) */
43 	uint32 vco_clks_field, max_pclks_field, req_pclks_field;
44 	/* We need this variable to be a float, because we need to be able to verify if this
45 	 * represents a whole number of clocks per field later on! */
46 	float calc_pclks_field;
47 
48 	LOG(2, ("MAVENTV: searching for EXACT videoclock match\n"));
49 
50 	/* determine the max. reference-frequency postscaler setting for the current card */
51 	//fixme: check G100 and G200 m_max if exist and possible...
52 	switch (si->ps.card_type)
53 	{
54 	case G100:
55 		LOG(2, ("MAVENTV: G100 restrictions apply\n"));
56 		m_max = 32;
57 		break;
58 	case G200:
59 		LOG(2, ("MAVENTV: G200 restrictions apply\n"));
60 		m_max = 32;
61 		break;
62 	default:
63 		LOG(2, ("MAVENTV: G400/G400MAX restrictions apply\n"));
64 		m_max = 32;
65 		break;
66 	}
67 
68 	/* set number of fields per second to generate */
69 	if ((target.flags & TV_BITS) == TV_PAL)
70 		fields_sec = 50.0;
71 	else
72 		fields_sec = 59.94;
73 
74 	/* determine the max. pixelclock for the current videomode */
75 	switch (target.space)
76 	{
77 		case B_RGB16_LITTLE:
78 			max_pclks_field = (si->ps.max_dac2_clock_16 * 1000000) / fields_sec;
79 			break;
80 		case B_RGB32_LITTLE:
81 			max_pclks_field = (si->ps.max_dac2_clock_32 * 1000000) / fields_sec;
82 			break;
83 		default:
84 			/* use fail-safe value */
85 			max_pclks_field = (si->ps.max_dac2_clock_32 * 1000000) / fields_sec;
86 			break;
87 	}
88 	/* if some dualhead mode is active, an extra restriction might apply */
89 	if ((target.flags & DUALHEAD_BITS) && (target.space == B_RGB32_LITTLE))
90 		max_pclks_field = (si->ps.max_dac2_clock_32dh * 1000000) / fields_sec;
91 
92 	/* Checkout all possible Htotal settings within the current granularity step
93 	 * of CRTC2 to get a real close videoclock match!
94 	 * (The MAVEN apparantly has a granularity of 1 pixel, while CRTC2 has 8 pixels) */
95 	for (h_total_mod = 0; h_total_mod < 8; h_total_mod++)
96 	{
97 		LOG(2, ("MAVENTV: trying h_total modification of +%d...\n", h_total_mod));
98 
99 		/* Calculate videoclock to be a bit to high so we can compensate for an exact
100 		 * match via h_total_lastline.. */
101 		*ht_new = target.timing.h_total + h_total_mod + 2;
102 
103 		/* Make sure the requested pixelclock is within the PLL's operational limits */
104 		/* lower limit is min_video_vco divided by highest postscaler-factor */
105 		req_pclks_field = *ht_new * target.timing.v_total;
106 		if (req_pclks_field < (((si->ps.min_video_vco * 1000000) / fields_sec) / 8.0))
107 		{
108 			req_pclks_field = (((si->ps.min_video_vco * 1000000) / fields_sec) / 8.0);
109 			LOG(4, ("MAVENTV: WARNING, clamping at lowest possible videoclock\n"));
110 		}
111 		/* upper limit is given by pins in combination with current active mode */
112 		if (req_pclks_field > max_pclks_field)
113 		{
114 			req_pclks_field = max_pclks_field;
115 			LOG(4, ("MAVENTV: WARNING, clamping at highest possible videoclock\n"));
116 		}
117 
118 		/* iterate through all valid PLL postscaler settings */
119 		for (p=0x01; p < 0x10; p = p<<1)
120 		{
121 			/* calc the needed number of VCO clocks per field for this postscaler setting */
122 			vco_clks_field = req_pclks_field * p;
123 
124 			/* check if this is within range of the VCO specs */
125 			if ((vco_clks_field >= ((si->ps.min_video_vco * 1000000) / fields_sec)) &&
126 				(vco_clks_field <= ((si->ps.max_video_vco * 1000000) / fields_sec)))
127 			{
128 				/* iterate trough all valid reference-frequency postscaler settings */
129 				for (m = 2; m <= m_max; m++)
130 				{
131 					/* calculate VCO postscaler setting for current setup.. */
132 					n = (int)(((vco_clks_field * m) / ((si->ps.f_ref * 1000000) / fields_sec)) + 0.5);
133 					/* ..and check for validity */
134 					if ((n < 8) || (n > 128))	continue;
135 
136 					/* special TVmode stuff starts here (rest is in fact standard): */
137 					/* calculate number of videoclocks per field */
138 					calc_pclks_field =
139 						(((uint32)((si->ps.f_ref * 1000000) / fields_sec)) * n) / ((float)(m * p));
140 
141 					/* we need a whole number of clocks per field, otherwise it won't work correctly.
142 					 * (TVout will flicker, green fields will occur) */
143 					if (calc_pclks_field != (uint32)calc_pclks_field) continue;
144 
145 					/* check if we have the min. needed number of clocks per field for a sync lock */
146 					if (calc_pclks_field < ((*ht_new * (target.timing.v_total - 1)) + 2)) continue;
147 
148 					/* calc number of clocks we have for the last field line */
149 					*ht_last_line = calc_pclks_field - (*ht_new * (target.timing.v_total - 1));
150 
151 					/* check if we haven't got too much clocks in the last field line for a sync lock */
152 					if (*ht_last_line > *ht_new) continue;
153 
154 					/* we have a match! */
155 					/* calculate the difference between a full line and the last line */
156 					diff = *ht_new - *ht_last_line;
157 
158 					/* if this last_line comes closer to a full line than earlier 'hits' then use it */
159 					if (diff < diff_smallest)
160 					{
161 						/* log results */
162 						if (diff_smallest == 999999999)
163 							LOG(2, ("MAVENTV: MATCH, "));
164 						else
165 							LOG(2, ("MAVENTV: better MATCH,"));
166 						f_vco = (si->ps.f_ref / m) * n;
167 						LOG(2, ("found vid VCO freq %fMhz, pixclk %fMhz\n", f_vco, (f_vco / p)));
168 						LOG(2, ("MAVENTV: mnp(ex. filter) 0x%02x 0x%02x 0x%02x, h_total %d, ht_lastline %d\n",
169 							(m - 1), (n - 1), (p - 1), (*ht_new - 2), (*ht_last_line - 2)));
170 
171 						/* remember this best match */
172 						diff_smallest = diff;
173 						best[0] = m;
174 						best[1] = n;
175 						best[2] = p;
176 						/* h_total to use for this setting:
177 						 * exclude the 'calculate clock a bit too high' trick */
178 						best[3] = *ht_new - 2;
179 						/* ht_last_line to use for this setting:
180 						 * exclude the 'calculate clock a bit too high' trick */
181 						best[4] = *ht_last_line - 2;
182 					}
183 				}
184 			}
185 		}
186 	}
187 	LOG(2, ("MAVENTV: search completed.\n"));
188 
189 	/* setup the scalers programming values for found optimum setting */
190 	m = best[0] - 1;
191 	n = best[1] - 1;
192 	p = best[2] - 1;
193 
194 	/* if no match was found set fixed PLL frequency so we have something valid at least */
195 	if (diff_smallest == 999999999)
196 	{
197 		LOG(4, ("MAVENTV: WARNING, no MATCH found!\n"));
198 
199 		if (si->ps.f_ref == 27.000)
200 		{
201 			/* set 13.5Mhz */
202 			m = 0x03;
203 			n = 0x07;
204 			p = 0x03;
205 		} else {
206 			/* set 14.31818Mhz */
207 			m = 0x01;
208 			n = 0x07;
209 			p = 0x03;
210 		}
211 		best[3] = target.timing.h_total;
212 		best[4] = target.timing.h_total;
213 	}
214 
215 	/* calc the needed PLL loopbackfilter setting belonging to current VCO speed */
216 	f_vco = (si->ps.f_ref / (m + 1)) * (n + 1);
217 	LOG(2, ("MAVENTV: using vid VCO frequency %fMhz\n", f_vco));
218 
219 	switch (si->ps.card_type)
220 	{
221 	case G100:
222 	case G200:
223 		for (;;)
224 		{
225 			if (f_vco >= 180) {p |= (0x03 << 3); break;};
226 			if (f_vco >= 140) {p |= (0x02 << 3); break;};
227 			if (f_vco >= 100) {p |= (0x01 << 3); break;};
228 			break;
229 		}
230 		break;
231 	default:
232 		for (;;)
233 		{
234 			if (f_vco >= 240) {p |= (0x03 << 3); break;};
235 			if (f_vco >= 170) {p |= (0x02 << 3); break;};
236 			if (f_vco >= 110) {p |= (0x01 << 3); break;};
237 			break;
238 		}
239 		break;
240 	}
241 
242 	/* return results */
243 	*m_result = m;
244 	*n_result = n;
245 	*p_result = p;
246 	*ht_new = best[3];
247 	*ht_last_line = best[4];
248 
249 	/* display the found pixelclock values */
250 	LOG(2, ("MAVENTV: vid PLL check: got %fMHz, mnp 0x%02x 0x%02x 0x%02x\n",
251 		(f_vco / ((p & 0x07) + 1)), m, n, p));
252 	LOG(2, ("MAVENTV: new h_total %d, ht_lastline %d\n", *ht_new, *ht_last_line));
253 
254 	/* return status */
255 	if (diff_smallest == 999999999) return B_ERROR;
256 	return B_OK;
257 }
258 
259 	/* Notes about timing:
260 	 * Note:
261 	 * all horizontal timing is measured in pixelclock periods;
262 	 * all? vertical timing is measured in field? lines.  */
263 
264 	/* Note:
265 	 * <= G400MAX cards have a fixed 27Mhz(?) clock for TV timing register values,
266 	 * while on G450/G550 these need to be calculated based on the video pixelclock. */
267 
268 
269 	/* Notes about signal strengths:
270 	 * Note:
271 	 * G400 and earlier cards have a fixed reference voltage of +2.6 Volt;
272 	 * G450 and G550 cards MAVEN DACs have a switchable ref voltage of +1.5/+2.0 Volt.
273 	 *
274 	 * This voltage is used to feed the videosignals:
275 	 * - Hsync pulse level;
276 	 * - Lowest active video output level;
277 	 * - Highest active video output level.
278 	 * These actual voltages are set via 10bit DACs.
279 	 *
280 	 * G450/G550:
281 	 * The color burst amplitude videosignal is fed by 80% of the above mentioned
282 	 * ref. voltage, and is set via an 8bit DAC.
283 	 * On G400 and earlier cards the ref. voltage is different, and also differs
284 	 * for PAL and NTSC mode. */
285 
286 	/* Note:
287 	 * Increasing the distance between the highest and lowest active video output
288 	 * level increases contrast; decreasing it decreases contrast. */
289 
290 	/* Note:
291 	 * Increasing both the highest and lowest active video output level with the
292 	 * same amount increases brightness; decreasing it decreases brightness. */
293 
294 	/* Note:
295 	 * Increasing the Hsync pulse level increases the black level, so decreases
296 	 * brightness and contrast. */
297 
298 /* Preset maven PAL output (625lines, 50Hz mode) */
299 static void gxx0_maventv_PAL_init(uint8* buffer)
300 {
301 	uint16 value;
302 
303 	/* Chroma subcarrier divider */
304 	buffer[0x00] = 0x2A;
305 	buffer[0x01] = 0x09;
306 	buffer[0x02] = 0x8A;
307 	buffer[0x03] = 0xCB;
308 
309 	buffer[0x04] = 0x00;
310 	buffer[0x05] = 0x00;
311 	buffer[0x06] = 0xF9;
312 	buffer[0x07] = 0x00;
313 	/* Hsync pulse length */
314 	buffer[0x08] = 0x7E;
315 	/* color burst length */
316 	buffer[0x09] = 0x44;
317 	/* back porch length */
318 	buffer[0x0a] = 0x9C;
319 
320 	/* color burst amplitude */
321 	if (si->ps.card_type <= G400MAX)
322 	{
323 		buffer[0x0b] = 0x3e;
324 	}
325 	else
326 	{
327 		buffer[0x0b] = 0x48;
328 	}
329 
330 	buffer[0x0c] = 0x21;
331 	buffer[0x0d] = 0x00;
332 
333 	if (si->ps.card_type <= G400MAX)
334 	{
335 		/* Lowest active video output level.
336 		 * Warning: make sure this stays above (or equals) the sync pulse level! */
337 		value = 0x0ea;
338 		buffer[0x0e] = ((value >> 2) & 0xff);
339 		buffer[0x0f] = (value & 0x03);
340 		/* horizontal sync pulse level */
341 		buffer[0x10] = ((value >> 2) & 0xff);
342 		buffer[0x11] = (value & 0x03);
343 	}
344 	else
345 	{
346 		/* Lowest active video output level.
347 		 * Warning: make sure this stays above (or equals) the sync pulse level! */
348 		value = 0x130;
349 		buffer[0x0e] = ((value >> 2) & 0xff);
350 		buffer[0x0f] = (value & 0x03);
351 		/* horizontal sync pulse level */
352 		buffer[0x10] = ((value >> 2) & 0xff);
353 		buffer[0x11] = (value & 0x03);
354 	}
355 
356 	buffer[0x12] = 0x1A;
357 	buffer[0x13] = 0x2A;
358 
359 	/* functional unit */
360 	buffer[0x14] = 0x1C;
361 	buffer[0x15] = 0x3D;
362 	buffer[0x16] = 0x14;
363 
364 	/* vertical total */ //(=625)
365 	/* b9-2 */
366 	buffer[0x17] = 0x9C;
367 	/* b1-0 in b1-0 */
368 	buffer[0x18] = 0x01;
369 
370 	buffer[0x19] = 0x00;
371 	buffer[0x1a] = 0xFE;
372 	buffer[0x1b] = 0x7E;
373 	buffer[0x1c] = 0x60;
374 	buffer[0x1d] = 0x05;
375 
376 	/* Highest active video output level.
377 	 * Warning: make sure this stays above the lowest active video output level! */
378 	if (si->ps.card_type <= G400MAX)
379 	{
380 		value = 0x24f;
381 		buffer[0x1e] = ((value >> 2) & 0xff);
382 		buffer[0x1f] = (value & 0x03);
383 	}
384 	else
385 	{
386 		value = 0x300;
387 		buffer[0x1e] = ((value >> 2) & 0xff);
388 		buffer[0x1f] = (value & 0x03);
389 	}
390 
391 	/* saturation (field?) #1 */
392 	if (si->ps.card_type <= G400MAX)
393 		buffer[0x20] = 0x72;
394 	else
395 		buffer[0x20] = 0xA5;
396 
397 	buffer[0x21] = 0x07;
398 
399 	/* saturation (field?) #2 */
400 	if (si->ps.card_type <= G400MAX)
401 		buffer[0x22] = 0x72;
402 	else
403 		buffer[0x22] = 0xA5;
404 
405 	buffer[0x23] = 0x00;
406 	buffer[0x24] = 0x00;
407 	/* hue? */
408 	buffer[0x25] = 0x00;
409 
410 	buffer[0x26] = 0x08;
411 	buffer[0x27] = 0x04;
412 	buffer[0x28] = 0x00;
413 	buffer[0x29] = 0x1A;
414 
415 	/* functional unit */
416 	buffer[0x2a] = 0x55;
417 	buffer[0x2b] = 0x01;
418 
419 	/* front porch length */
420 	buffer[0x2c] = 0x26;
421 
422 	/* functional unit */
423 	buffer[0x2d] = 0x07;
424 	buffer[0x2e] = 0x7E;
425 
426 	/* functional unit */
427 	buffer[0x2f] = 0x02;
428 	buffer[0x30] = 0x54;
429 
430 	/* horizontal visible */
431 	value = 0x580;
432 	buffer[0x31] = ((value >> 3) & 0xff);
433 	buffer[0x32] = (value & 0x07);
434 
435 	/* upper blanking (in field lines) */
436 	buffer[0x33] = 0x14; //=((v_total - v_sync_end)/2) -1
437 
438 	buffer[0x34] = 0x49;
439 	buffer[0x35] = 0x00;
440 	buffer[0x36] = 0x00;
441 	buffer[0x37] = 0xA3;
442 	buffer[0x38] = 0xC8;
443 	buffer[0x39] = 0x22;
444 	buffer[0x3a] = 0x02;
445 	buffer[0x3b] = 0x22;
446 
447 	/* functional unit */
448 	buffer[0x3c] = 0x3F;
449 	buffer[0x3d] = 0x03;
450 }
451 
452 /* Preset maven NTSC output (525lines, 59.94Hz mode) */
453 static void gxx0_maventv_NTSC_init(uint8* buffer)
454 {
455 	uint16 value;
456 
457 	/* Chroma subcarrier frequency */
458 	buffer[0x00] = 0x21;
459 	buffer[0x01] = 0xF0;
460 	buffer[0x02] = 0x7C;
461 	buffer[0x03] = 0x1F;
462 
463 	buffer[0x04] = 0x00;
464 	buffer[0x05] = 0x00;//b1 = ON enables colorbar testimage
465 	buffer[0x06] = 0xF9;//b0 = ON enables MAVEN TV output
466 	buffer[0x07] = 0x00;//influences the colorburst signal amplitude somehow
467 
468 	/* Hsync pulse length */
469 	buffer[0x08] = 0x7E;
470 	/* color burst length */
471 	buffer[0x09] = 0x43;
472 	/* back porch length */
473 	buffer[0x0a] = 0x7E;
474 
475 	/* color burst amplitude */
476 	if (si->ps.card_type <= G400MAX)
477 	{
478 		buffer[0x0b] = 0x46;
479 	}
480 	else
481 	{
482 		buffer[0x0b] = 0x48;
483 	}
484 
485 	buffer[0x0c] = 0x00;
486 	buffer[0x0d] = 0x00;
487 
488 	if (si->ps.card_type <= G400MAX)
489 	{
490 		/* Lowest active video output level.
491 		 * Warning: make sure this stays above (or equals) the sync pulse level! */
492 		value = 0x0ea;
493 		buffer[0x0e] = ((value >> 2) & 0xff);
494 		buffer[0x0f] = (value & 0x03);
495 		/* horizontal sync pulse level */
496 		buffer[0x10] = ((value >> 2) & 0xff);
497 		buffer[0x11] = (value & 0x03);
498 	}
499 	else
500 	{
501 		/* Lowest active video output level.
502 		 * Warning: make sure this stays above (or equals) the sync pulse level! */
503 		value = 0x130;
504 		buffer[0x0e] = ((value >> 2) & 0xff);
505 		buffer[0x0f] = (value & 0x03);
506 		/* horizontal sync pulse level */
507 		buffer[0x10] = ((value >> 2) & 0xff);
508 		buffer[0x11] = (value & 0x03);
509 	}
510 
511 	buffer[0x12] = 0x17;
512 	buffer[0x13] = 0x21;
513 
514 	/* functional unit */
515 	buffer[0x14] = 0x1B;
516 	buffer[0x15] = 0x1B;
517 	buffer[0x16] = 0x24;
518 
519 	/* vertical total */
520 	/* b9-2 */
521 	buffer[0x17] = 0x83;
522 	/* b1-0 in b1-0 */
523 	buffer[0x18] = 0x01;
524 
525 	buffer[0x19] = 0x00;//mv register?
526 	buffer[0x1a] = 0x0F;
527 	buffer[0x1b] = 0x0F;
528 	buffer[0x1c] = 0x60;
529 	buffer[0x1d] = 0x05;
530 
531 	/* Highest active video output level.
532 	 * Warning: make sure this stays above the lowest active video output level! */
533 	if (si->ps.card_type <= G400MAX)
534 	{
535 		value = 0x24f;
536 		buffer[0x1e] = ((value >> 2) & 0xff);
537 		buffer[0x1f] = (value & 0x03);
538 	}
539 	else
540 	{
541 		value = 0x300;
542 		buffer[0x1e] = ((value >> 2) & 0xff);
543 		buffer[0x1f] = (value & 0x03);
544 	}
545 
546 	/* color saturation #1 (Y-B ?) */
547 	if (si->ps.card_type <= G400MAX)
548 		buffer[0x20] = 0x5F;
549 	else
550 		buffer[0x20] = 0x9C;
551 
552 	buffer[0x21] = 0x04;
553 
554 	/* color saturation #2 (Y-R ?) */
555 	if (si->ps.card_type <= G400MAX)
556 		buffer[0x22] = 0x5F;
557 	else
558 		buffer[0x22] = 0x9C;
559 
560 	buffer[0x23] = 0x01;
561 	buffer[0x24] = 0x02;
562 
563 	/* hue: preset at 0 degrees */
564 	buffer[0x25] = 0x00;
565 
566 	buffer[0x26] = 0x0A;
567 	buffer[0x27] = 0x05;//sync stuff
568 	buffer[0x28] = 0x00;
569 	buffer[0x29] = 0x10;//field line-length stuff
570 
571 	/* functional unit */
572 	buffer[0x2a] = 0xFF;
573 	buffer[0x2b] = 0x03;
574 
575 	/* front porch length */
576 	buffer[0x2c] = 0x24;
577 
578 	/* functional unit */
579 	buffer[0x2d] = 0x0F;
580 	buffer[0x2e] = 0x78;
581 
582 	/* functional unit */
583 	buffer[0x2f] = 0x00;
584 	buffer[0x30] = 0x00;
585 
586 	/* horizontal visible */
587 	/* b10-3 */
588 	buffer[0x31] = 0xB2;
589 	/* b2-0 in b2-0 */
590 	buffer[0x32] = 0x04;
591 
592 	/* upper blanking (in field lines) */
593 	buffer[0x33] = 0x14;
594 
595 	buffer[0x34] = 0x02;//colorphase or so stuff.
596 	buffer[0x35] = 0x00;
597 	buffer[0x36] = 0x00;
598 	buffer[0x37] = 0xA3;
599 	buffer[0x38] = 0xC8;
600 	buffer[0x39] = 0x15;
601 	buffer[0x3a] = 0x05;
602 	buffer[0x3b] = 0x3B;
603 
604 	/* functional unit */
605 	buffer[0x3c] = 0x3C;
606 	buffer[0x3d] = 0x00;
607 }
608 
609 static void gx50_maventv_PAL_timing(gx50_maven_timing *m_timing)
610 {
611 	/* values are given in picoseconds */
612 	m_timing->h_total = 64000000;
613 	/* the sum of the signal duration below should match h_total! */
614 	m_timing->h_display = 52148148;
615 	m_timing->h_sync_length = 4666667;
616 	m_timing->front_porch = 1407407;
617 	m_timing->back_porch = 5777778;
618 	/* colorburst is 'superimposed' on the above timing */
619 	m_timing->color_burst = 2518518;
620 	/* number of lines per frame */
621 	m_timing->v_total = 625;
622 	/* color carrier frequency in Mhz */
623 	m_timing->chroma_subcarrier = 4.43361875;
624 }
625 
626 static void gx50_maventv_NTSC_timing(gx50_maven_timing *m_timing)
627 {
628 	/* values are given in picoseconds */
629 	m_timing->h_total = 63555556;
630 	/* the sum of the signal duration below should match h_total! */
631 	m_timing->h_display = 52888889;
632 	m_timing->h_sync_length = 4666667;
633 	m_timing->front_porch = 1333333;
634 	m_timing->back_porch = 4666667;
635 	/* colorburst is 'superimposed' on the above timing */
636 	m_timing->color_burst = 2418418;
637 	/* number of lines per frame */
638 	m_timing->v_total = 525;
639 	/* color carrier frequency in Mhz */
640 	m_timing->chroma_subcarrier = 3.579545454;
641 }
642 
643 int maventv_init(display_mode target)
644 {
645 	uint8 val;
646 	uint8 m_result, n_result, p_result;
647 	unsigned int ht_new, ht_last_line;
648 	float calc_pclk;
649 	/* use a display_mode copy because we might tune it for TVout compatibility */
650 	display_mode tv_target = target;
651 	/* used as buffer for TVout signal to generate */
652 	uint8 maventv_regs[64];
653 	/* used in G450/G550 to calculate TVout signal timing dependant on pixelclock;
654 	 * <= G400MAX use fixed settings because base-clock here is the fixed crystal
655 	 * frequency. */
656 	//fixme:
657 	//if <=G400 cards with MAVEN and crystal of 14.31818Mhz exist, modify!?!
658 	//only saw 27Mhz versions of G100 and G200 that (can) hold a MAVEN upto now...
659 	gx50_maven_timing m_timing;
660 
661 	/* preset new TVout mode */
662 	if ((tv_target.flags & TV_BITS) == TV_PAL)
663 	{
664 		LOG(4, ("MAVENTV: PAL TVout\n"));
665 		gxx0_maventv_PAL_init(maventv_regs);
666 		gx50_maventv_PAL_timing(&m_timing);
667 	}
668 	else
669 	{
670 		LOG(4, ("MAVENTV: NTSC TVout\n"));
671 		gxx0_maventv_NTSC_init(maventv_regs);
672 		gx50_maventv_NTSC_timing(&m_timing);
673 	}
674 
675 	/* enter mode-program mode */
676 	if (si->ps.card_type <= G400MAX) MAVW(PGM, 0x01);
677 	else
678 	{
679 		DXIW(TVO_IDX, MGAMAV_PGM);
680 		DXIW(TVO_DATA, 0x01);
681 	}
682 
683 	/* tune new TVout mode */
684 	if (si->ps.card_type <= G400MAX)
685 	{
686 		/* setup TV-mode 'copy' of CRTC2, setup PLL, inputs, outputs and sync-locks */
687 		MAVW(MONSET, 0x00);
688 		MAVW(MONEN, 0xA2);
689 
690 		/* xmiscctrl */
691 		//unknown regs:
692 		MAVWW(WREG_0X8E_L, 0x1EFF);
693 		MAVW(BREG_0XC6, 0x01);
694 
695 		MAVW(LOCK, 0x01);
696 		MAVW(OUTMODE, 0x08);
697 
698 		/* set high contrast/brightness range for TVout */
699 		/* Note:
700 		 * b4-5 have contrast/brightness function during TVout, while these bits
701 		 * are used to set sync polarity in a serial fashion during monitor modes.
702 		 * Setting both these bits here will 'increase' the sync polarity offset
703 		 * by one! */
704 		MAVW(LUMA, 0x78);
705 		/* We just made a sync polarity programming step for monitor modes, so:
706 		 * note the offset from 'reset position' we will have now.
707 		 * Note:
708 		 * Not applicable for singlehead cards with a MAVEN, since it's only used
709 		 * for TVout there. */
710 		si->maven_syncpol_offset += 1;
711 		if (si->maven_syncpol_offset > 3) si->maven_syncpol_offset = 0;
712 
713 		//unknown regs:
714 		MAVW(STABLE, 0x02);
715 		MAVW(MONEN, 0xB3);
716 
717 		/* modify mode to center and size correctly on TV */
718 		{
719 			int diff;
720 			float uscan_fact;
721 			bool tweak = false; /* needed for NTSC VCD mode */
722 
723 			if (!(tv_target.flags & TV_VIDEO)) /* Desktop modes */
724 			{
725 				LOG(4,("MAVENTV: setting underscanning ('downscaled') desktop mode\n"));
726 
727 				/* adapt mode to underscan correctly */
728 				if ((tv_target.timing.h_display < 704) && ((tv_target.flags & TV_BITS) == TV_PAL))
729 				{
730 					/* can't be higher because of scaling limitations in MAVEN! */
731 					uscan_fact = 0.76;
732 				}
733 				else
734 				{
735 					/* optimal setting for desktop modes.. */
736 					uscan_fact = 0.80;
737 				}
738 				/* horizontal */
739 				tv_target.timing.h_total = (tv_target.timing.h_display / uscan_fact);
740 				/* adhere to CRTC constraints */
741 				tv_target.timing.h_total &= ~0x0007;
742 				/* vertical */
743 				tv_target.timing.v_total = (tv_target.timing.v_display / uscan_fact);
744 
745 				/* now do vertical centering */
746 				if ((tv_target.flags & TV_BITS) == TV_PAL)
747 				{
748 					diff = tv_target.timing.v_total - tv_target.timing.v_display;
749 					tv_target.timing.v_sync_start = tv_target.timing.v_display + ((diff * 7) / 20);
750 					/* sync all the way 'to the end' to prevent vertical overscanning
751 					 * rubbish on top of screen due to MAVEN hardware design fault */
752 					tv_target.timing.v_sync_end = tv_target.timing.v_total;
753 				}
754 				else
755 				{
756 					diff = tv_target.timing.v_total - tv_target.timing.v_display;
757 					tv_target.timing.v_sync_start = tv_target.timing.v_display + ((diff * 5) / 20);
758 					/* sync all the way 'to the end' to prevent vertical overscanning
759 					 * rubbish on top of screen due to MAVEN hardware design fault */
760 					tv_target.timing.v_sync_end = tv_target.timing.v_total;
761 				}
762 			}
763 			else /* Video modes */
764 			{
765 				uint16 mode =
766 					(((tv_target.flags & TV_BITS) << (14 - 9)) | tv_target.timing.h_display);
767 
768 				LOG(4,("MAVENTV: setting overscanning ('unscaled') video mode\n"));
769 
770 				/* adapt standard modes to be displayed 1:1 */
771 				switch (mode)
772 				{
773 				case ((TV_NTSC << (14 - 9)) | 640): /* NTSC VCD mode */
774 					/* horizontal: adhere to CRTC granularity (8) */
775 					/* Note: h_total = 704 has no PLL match! */
776 					tv_target.timing.h_total = 696;
777 					/* because this 'low' horizontal resolution cannot be scaled up
778 					 * for overscanning use (MAVEN restriction) we need to do some
779 					 * tweaking to get a mode we can work with that still has the
780 					 * correct aspect ratio.
781 					 * This mode has just a little bit horizontal overscanning. */
782 					tweak = true;
783 					break;
784 				case ((TV_NTSC << (14 - 9)) | 720): /* NTSC DVD mode */
785 					/* horizontal: adhere to CRTC granularity (8) */
786 					tv_target.timing.h_total = 784;
787 					/* MGA_TVOs need additional tweaking */
788 					if (!si->ps.secondary_head) si->crtc_delay += 12;
789 					break;
790 				case ((TV_PAL << (14 - 9)) | 768): /* PAL VCD mode */
791 					/* horizontal: adhere to CRTC granularity (8) */
792 					tv_target.timing.h_total = 832;
793 					break;
794 				case ((TV_PAL << (14 - 9)) | 720): /* PAL DVD mode */
795 					/* horizontal: adhere to CRTC granularity (8) */
796 					tv_target.timing.h_total = 784;
797 					break;
798 				default:
799 					/* non-standard mode: just hope for he best. */
800 					break;
801 				}
802 
803 				/* now do vertical centering and clipping */
804 				if ((tv_target.flags & TV_BITS) == TV_PAL)
805 				{
806 					/* defined by the PAL standard */
807 					tv_target.timing.v_total = m_timing.v_total;
808 					/* we need to center the image on TV vertically.
809 					 * note that 576 is the maximum supported resolution for the PAL standard,
810 					 * this is already overscanning by approx 8-10% */
811 					diff = 576 - tv_target.timing.v_display;
812 					/* if we cannot display the current vertical resolution fully, clip it */
813 					if (diff < 0)
814 					{
815 						tv_target.timing.v_display = 576;
816 						diff = 0;
817 					}
818 					/* now center the image on TV */
819 					tv_target.timing.v_sync_start = tv_target.timing.v_display + (diff / 2);
820 					/* sync all the way 'to the end' to prevent vertical overscanning
821 					 * rubbish on top of screen due to MAVEN hardware design fault */
822 					/* note: probably invisible in these Video modes */
823 					tv_target.timing.v_sync_end = tv_target.timing.v_total;
824 				}
825 				else
826 				{
827 					/* defined by the NTSC standard */
828 					tv_target.timing.v_total = m_timing.v_total;
829 					/* NTSC VCD mode needs to be scaled down vertically to get correct
830 					 * aspect ratio... */
831 					if (tweak) tv_target.timing.v_total += 32;
832 					/* we need to center the image on TV vertically.
833 					 * note that 480 is the maximum supported resolution for the NTSC standard,
834 					 * this is already overscanning by approx 8-10% */
835 					diff = 480 - tv_target.timing.v_display;
836 					/* if we cannot display the current vertical resolution fully, clip it */
837 					if (diff < 0)
838 					{
839 						tv_target.timing.v_display = 480;
840 						diff = 0;
841 					}
842 					/* now center the image on TV */
843 					tv_target.timing.v_sync_start = tv_target.timing.v_display + (diff / 2);
844 					/* ...NTSC VCD mode needs to be moved up to center the tweaked mode
845 					 * correcty... */
846 					if (tweak) tv_target.timing.v_sync_start += 9;
847 					/* sync all the way 'to the end' to prevent vertical overscanning
848 					 * rubbish on top of screen due to MAVEN hardware design fault */
849 					/* note: might be visible in the NTSC VCD Video mode */
850 					tv_target.timing.v_sync_end = tv_target.timing.v_total;
851 				}
852 			}
853 
854 			/* finally do horizontal centering */
855 			if ((tv_target.flags & TV_BITS) == TV_PAL)
856 			{
857 				diff = tv_target.timing.h_total - tv_target.timing.h_display;
858 				if (!si->ps.secondary_head)
859 				{
860 					tv_target.timing.h_sync_start = tv_target.timing.h_display - 16 + (diff / 2);
861 					/* keep adhering to CRTC constraints */
862 					tv_target.timing.h_sync_start &= ~0x0007;
863 					tv_target.timing.h_sync_end = tv_target.timing.h_sync_start + 32;
864 				}
865 				else
866 				{
867 					tv_target.timing.h_sync_start = tv_target.timing.h_display - 0 + (diff / 2);
868 					/* keep adhering to CRTC constraints */
869 					tv_target.timing.h_sync_start &= ~0x0007;
870 					tv_target.timing.h_sync_end = tv_target.timing.h_sync_start + 16;
871 				}
872 			}
873 			else
874 			{
875 				diff = tv_target.timing.h_total - tv_target.timing.h_display;
876 				tv_target.timing.h_sync_start = tv_target.timing.h_display - 16 + (diff / 2);
877 				/* ...and finally the NTSC VCD mode needs to be moved to the right to
878 				 * center the tweaked mode correctly. */
879 				if (tweak) tv_target.timing.h_sync_start -= 16;
880 				/* keep adhering to CRTC constraints */
881 				tv_target.timing.h_sync_start &= ~0x0007;
882 				tv_target.timing.h_sync_end = tv_target.timing.h_sync_start + 16;
883 			}
884 		}
885 
886 		/* tune crtc delay */
887 		if ((tv_target.timing.h_display >= 1000) && (((tv_target.flags & TV_BITS) != TV_PAL)))
888 		{
889 			si->crtc_delay += 1;
890 		}
891 		else
892 		{
893 			si->crtc_delay -= 3;
894 		}
895 
896 		/* setup video PLL */
897 		g100_g400max_maventv_vid_pll_find(
898 			tv_target, &ht_new, &ht_last_line, &m_result, &n_result, &p_result);
899 		MAVW(PIXPLLM, m_result);
900 		MAVW(PIXPLLN, n_result);
901 		MAVW(PIXPLLP, (p_result | 0x80));
902 
903 		MAVW(MONSET, 0x20);
904 
905 		MAVW(TEST, 0x10);
906 
907 		/* htotal - 2 */
908 		MAVWW(HTOTALL, ht_new);
909 
910 		/* last line in field can have different length */
911 		/* hlen - 2 */
912 		MAVWW(LASTLINEL, ht_last_line);
913 
914 		/* horizontal vidrst pos: 0 <= vidrst pos <= htotal - 2 */
915 		MAVWW(HVIDRSTL, (ht_last_line - si->crtc_delay -
916 						(tv_target.timing.h_sync_end - tv_target.timing.h_sync_start)));
917 		//ORG (does the same but with limit checks but these limits should never occur!):
918 //		slen = tv_target.timing.h_sync_end - tv_target.timing.h_sync_start;
919 //		hcrt = tv_target.timing.h_total - slen - si->crtc_delay;
920 //		if (ht_last_line < tv_target.timing.h_total) hcrt += ht_last_line;
921 //		if (hcrt > tv_target.timing.h_total) hcrt -= tv_target.timing.h_total;
922 //		if (hcrt + 2 > tv_target.timing.h_total) hcrt = 0;	/* or issue warning? */
923 //		MAVWW(HVIDRSTL, hcrt);
924 
925 		/* who knows */
926 		MAVWW(HSYNCSTRL, 0x0004);//must be 4!!
927 
928 		/* hblanking end: 100% */
929 		MAVWW(HSYNCLENL, (tv_target.timing.h_total - tv_target.timing.h_sync_end));
930 
931 		/* vertical line count - 1 */
932 		MAVWW(VTOTALL, (tv_target.timing.v_total - 1));
933 
934 		/* vertical vidrst pos */
935 		MAVWW(VVIDRSTL, (tv_target.timing.v_total - 2));
936 
937 		/* something end... [A6]+1..[A8] */
938 		MAVWW(VSYNCSTRL, 0x0001);
939 
940 		/* vblanking end: stop vblanking */
941 		MAVWW(VSYNCLENL, (tv_target.timing.v_sync_end - tv_target.timing.v_sync_start - 1));
942 		//org: no visible diff:
943 		//MAVWW(VSYNCLENL, (tv_target.timing.v_total - tv_target.timing.v_sync_start - 1));
944 
945 		/* something start... 0..[A4]-1 */
946 		MAVWW(VDISPLAYL, 0x0000);
947 		//std setmode (no visible difference)
948 		//MAVWW(VDISPLAYL, (tv_target.timing.v_total - 1));
949 
950 		/* ... */
951 		MAVWW(WREG_0X98_L, 0x0000);
952 
953 		/* moves picture up/down and so on... */
954 		MAVWW(VSOMETHINGL, 0x0001); /* Fix this... 0..VTotal */
955 
956 		{
957 			uint32 h_display_tv;
958 			uint8 h_scale_tv;
959 
960 			unsigned int ib_min_length;
961 			unsigned int ib_length;
962 			int index;
963 
964 			/* calc hor scale-back factor from input to output picture (in 1.7 format)
965 			 * the MAVEN has 736 pixels fixed total outputline length for TVout */
966 			h_scale_tv = (736 << 7) / tv_target.timing.h_total;//should be PLL corrected
967 			LOG(4,("MAVENTV: horizontal scale-back factor is: %f\n", (h_scale_tv / 128.0)));
968 
969 			/* limit values to MAVEN capabilities (scale-back factor is 0.5-1.0) */
970 			if (h_scale_tv > 0x80)
971 			{
972 				h_scale_tv = 0x80;
973 				LOG(4,("MAVENTV: limiting horizontal scale-back factor to: %f\n", (h_scale_tv / 128.0)));
974 			}
975 			if (h_scale_tv < 0x40)
976 			{
977 				h_scale_tv = 0x40;
978 				LOG(4,("MAVENTV: limiting horizontal scale-back factor to: %f\n", (h_scale_tv / 128.0)));
979 			}
980 			/* make sure we get no round-off error artifacts on screen */
981 			h_scale_tv--;
982 
983 			/* calc difference in (wanted output picture width (excl. hsync_length)) and
984 			 * (fixed total output line length (=768)),
985 			 * based on input picture and scaling factor */
986 			/* (MAVEN trick (part 1) to get output picture width to fit into just 8 bits) */
987 			h_display_tv = ((768 - 1) << 7) -
988 				(((tv_target.timing.h_total - tv_target.timing.h_sync_end)	/* is left margin */
989 				 + tv_target.timing.h_display - 8)
990 				 * h_scale_tv);
991 			/* convert result from 25.7 to 32.0 format */
992 			h_display_tv = h_display_tv >> 7;
993 			LOG(4,("MAVENTV: displaying output on %d picture pixels\n",
994 				((768 - 1) - h_display_tv)));
995 
996 			/* half result: MAVEN trick (part 2)
997 			 * (258 - 768 pixels, only even number per line is possible) */
998 			h_display_tv = h_display_tv >> 1;
999 			/* limit value to register contraints */
1000 			if (h_display_tv > 0xFF) h_display_tv = 0xFF;
1001 			MAVW(HSCALETV, h_scale_tv);
1002 			MAVW(HDISPLAYTV, h_display_tv);
1003 
1004 
1005 			/* calculate line inputbuffer length */
1006 			/* It must be between (including):
1007 			 * ((input picture left margin) + (input picture hor. resolution) + 4)
1008 			 * AND
1009 			 * (input picture total line length) (PLL corrected) */
1010 
1011 			/* calculate minimal line input buffer length */
1012 			ib_min_length = ((tv_target.timing.h_total - tv_target.timing.h_sync_end) +
1013 				 			  tv_target.timing.h_display + 4);
1014 
1015 			/* calculate optimal line input buffer length (so top of picture is OK too) */
1016 			/* The following formula applies:
1017 			 * optimal buffer length = ((((0x78 * i) - R) / hor. scaling factor) + Q)
1018 			 *
1019 			 * where (in 4.8 format!)
1020 		     * R      Qmin  Qmax
1021 			 * 0x0E0  0x5AE 0x5BF
1022 			 * 0x100  0x5CF 0x5FF
1023 			 * 0x180  0x653 0x67F
1024 			 * 0x200  0x6F8 0x6FF
1025 			 */
1026 			index = 1;
1027 			do
1028 			{
1029 				ib_length = ((((((0x7800 << 7) * index) - (0x100 << 7)) / h_scale_tv) + 0x05E7) >> 8);
1030 				index++;
1031 			} while (ib_length < ib_min_length);
1032 			LOG(4,("MAVENTV: optimal line inputbuffer length: %d\n", ib_length));
1033 
1034 			if (ib_length >= ht_new + 2)
1035 			{
1036 				ib_length = ib_min_length;
1037 				LOG(4,("MAVENTV: limiting line inputbuffer length, setting minimal usable: %d\n", ib_length));
1038 			}
1039 			MAVWW(HDISPLAYL, ib_length);
1040 		}
1041 
1042 		{
1043 			uint16 t_scale_tv;
1044 			uint32 v_display_tv;
1045 
1046 			/* calc total scale-back factor from input to output picture */
1047 			{
1048 				uint32 out_clocks;
1049 				uint32 in_clocks;
1050 
1051 				//takes care of green stripes:
1052 				/* calc output clocks per frame */
1053 				out_clocks = m_timing.v_total * (ht_new + 2);
1054 
1055 				/* calc input clocks per frame */
1056 				in_clocks = (tv_target.timing.v_total - 1) * (ht_new + 2) +	ht_last_line + 2;
1057 
1058 				/* calc total scale-back factor from input to output picture in 1.15 format */
1059 				t_scale_tv = ((((uint64)out_clocks) << 15) / in_clocks);
1060 				LOG(4,("MAVENTV: total scale-back factor is: %f\n", (t_scale_tv / 32768.0)));
1061 
1062 				/* min. scale-back factor is 1.0 for 1:1 output */
1063 				if (t_scale_tv > 0x8000)
1064 				{
1065 					t_scale_tv = 0x8000;
1066 					LOG(4,("MAVENTV: limiting total scale-back factor to: %f\n", (t_scale_tv / 32768.0)));
1067 				}
1068 			}
1069 
1070 			/*calc output picture height based on input picture and scaling factor */
1071 			//warning: v_display was 'one' lower originally!
1072 			v_display_tv =
1073 				((tv_target.timing.v_sync_end - tv_target.timing.v_sync_start) 	/* is sync length */
1074 				 + (tv_target.timing.v_total - tv_target.timing.v_sync_end) 	/* is upper margin */
1075 				 + tv_target.timing.v_display)
1076 				 * t_scale_tv;
1077 			/* convert result from 17.15 to 32.0 format */
1078 			v_display_tv = (v_display_tv >> 15);
1079 			LOG(4,("MAVENTV: displaying output on %d picture frame-lines\n", v_display_tv));
1080 
1081 			/* half result, and compensate for internal register offset
1082 			 * (MAVEN trick to get it to fit into just 8 bits).
1083 			 * (allowed output frame height is 292 - 802 lines, only even numbers) */
1084 			v_display_tv = (v_display_tv >> 1) - 146;
1085 			/* limit value to register contraints */
1086 			if (v_display_tv > 0xFF) v_display_tv = 0xFF;
1087 			/* make sure we get no round-off error artifacts on screen */
1088 			t_scale_tv--;
1089 
1090 			MAVWW(TSCALETVL, t_scale_tv);
1091 			MAVW(VDISPLAYTV, v_display_tv);
1092 		}
1093 
1094 		MAVW(TEST, 0x00);
1095 
1096 		/* gamma correction registers */
1097 		MAVW(GAMMA1, 0x00);
1098 		MAVW(GAMMA2, 0x00);
1099 		MAVW(GAMMA3, 0x00);
1100 		MAVW(GAMMA4, 0x1F);
1101 		MAVW(GAMMA5, 0x10);
1102 		MAVW(GAMMA6, 0x10);
1103 		MAVW(GAMMA7, 0x10);
1104 		MAVW(GAMMA8, 0x64);	/* 100 */
1105 		MAVW(GAMMA9, 0xC8);	/* 200 */
1106 
1107 		/* set flickerfilter */
1108 		if (!(tv_target.flags & TV_VIDEO))
1109 		{
1110 			/* Desktop modes (those are scaled): filter on to prevent artifacts */
1111 			MAVW(FFILTER, 0xa2);
1112 			LOG(4,("MAVENTV: enabling flicker filter\n"));
1113 		}
1114 		else
1115 		{
1116 			/* Video modes (those are unscaled): filter off to increase sharpness */
1117 			//fixme? OFF is dependant on MAVEN version(?): MGA_TVO_B = $40, else $00.
1118 			MAVW(FFILTER, 0x00);
1119 			LOG(4,("MAVENTV: disabling flicker filter\n"));
1120 		}
1121 
1122 		/* 0x10 or anything ored with it */
1123 		//fixme? linux uses 0x14...
1124 		MAVW(TEST, (MAVR(TEST) & 0x10));
1125 
1126 		/* output: SVideo/Composite */
1127 		MAVW(OUTMODE, 0x08);
1128 	}
1129 	else /* card_type is >= G450 */
1130 	{
1131 		//fixme: setup an intermediate buffer if vertical res is different than settings below!
1132 		//fixme: setup 2D or 3D engine to do screen_to_screen_scaled_filtered_blit between the buffers
1133 		//       during vertical retrace!
1134 		if ((tv_target.flags & TV_BITS) == TV_PAL)
1135 		{
1136 			int diff;
1137 
1138 			/* defined by the PAL standard */
1139 			tv_target.timing.v_total = m_timing.v_total;
1140 			/* we need to center the image on TV vertically.
1141 			 * note that 576 is the maximum supported resolution for the PAL standard,
1142 			 * this is already overscanning by approx 8-10% */
1143 			diff = 576 - tv_target.timing.v_display;
1144 			/* if we cannot display the current vertical resolution fully, clip it */
1145 			if (diff < 0)
1146 			{
1147 				tv_target.timing.v_display = 576;
1148 				diff = 0;
1149 			}
1150 			/* now center the image on TV by centering the vertical sync pulse */
1151 			tv_target.timing.v_sync_start = tv_target.timing.v_display + 1 + (diff / 2);
1152 			tv_target.timing.v_sync_end = tv_target.timing.v_sync_start + 1;
1153 		}
1154 		else
1155 		{
1156 			int diff;
1157 
1158 			/* defined by the NTSC standard */
1159 			tv_target.timing.v_total = m_timing.v_total;
1160 			/* we need to center the image on TV vertically.
1161 			 * note that 480 is the maximum supported resolution for the NTSC standard,
1162 			 * this is already overscanning by approx 8-10% */
1163 			diff = 480 - tv_target.timing.v_display;
1164 			/* if we cannot display the current vertical resolution fully, clip it */
1165 			if (diff < 0)
1166 			{
1167 				tv_target.timing.v_display = 480;
1168 				diff = 0;
1169 			}
1170 			/* now center the image on TV by centering the vertical sync pulse */
1171 			tv_target.timing.v_sync_start = tv_target.timing.v_display + 1 + (diff / 2);
1172 			tv_target.timing.v_sync_end = tv_target.timing.v_sync_start + 1;
1173 		}
1174 
1175 		/* setup video PLL for G450/G550:
1176 		 * this can be done in the normal way because the MAVEN works in slave mode!
1177 		 * NOTE: must be done before programming CRTC2, or interlaced startup may fail. */
1178 
1179 		//fixme: make sure videoPLL is powered up: XPWRCTRL b1=1
1180 		{
1181 			uint16 front_porch, back_porch, h_sync_length, burst_length, h_total, h_display;
1182 			uint32 chromasc;
1183 			uint64 pix_period;
1184 			uint16 h_total_wanted, leftover;
1185 
1186 			/* calculate tv_h_display in 'half pixelclocks' and adhere to MAVEN restrictions.
1187 			 * ('half pixelclocks' exist because the MAVEN uses them...) */
1188 			h_display = (((tv_target.timing.h_display << 1) + 3) & ~0x03);
1189 			if (h_display > 2044) h_display = 2044;
1190 			/* copy result to MAVEN TV mode */
1191 			maventv_regs[0x31] = (h_display >> 3);
1192 			maventv_regs[0x32] = (h_display & 0x07);
1193 
1194 			/* calculate needed video pixelclock in kHz.
1195 			 * NOTE:
1196 			 * The clock calculated is based on MAVEN output, so each pixelclock period
1197 			 * is in fact a 'half pixelclock' period compared to monitor mode use. */
1198 			tv_target.timing.pixel_clock =
1199 				((((uint64)h_display) * 1000000000) / m_timing.h_display);
1200 
1201 			/* tune display_mode adhering to CRTC2 restrictions */
1202 			/* (truncate h_display to 'whole pixelclocks') */
1203 			tv_target.timing.h_display = ((h_display >> 1) & ~0x07);
1204 			tv_target.timing.h_sync_start = tv_target.timing.h_display + 8;
1205 
1206 			g450_g550_maven_vid_pll_find(tv_target, &calc_pclk, &m_result, &n_result, &p_result, 1);
1207 			/* adjust mode to actually used pixelclock */
1208 			tv_target.timing.pixel_clock = (calc_pclk * 1000);
1209 
1210 			/* program videoPLL */
1211 			DXIW(VIDPLLM, m_result);
1212 			DXIW(VIDPLLN, n_result);
1213 			DXIW(VIDPLLP, p_result);
1214 
1215 			/* calculate videoclock 'half' period duration in picoseconds */
1216 			pix_period = (1000000000 / ((float)tv_target.timing.pixel_clock)) + 0.5;
1217 			LOG(4,("MAVENTV: TV videoclock period is %d picoseconds\n", pix_period));
1218 
1219 			/* calculate number of 'half' clocks per line according to pixelclock set */
1220 			/* fixme: try to setup the modes in such a way that
1221 			 * (h_total_clk % 16) == 0 because of the CRTC2 restrictions:
1222 			 * we want to loose the truncating h_total trick below if possible! */
1223 			/* Note:
1224 			 * This is here so we can see the wanted and calc'd timing difference. */
1225 			h_total_wanted = ((m_timing.h_total / ((float)pix_period)) + 0.5);
1226 			LOG(4,("MAVENTV: TV h_total should be %d units\n", h_total_wanted));
1227 
1228 			/* calculate chroma subcarrier value to setup:
1229 			 * do this as exact as possible because this signal is very sensitive.. */
1230 			chromasc =
1231 				((((uint64)0x100000000) * (m_timing.chroma_subcarrier / calc_pclk)) + 0.5);
1232 			/* copy result to MAVEN TV mode */
1233 			maventv_regs[0] = ((chromasc >> 24) & 0xff);
1234 			maventv_regs[1] = ((chromasc >> 16) & 0xff);
1235 			maventv_regs[2] = ((chromasc >>  8) & 0xff);
1236 			maventv_regs[3] = ((chromasc >>  0) & 0xff);
1237 			LOG(4,("MAVENTV: TV chroma subcarrier divider set is $%08x\n", chromasc));
1238 
1239 			/* calculate front porch in 'half pixelclocks' */
1240 			/* we always round up because of the h_total truncating 'trick' below,
1241 			 * which works in combination with the existing difference between
1242 			 * h_total_clk and h_total */
1243 			//fixme: prevent this if possible!
1244 			front_porch = ((m_timing.front_porch / ((float)pix_period)) + 1);
1245 			/* value must be even */
1246 			front_porch &= ~0x01;
1247 
1248 			/* calculate back porch in 'half pixelclocks' */
1249 			/* we always round up because of the h_total truncating 'trick' below,
1250 			 * which works in combination with the existing difference between
1251 			 * h_total_clk and h_total */
1252 			//fixme: prevent this if possible!
1253 			back_porch = ((m_timing.back_porch / ((float)pix_period)) + 1);
1254 			/* value must be even */
1255 			back_porch &= ~0x01;
1256 
1257 			/* calculate h_sync length in 'half pixelclocks' */
1258 			/* we always round up because of the h_total truncating 'trick' below,
1259 			 * which works in combination with the existing difference between
1260 			 * h_total_clk and h_total */
1261 			//fixme: prevent this if possible!
1262 			h_sync_length = ((m_timing.h_sync_length / ((float)pix_period)) + 1);
1263 			/* value must be even */
1264 			h_sync_length &= ~0x01;
1265 
1266 			/* calculate h_total in 'half pixelclocks' */
1267 			h_total = h_display + front_porch + back_porch + h_sync_length;
1268 
1269 			LOG(4,("MAVENTV: TV front_porch is %d clocks\n", front_porch));
1270 			LOG(4,("MAVENTV: TV back_porch is %d clocks\n", back_porch));
1271 			LOG(4,("MAVENTV: TV h_sync_length is %d clocks\n", h_sync_length));
1272 			LOG(4,("MAVENTV: TV h_display is %d clocks \n", h_display));
1273 			LOG(4,("MAVENTV: TV h_total is %d clocks\n", h_total));
1274 
1275 			/* calculate color_burst length in 'half pixelclocks' */
1276 			burst_length = (((m_timing.color_burst /*- 1*/) / ((float)pix_period)) + 0.5);
1277 			LOG(4,("MAVENTV: TV color_burst is %d clocks.\n", burst_length));
1278 
1279 			/* copy result to MAVEN TV mode */
1280 			maventv_regs[0x09] = burst_length;
1281 
1282 			/* Calculate line length 'rest' that remains after truncating
1283 			 * h_total to adhere to the CRTC2 timing restrictions. */
1284 			leftover = h_total & 0x0F;
1285 			/* if some 'rest' exists, we need to compensate for it... */
1286 			/* Note:
1287 			 * It's much better to prevent this from happening because this
1288 			 * 'trick' will decay TVout timing! (timing is nolonger official) */
1289 			if (leftover)
1290 			{
1291 				/* truncate line length to adhere to CRTC2 restrictions */
1292 				front_porch -= leftover;
1293 				h_total -= leftover;
1294 
1295 				/* now set line length to closest CRTC2 valid match */
1296 				if (leftover < 3)
1297 				{
1298 					/* 1 <= old rest <= 2:
1299 					 * Truncated line length is closest match. */
1300 					LOG(4,("MAVENTV: CRTC2 h_total leftover discarded (< 3)\n"));
1301 				}
1302 				else
1303 				{
1304 					if (leftover < 10)
1305 					{
1306 						/* 3 <= old rest <= 9:
1307 						 * We use the NTSC killer circuitry to get closest match.
1308 						 * (The 'g400_crtc2_set_timing' routine will enable it
1309 						 *  because of the illegal h_total timing we create here.) */
1310 						front_porch += 4;
1311 						h_total += 4;
1312 						LOG(4,("MAVENTV: CRTC2 h_total leftover corrected via killer (> 2, < 10)\n"));
1313 					}
1314 					else
1315 					{
1316 						/* 10 <= old rest <= 15:
1317 						 * Set closest valid CRTC2 match. */
1318 						front_porch += 16;
1319 						h_total += 16;
1320 						LOG(4,("MAVENTV: CRTC2 h_total leftover corrected via increase (> 9, < 16)\n"));
1321 					}
1322 				}
1323 			}
1324 
1325 			/* (linux) fixme: maybe MAVEN has requirement 800 < h_total < 1184 */
1326 			maventv_regs[0x2C] = front_porch;
1327 			maventv_regs[0x0A] = back_porch;
1328 			maventv_regs[0x08] = h_sync_length;
1329 
1330 			/* change h_total to represent 'whole pixelclocks' */
1331 			h_total = h_total >> 1;
1332 
1333 			/* tune display_mode adhering to CRTC2 restrictions */
1334 			tv_target.timing.h_sync_end = (h_total & ~0x07) - 8;
1335 			/* h_total is checked before being programmed! (NTSC killer circuitry) */
1336 			tv_target.timing.h_total = h_total;
1337 		}
1338 
1339 		/* output Y/C and CVBS signals (| $40 needed for SCART) */
1340 		DXIW(TVO_IDX, 0x80);
1341 		DXIW(TVO_DATA, 0x03);
1342 
1343 		/* select input colorspace */
1344 		//fixme?: has no effect on output picture on monitor or TV...
1345 		//DXIW(TVO_IDX, 0x81);
1346 		//DXIW(TVO_DATA, 0x00);
1347 
1348 		/* calculate vertical sync point */
1349 		{
1350 			int upper;
1351 
1352 			/* set 625 lines for PAL or 525 lines for NTSC */
1353 			maventv_regs[0x17] = m_timing.v_total / 4;
1354 			maventv_regs[0x18] = m_timing.v_total & 3;
1355 
1356 			/* calculate upper blanking range in field lines */
1357 			upper = (m_timing.v_total - tv_target.timing.v_sync_end) >> 1;
1358 
1359 			/* blank TVout above the line number calculated */
1360 			maventv_regs[0x33] = upper - 1;
1361 
1362 			/* set calculated vertical sync point */
1363 			DXIW(TVO_IDX, 0x82);
1364 			DXIW(TVO_DATA, (upper & 0xff));
1365 			DXIW(TVO_IDX, 0x83);
1366 			DXIW(TVO_DATA, ((upper >> 8) & 0xff));
1367 			LOG(4,("MAVENTV: TV upper blanking range set is %d\n", upper));
1368 		}
1369 
1370 		/* set fized horizontal sync point */
1371 		DXIW(TVO_IDX, 0x84);
1372 		DXIW(TVO_DATA, 0x01);
1373 		DXIW(TVO_IDX, 0x85);
1374 		DXIW(TVO_DATA, 0x00);
1375 
1376 		/* connect DAC1 to CON1, CRTC2/'DAC2' to CON2 (TVout mode) */
1377 		DXIW(OUTPUTCONN,0x0d);
1378 	}
1379 
1380 	/* program new TVout mode */
1381 	for (val = 0x00; val <= 0x3D; val++)
1382 	{
1383 		if (si->ps.card_type <= G400MAX)
1384 		{
1385 			i2c_maven_write(val, maventv_regs[val]);
1386 		}
1387 		else
1388 		{
1389 			DXIW(TVO_IDX, val);
1390 			DXIW(TVO_DATA, maventv_regs[val]);
1391 		}
1392 	}
1393 
1394 	/* leave mode-program mode */
1395 	if (si->ps.card_type <= G400MAX) MAVW(PGM, 0x00);
1396 	else
1397 	{
1398 		DXIW(TVO_IDX, MGAMAV_PGM);
1399 		DXIW(TVO_DATA, 0x00);
1400 
1401 		/* Select 2.0 Volt MAVEN DAC ref. so we have enough contrast/brightness range */
1402 		DXIW(GENIOCTRL, DXIR(GENIOCTRL) | 0x40);
1403 		DXIW(GENIODATA, 0x00);
1404 	}
1405 
1406 	/* setup CRTC timing */
1407 	if (si->ps.secondary_head)
1408 	{
1409 		/* on dualhead cards TVout is always used on CRTC2 */
1410 		g400_crtc2_set_timing(tv_target);
1411 	}
1412 	else
1413 	{
1414 		/* on singlehead cards TVout is always used on CRTC1 */
1415 		gx00_crtc_set_timing(tv_target);
1416 	}
1417 
1418 	/* start whole thing if needed */
1419 	if (si->ps.card_type <= G400MAX) MAVW(RESYNC, 0x20);
1420 
1421 	return 0;
1422 }
1423