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