xref: /haiku/src/add-ons/accelerants/matrox/engine/mga_maventv.c (revision 01b25646004ff628ecad0281a9795e5e90f71746)
1 /* Authors:
2    Mark Watson 2000,
3    Rudolf Cornelissen 1/2003-12/2003
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: if <=G400 cards with MAVEN and crystal of 14.31818Mhz exist, modify!?!
656 	gx50_maven_timing m_timing;
657 
658 	/* preset new TVout mode */
659 	if ((tv_target.flags & TV_BITS) == TV_PAL)
660 	{
661 		LOG(4, ("MAVENTV: PAL TVout\n"));
662 		gxx0_maventv_PAL_init(maventv_regs);
663 		gx50_maventv_PAL_timing(&m_timing);
664 	}
665 	else
666 	{
667 		LOG(4, ("MAVENTV: NTSC TVout\n"));
668 		gxx0_maventv_NTSC_init(maventv_regs);
669 		gx50_maventv_NTSC_timing(&m_timing);
670 	}
671 
672 	/* enter mode-program mode */
673 	if (si->ps.card_type <= G400MAX) MAVW(PGM, 0x01);
674 	else
675 	{
676 		DXIW(TVO_IDX, MGAMAV_PGM);
677 		DXIW(TVO_DATA, 0x01);
678 	}
679 
680 	/* tune new TVout mode */
681 	if (si->ps.card_type <= G400MAX)
682 	{
683 		/* setup TV-mode 'copy' of CRTC2, setup PLL, inputs, outputs and sync-locks */
684 		MAVW(MONSET, 0x00);
685 		MAVW(MONEN, 0xA2);
686 
687 		/* xmiscctrl */
688 		//unknown regs:
689 		MAVWW(WREG_0X8E_L, 0x1EFF);
690 		MAVW(BREG_0XC6, 0x01);
691 
692 		MAVW(LOCK, 0x01);
693 		MAVW(OUTMODE, 0x08);
694 
695 		/* set high contrast/brightness range for TVout */
696 		/* Note:
697 		 * b4-5 have contrast/brightness function during TVout, while these bits
698 		 * are used to set sync polarity in a serial fashion during monitor modes.
699 		 * Setting both these bits here will 'increase' the sync polarity offset
700 		 * by one! */
701 		MAVW(LUMA, 0x78);
702 		/* We just made a sync polarity programming step for monitor modes, so:
703 		 * note the offset from 'reset position' we will have now */
704 		si->maven_syncpol_offset += 1;
705 		if (si->maven_syncpol_offset > 3) si->maven_syncpol_offset = 0;
706 
707 		//unknown regs:
708 		MAVW(STABLE, 0x02);
709 		MAVW(MONEN, 0xB3);
710 
711 		/* modify mode to center and size correctly on TV */
712 		{
713 			int diff;
714 			float uscan_fact;
715 			bool tweak = false; /* needed for NTSC VCD mode */
716 
717 			if (!(tv_target.flags & TV_VIDEO)) /* Desktop modes */
718 			{
719 				LOG(4,("MAVENTV: setting underscanning ('downscaled') desktop mode\n"));
720 
721 				/* adapt mode to underscan correctly */
722 				if ((tv_target.timing.h_display < 704) && ((tv_target.flags & TV_BITS) == TV_PAL))
723 				{
724 					/* can't be higher because of scaling limitations in MAVEN! */
725 					uscan_fact = 0.77;
726 				}
727 				else
728 				{
729 					/* optimal setting for desktop modes.. */
730 					uscan_fact = 0.80;
731 				}
732 				/* horizontal */
733 				tv_target.timing.h_total = (tv_target.timing.h_display / uscan_fact);
734 				/* adhere to CRTC constraints */
735 				tv_target.timing.h_total &= ~0x0007;
736 				/* vertical */
737 				tv_target.timing.v_total = (tv_target.timing.v_display / uscan_fact);
738 
739 				/* now do vertical centering */
740 				if ((tv_target.flags & TV_BITS) == TV_PAL)
741 				{
742 					diff = tv_target.timing.v_total - tv_target.timing.v_display;
743 					tv_target.timing.v_sync_start = tv_target.timing.v_display + ((diff * 7) / 20);
744 					/* sync all the way 'to the end' to prevent vertical overscanning
745 					 * rubbish on top of screen due to MAVEN hardware design fault */
746 					tv_target.timing.v_sync_end = tv_target.timing.v_total;
747 				}
748 				else
749 				{
750 					diff = tv_target.timing.v_total - tv_target.timing.v_display;
751 					tv_target.timing.v_sync_start = tv_target.timing.v_display + ((diff * 5) / 20);
752 					/* sync all the way 'to the end' to prevent vertical overscanning
753 					 * rubbish on top of screen due to MAVEN hardware design fault */
754 					tv_target.timing.v_sync_end = tv_target.timing.v_total;
755 				}
756 			}
757 			else /* Video modes */
758 			{
759 				uint16 mode =
760 					(((tv_target.flags & TV_BITS) << (14 - 9)) | tv_target.timing.h_display);
761 
762 				LOG(4,("MAVENTV: setting overscanning ('unscaled') video mode\n"));
763 
764 				/* adapt standard modes to be displayed 1:1 */
765 				switch (mode)
766 				{
767 				case ((TV_NTSC << (14 - 9)) | 640): /* NTSC VCD mode */
768 					/* horizontal: adhere to CRTC granularity (8) */
769 					/* Note: h_total = 704 has no PLL match! */
770 					tv_target.timing.h_total = 696;
771 					/* because this 'low' horizontal resolution cannot be scaled up
772 					 * for overscanning use (MAVEN restriction) we need to do some
773 					 * tweaking to get a mode we can work with that still has the
774 					 * correct aspect ratio.
775 					 * This mode has just a little bit horizontal overscanning. */
776 					tweak = true;
777 					break;
778 				case ((TV_NTSC << (14 - 9)) | 720): /* NTSC DVD mode */
779 					/* horizontal: adhere to CRTC granularity (8) */
780 					tv_target.timing.h_total = 784;
781 					break;
782 				case ((TV_PAL << (14 - 9)) | 768): /* PAL VCD mode */
783 					/* horizontal: adhere to CRTC granularity (8) */
784 					tv_target.timing.h_total = 832;
785 					break;
786 				case ((TV_PAL << (14 - 9)) | 720): /* PAL DVD mode */
787 					/* horizontal: adhere to CRTC granularity (8) */
788 					tv_target.timing.h_total = 784;
789 					break;
790 				default:
791 					/* non-standard mode: just hope for he best. */
792 					break;
793 				}
794 
795 				/* now do vertical centering and clipping */
796 				if ((tv_target.flags & TV_BITS) == TV_PAL)
797 				{
798 					/* defined by the PAL standard */
799 					tv_target.timing.v_total = m_timing.v_total;
800 					/* we need to center the image on TV vertically.
801 					 * note that 576 is the maximum supported resolution for the PAL standard,
802 					 * this is already overscanning by approx 8-10% */
803 					diff = 576 - tv_target.timing.v_display;
804 					/* if we cannot display the current vertical resolution fully, clip it */
805 					if (diff < 0)
806 					{
807 						tv_target.timing.v_display = 576;
808 						diff = 0;
809 					}
810 					/* now center the image on TV */
811 					tv_target.timing.v_sync_start = tv_target.timing.v_display + (diff / 2);
812 					/* sync all the way 'to the end' to prevent vertical overscanning
813 					 * rubbish on top of screen due to MAVEN hardware design fault */
814 					/* note: probably invisible in these Video modes */
815 					tv_target.timing.v_sync_end = tv_target.timing.v_total;
816 				}
817 				else
818 				{
819 					/* defined by the NTSC standard */
820 					tv_target.timing.v_total = m_timing.v_total;
821 					/* NTSC VCD mode needs to be scaled down vertically to get correct
822 					 * aspect ratio... */
823 					if (tweak) tv_target.timing.v_total += 32;
824 					/* we need to center the image on TV vertically.
825 					 * note that 480 is the maximum supported resolution for the NTSC standard,
826 					 * this is already overscanning by approx 8-10% */
827 					diff = 480 - tv_target.timing.v_display;
828 					/* if we cannot display the current vertical resolution fully, clip it */
829 					if (diff < 0)
830 					{
831 						tv_target.timing.v_display = 480;
832 						diff = 0;
833 					}
834 					/* now center the image on TV */
835 					tv_target.timing.v_sync_start = tv_target.timing.v_display + (diff / 2);
836 					/* ...NTSC VCD mode needs to be moved up to center the tweaked mode
837 					 * correcty... */
838 					if (tweak) tv_target.timing.v_sync_start += 9;
839 					/* sync all the way 'to the end' to prevent vertical overscanning
840 					 * rubbish on top of screen due to MAVEN hardware design fault */
841 					/* note: might be visible in the NTSC VCD Video mode */
842 					tv_target.timing.v_sync_end = tv_target.timing.v_total;
843 				}
844 			}
845 
846 			/* finally do horizontal centering */
847 			if ((tv_target.flags & TV_BITS) == TV_PAL)
848 			{
849 				diff = tv_target.timing.h_total - tv_target.timing.h_display;
850 				tv_target.timing.h_sync_start = tv_target.timing.h_display - 0 + (diff / 2);
851 				/* keep adhering to CRTC constraints */
852 				tv_target.timing.h_sync_start &= ~0x0007;
853 				tv_target.timing.h_sync_end = tv_target.timing.h_sync_start + 16;
854 			}
855 			else
856 			{
857 				diff = tv_target.timing.h_total - tv_target.timing.h_display;
858 				tv_target.timing.h_sync_start = tv_target.timing.h_display - 16 + (diff / 2);
859 				/* ...and finally the NTSC VCD mode needs to be moved to the right to
860 				 * center the tweaked mode correctly. */
861 				if (tweak) tv_target.timing.h_sync_start -= 16;
862 				/* keep adhering to CRTC constraints */
863 				tv_target.timing.h_sync_start &= ~0x0007;
864 				tv_target.timing.h_sync_end = tv_target.timing.h_sync_start + 16;
865 			}
866 		}
867 
868 		/* tune crtc delay */
869 		if ((tv_target.timing.h_display >= 1000) && (((tv_target.flags & TV_BITS) != TV_PAL)))
870 		{
871 			si->crtc_delay += 1;
872 		}
873 		else
874 		{
875 			si->crtc_delay -= 3;
876 		}
877 
878 		/* setup video PLL */
879 		g100_g400max_maventv_vid_pll_find(
880 			tv_target, &ht_new, &ht_last_line, &m_result, &n_result, &p_result);
881 		MAVW(PIXPLLM, m_result);
882 		MAVW(PIXPLLN, n_result);
883 		MAVW(PIXPLLP, (p_result | 0x80));
884 
885 		MAVW(MONSET, 0x20);
886 
887 		MAVW(TEST, 0x10);
888 
889 		/* htotal - 2 */
890 		MAVWW(HTOTALL, ht_new);
891 
892 		/* last line in field can have different length */
893 		/* hlen - 2 */
894 		MAVWW(LASTLINEL, ht_last_line);
895 
896 		/* horizontal vidrst pos: 0 <= vidrst pos <= htotal - 2 */
897 		MAVWW(HVIDRSTL, (ht_last_line - si->crtc_delay -
898 						(tv_target.timing.h_sync_end - tv_target.timing.h_sync_start)));
899 		//ORG (does the same but with limit checks but these limits should never occur!):
900 //		slen = tv_target.timing.h_sync_end - tv_target.timing.h_sync_start;
901 //		hcrt = tv_target.timing.h_total - slen - si->crtc_delay;
902 //		if (ht_last_line < tv_target.timing.h_total) hcrt += ht_last_line;
903 //		if (hcrt > tv_target.timing.h_total) hcrt -= tv_target.timing.h_total;
904 //		if (hcrt + 2 > tv_target.timing.h_total) hcrt = 0;	/* or issue warning? */
905 //		MAVWW(HVIDRSTL, hcrt);
906 
907 		/* who knows */
908 		MAVWW(HSYNCSTRL, 0x0004);//must be 4!!
909 
910 		/* hblanking end: 100% */
911 		MAVWW(HSYNCLENL, (tv_target.timing.h_total - tv_target.timing.h_sync_end));
912 
913 		/* vertical line count - 1 */
914 		MAVWW(VTOTALL, (tv_target.timing.v_total - 1));
915 
916 		/* vertical vidrst pos */
917 		MAVWW(VVIDRSTL, (tv_target.timing.v_total - 2));
918 
919 		/* something end... [A6]+1..[A8] */
920 		MAVWW(VSYNCSTRL, 0x0001);
921 
922 		/* vblanking end: stop vblanking */
923 		MAVWW(VSYNCLENL, (tv_target.timing.v_sync_end - tv_target.timing.v_sync_start - 1));
924 		//org: no visible diff:
925 		//MAVWW(VSYNCLENL, (tv_target.timing.v_total - tv_target.timing.v_sync_start - 1));
926 
927 		/* something start... 0..[A4]-1 */
928 		MAVWW(VDISPLAYL, 0x0000);
929 		//std setmode (no visible difference)
930 		//MAVWW(VDISPLAYL, (tv_target.timing.v_total - 1));
931 
932 		/* ... */
933 		MAVWW(WREG_0X98_L, 0x0000);
934 
935 		/* moves picture up/down and so on... */
936 		MAVWW(VSOMETHINGL, 0x0001); /* Fix this... 0..VTotal */
937 
938 		{
939 			uint32 h_display_tv;
940 			uint8 h_scale_tv;
941 
942 			unsigned int ib_min_length;
943 			unsigned int ib_length;
944 			int index;
945 
946 			/* calc hor scale-back factor from input to output picture (in 1.7 format)
947 			 * the MAVEN has 736 pixels fixed total outputline length for TVout */
948 			h_scale_tv = (736 << 7) / tv_target.timing.h_total;//should be PLL corrected
949 			LOG(4,("MAVENTV: horizontal scale-back factor is: %f\n", (h_scale_tv / 128.0)));
950 
951 			/* limit values to MAVEN capabilities (scale-back factor is 0.5-1.0) */
952 			if (h_scale_tv > 0x80)
953 			{
954 				h_scale_tv = 0x80;
955 				LOG(4,("MAVENTV: limiting horizontal scale-back factor to: %f\n", (h_scale_tv / 128.0)));
956 			}
957 			if (h_scale_tv < 0x40)
958 			{
959 				h_scale_tv = 0x40;
960 				LOG(4,("MAVENTV: limiting horizontal scale-back factor to: %f\n", (h_scale_tv / 128.0)));
961 			}
962 			/* make sure we get no round-off error artifacts on screen */
963 			h_scale_tv--;
964 
965 			/* calc difference in (wanted output picture width (excl. hsync_length)) and
966 			 * (fixed total output line length (=768)),
967 			 * based on input picture and scaling factor */
968 			/* (MAVEN trick (part 1) to get output picture width to fit into just 8 bits) */
969 			h_display_tv = ((768 - 1) << 7) -
970 				(((tv_target.timing.h_total - tv_target.timing.h_sync_end)	/* is left margin */
971 				 + tv_target.timing.h_display - 8)
972 				 * h_scale_tv);
973 			/* convert result from 25.7 to 32.0 format */
974 			h_display_tv = h_display_tv >> 7;
975 			LOG(4,("MAVENTV: displaying output on %d picture pixels\n",
976 				((768 - 1) - h_display_tv)));
977 
978 			/* half result: MAVEN trick (part 2)
979 			 * (258 - 768 pixels, only even number per line is possible) */
980 			h_display_tv = h_display_tv >> 1;
981 			/* limit value to register contraints */
982 			if (h_display_tv > 0xFF) h_display_tv = 0xFF;
983 			MAVW(HSCALETV, h_scale_tv);
984 			MAVW(HDISPLAYTV, h_display_tv);
985 
986 
987 			/* calculate line inputbuffer length */
988 			/* It must be between (including):
989 			 * ((input picture left margin) + (input picture hor. resolution) + 4)
990 			 * AND
991 			 * (input picture total line length) (PLL corrected) */
992 
993 			/* calculate minimal line input buffer length */
994 			ib_min_length = ((tv_target.timing.h_total - tv_target.timing.h_sync_end) +
995 				 			  tv_target.timing.h_display + 4);
996 
997 			/* calculate optimal line input buffer length (so top of picture is OK too) */
998 			/* The following formula applies:
999 			 * optimal buffer length = ((((0x78 * i) - R) / hor. scaling factor) + Q)
1000 			 *
1001 			 * where (in 4.8 format!)
1002 		     * R      Qmin  Qmax
1003 			 * 0x0E0  0x5AE 0x5BF
1004 			 * 0x100  0x5CF 0x5FF
1005 			 * 0x180  0x653 0x67F
1006 			 * 0x200  0x6F8 0x6FF
1007 			 */
1008 			index = 1;
1009 			do
1010 			{
1011 				ib_length = ((((((0x7800 << 7) * index) - (0x100 << 7)) / h_scale_tv) + 0x05E7) >> 8);
1012 				index++;
1013 			} while (ib_length < ib_min_length);
1014 			LOG(4,("MAVENTV: optimal line inputbuffer length: %d\n", ib_length));
1015 
1016 			if (ib_length >= ht_new + 2)
1017 			{
1018 				ib_length = ib_min_length;
1019 				LOG(4,("MAVENTV: limiting line inputbuffer length, setting minimal usable: %d\n", ib_length));
1020 			}
1021 			MAVWW(HDISPLAYL, ib_length);
1022 		}
1023 
1024 		{
1025 			uint16 t_scale_tv;
1026 			uint32 v_display_tv;
1027 
1028 			/* calc total scale-back factor from input to output picture */
1029 			{
1030 				uint32 out_clocks;
1031 				uint32 in_clocks;
1032 
1033 				//takes care of green stripes:
1034 				/* calc output clocks per frame */
1035 				out_clocks = m_timing.v_total * (ht_new + 2);
1036 
1037 				/* calc input clocks per frame */
1038 				in_clocks = (tv_target.timing.v_total - 1) * (ht_new + 2) +	ht_last_line + 2;
1039 
1040 				/* calc total scale-back factor from input to output picture in 1.15 format */
1041 				t_scale_tv = ((((uint64)out_clocks) << 15) / in_clocks);
1042 				LOG(4,("MAVENTV: total scale-back factor is: %f\n", (t_scale_tv / 32768.0)));
1043 
1044 				/* min. scale-back factor is 1.0 for 1:1 output */
1045 				if (t_scale_tv > 0x8000)
1046 				{
1047 					t_scale_tv = 0x8000;
1048 					LOG(4,("MAVENTV: limiting total scale-back factor to: %f\n", (t_scale_tv / 32768.0)));
1049 				}
1050 			}
1051 
1052 			/*calc output picture height based on input picture and scaling factor */
1053 			//warning: v_display was 'one' lower originally!
1054 			v_display_tv =
1055 				((tv_target.timing.v_sync_end - tv_target.timing.v_sync_start) 	/* is sync length */
1056 				 + (tv_target.timing.v_total - tv_target.timing.v_sync_end) 	/* is upper margin */
1057 				 + tv_target.timing.v_display)
1058 				 * t_scale_tv;
1059 			/* convert result from 17.15 to 32.0 format */
1060 			v_display_tv = (v_display_tv >> 15);
1061 			LOG(4,("MAVENTV: displaying output on %d picture frame-lines\n", v_display_tv));
1062 
1063 			/* half result, and compensate for internal register offset
1064 			 * (MAVEN trick to get it to fit into just 8 bits).
1065 			 * (allowed output frame height is 292 - 802 lines, only even numbers) */
1066 			v_display_tv = (v_display_tv >> 1) - 146;
1067 			/* limit value to register contraints */
1068 			if (v_display_tv > 0xFF) v_display_tv = 0xFF;
1069 			/* make sure we get no round-off error artifacts on screen */
1070 			t_scale_tv--;
1071 
1072 			MAVWW(TSCALETVL, t_scale_tv);
1073 			MAVW(VDISPLAYTV, v_display_tv);
1074 		}
1075 
1076 		MAVW(TEST, 0x00);
1077 
1078 		/* gamma correction registers */
1079 		MAVW(GAMMA1, 0x00);
1080 		MAVW(GAMMA2, 0x00);
1081 		MAVW(GAMMA3, 0x00);
1082 		MAVW(GAMMA4, 0x1F);
1083 		MAVW(GAMMA5, 0x10);
1084 		MAVW(GAMMA6, 0x10);
1085 		MAVW(GAMMA7, 0x10);
1086 		MAVW(GAMMA8, 0x64);	/* 100 */
1087 		MAVW(GAMMA9, 0xC8);	/* 200 */
1088 
1089 		/* set flickerfilter */
1090 		if (!(tv_target.flags & TV_VIDEO))
1091 		{
1092 			/* Desktop modes (those are scaled): filter on to prevent artifacts */
1093 			MAVW(FFILTER, 0xa2);
1094 			LOG(4,("MAVENTV: enabling flicker filter\n"));
1095 		}
1096 		else
1097 		{
1098 			/* Video modes (those are unscaled): filter off to increase sharpness */
1099 			//fixme? OFF is dependant on MAVEN version(?): MGA_TVO_B = $40, else $00.
1100 			MAVW(FFILTER, 0x00);
1101 			LOG(4,("MAVENTV: disabling flicker filter\n"));
1102 		}
1103 
1104 		/* 0x10 or anything ored with it */
1105 		//fixme? linux uses 0x14...
1106 		MAVW(TEST, (MAVR(TEST) & 0x10));
1107 
1108 		/* output: SVideo/Composite */
1109 		MAVW(OUTMODE, 0x08);
1110 	}
1111 	else /* card_type is >= G450 */
1112 	{
1113 		//fixme: setup an intermediate buffer if vertical res is different than settings below!
1114 		//fixme: setup 2D or 3D engine to do screen_to_screen_scaled_filtered_blit between the buffers
1115 		//       during vertical retrace!
1116 		if ((tv_target.flags & TV_BITS) == TV_PAL)
1117 		{
1118 			int diff;
1119 
1120 			/* defined by the PAL standard */
1121 			tv_target.timing.v_total = m_timing.v_total;
1122 			/* we need to center the image on TV vertically.
1123 			 * note that 576 is the maximum supported resolution for the PAL standard,
1124 			 * this is already overscanning by approx 8-10% */
1125 			diff = 576 - tv_target.timing.v_display;
1126 			/* if we cannot display the current vertical resolution fully, clip it */
1127 			if (diff < 0)
1128 			{
1129 				tv_target.timing.v_display = 576;
1130 				diff = 0;
1131 			}
1132 			/* now center the image on TV by centering the vertical sync pulse */
1133 			tv_target.timing.v_sync_start = tv_target.timing.v_display + 1 + (diff / 2);
1134 			tv_target.timing.v_sync_end = tv_target.timing.v_sync_start + 1;
1135 		}
1136 		else
1137 		{
1138 			int diff;
1139 
1140 			/* defined by the NTSC standard */
1141 			tv_target.timing.v_total = m_timing.v_total;
1142 			/* we need to center the image on TV vertically.
1143 			 * note that 480 is the maximum supported resolution for the NTSC standard,
1144 			 * this is already overscanning by approx 8-10% */
1145 			diff = 480 - tv_target.timing.v_display;
1146 			/* if we cannot display the current vertical resolution fully, clip it */
1147 			if (diff < 0)
1148 			{
1149 				tv_target.timing.v_display = 480;
1150 				diff = 0;
1151 			}
1152 			/* now center the image on TV by centering the vertical sync pulse */
1153 			tv_target.timing.v_sync_start = tv_target.timing.v_display + 1 + (diff / 2);
1154 			tv_target.timing.v_sync_end = tv_target.timing.v_sync_start + 1;
1155 		}
1156 
1157 		/* setup video PLL for G450/G550:
1158 		 * this can be done in the normal way because the MAVEN works in slave mode!
1159 		 * NOTE: must be done before programming CRTC2, or interlaced startup may fail. */
1160 
1161 		//fixme: make sure videoPLL is powered up: XPWRCTRL b1=1
1162 		{
1163 			uint16 front_porch, back_porch, h_sync_length, burst_length, h_total, h_display;
1164 			uint32 chromasc;
1165 			uint64 pix_period;
1166 			uint16 h_total_wanted, leftover;
1167 
1168 			/* calculate tv_h_display in 'half pixelclocks' and adhere to MAVEN restrictions.
1169 			 * ('half pixelclocks' exist because the MAVEN uses them...) */
1170 			h_display = (((tv_target.timing.h_display << 1) + 3) & ~0x03);
1171 			if (h_display > 2044) h_display = 2044;
1172 			/* copy result to MAVEN TV mode */
1173 			maventv_regs[0x31] = (h_display >> 3);
1174 			maventv_regs[0x32] = (h_display & 0x07);
1175 
1176 			/* calculate needed video pixelclock in kHz.
1177 			 * NOTE:
1178 			 * The clock calculated is based on MAVEN output, so each pixelclock period
1179 			 * is in fact a 'half pixelclock' period compared to monitor mode use. */
1180 			tv_target.timing.pixel_clock =
1181 				((((uint64)h_display) * 1000000000) / m_timing.h_display);
1182 
1183 			/* tune display_mode adhering to CRTC2 restrictions */
1184 			/* (truncate h_display to 'whole pixelclocks') */
1185 			tv_target.timing.h_display = ((h_display >> 1) & ~0x07);
1186 			tv_target.timing.h_sync_start = tv_target.timing.h_display + 8;
1187 
1188 			g450_g550_maven_vid_pll_find(tv_target, &calc_pclk, &m_result, &n_result, &p_result, 1);
1189 			/* adjust mode to actually used pixelclock */
1190 			tv_target.timing.pixel_clock = (calc_pclk * 1000);
1191 
1192 			/* program videoPLL */
1193 			DXIW(VIDPLLM, m_result);
1194 			DXIW(VIDPLLN, n_result);
1195 			DXIW(VIDPLLP, p_result);
1196 
1197 			/* calculate videoclock 'half' period duration in picoseconds */
1198 			pix_period = (1000000000 / ((float)tv_target.timing.pixel_clock)) + 0.5;
1199 			LOG(4,("MAVENTV: TV videoclock period is %d picoseconds\n", pix_period));
1200 
1201 			/* calculate number of 'half' clocks per line according to pixelclock set */
1202 			/* fixme: try to setup the modes in such a way that
1203 			 * (h_total_clk % 16) == 0 because of the CRTC2 restrictions:
1204 			 * we want to loose the truncating h_total trick below if possible! */
1205 			/* Note:
1206 			 * This is here so we can see the wanted and calc'd timing difference. */
1207 			h_total_wanted = ((m_timing.h_total / ((float)pix_period)) + 0.5);
1208 			LOG(4,("MAVENTV: TV h_total should be %d units\n", h_total_wanted));
1209 
1210 			/* calculate chroma subcarrier value to setup:
1211 			 * do this as exact as possible because this signal is very sensitive.. */
1212 			chromasc =
1213 				((((uint64)0x100000000) * (m_timing.chroma_subcarrier / calc_pclk)) + 0.5);
1214 			/* copy result to MAVEN TV mode */
1215 			maventv_regs[0] = ((chromasc >> 24) & 0xff);
1216 			maventv_regs[1] = ((chromasc >> 16) & 0xff);
1217 			maventv_regs[2] = ((chromasc >>  8) & 0xff);
1218 			maventv_regs[3] = ((chromasc >>  0) & 0xff);
1219 			LOG(4,("MAVENTV: TV chroma subcarrier divider set is $%08x\n", chromasc));
1220 
1221 			/* calculate front porch in 'half pixelclocks' */
1222 			/* we always round up because of the h_total truncating 'trick' below,
1223 			 * which works in combination with the existing difference between
1224 			 * h_total_clk and h_total */
1225 			//fixme: prevent this if possible!
1226 			front_porch = ((m_timing.front_porch / ((float)pix_period)) + 1);
1227 			/* value must be even */
1228 			front_porch &= ~0x01;
1229 
1230 			/* calculate back porch in 'half pixelclocks' */
1231 			/* we always round up because of the h_total truncating 'trick' below,
1232 			 * which works in combination with the existing difference between
1233 			 * h_total_clk and h_total */
1234 			//fixme: prevent this if possible!
1235 			back_porch = ((m_timing.back_porch / ((float)pix_period)) + 1);
1236 			/* value must be even */
1237 			back_porch &= ~0x01;
1238 
1239 			/* calculate h_sync length 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 			h_sync_length = ((m_timing.h_sync_length / ((float)pix_period)) + 1);
1245 			/* value must be even */
1246 			h_sync_length &= ~0x01;
1247 
1248 			/* calculate h_total in 'half pixelclocks' */
1249 			h_total = h_display + front_porch + back_porch + h_sync_length;
1250 
1251 			LOG(4,("MAVENTV: TV front_porch is %d clocks\n", front_porch));
1252 			LOG(4,("MAVENTV: TV back_porch is %d clocks\n", back_porch));
1253 			LOG(4,("MAVENTV: TV h_sync_length is %d clocks\n", h_sync_length));
1254 			LOG(4,("MAVENTV: TV h_display is %d clocks \n", h_display));
1255 			LOG(4,("MAVENTV: TV h_total is %d clocks\n", h_total));
1256 
1257 			/* calculate color_burst length in 'half pixelclocks' */
1258 			burst_length = (((m_timing.color_burst /*- 1*/) / ((float)pix_period)) + 0.5);
1259 			LOG(4,("MAVENTV: TV color_burst is %d clocks.\n", burst_length));
1260 
1261 			/* copy result to MAVEN TV mode */
1262 			maventv_regs[0x09] = burst_length;
1263 
1264 			/* Calculate line length 'rest' that remains after truncating
1265 			 * h_total to adhere to the CRTC2 timing restrictions. */
1266 			leftover = h_total & 0x0F;
1267 			/* if some 'rest' exists, we need to compensate for it... */
1268 			/* Note:
1269 			 * It's much better to prevent this from happening because this
1270 			 * 'trick' will decay TVout timing! (timing is nolonger official) */
1271 			if (leftover)
1272 			{
1273 				/* truncate line length to adhere to CRTC2 restrictions */
1274 				front_porch -= leftover;
1275 				h_total -= leftover;
1276 
1277 				/* now set line length to closest CRTC2 valid match */
1278 				if (leftover < 3)
1279 				{
1280 					/* 1 <= old rest <= 2:
1281 					 * Truncated line length is closest match. */
1282 					LOG(4,("MAVENTV: CRTC2 h_total leftover discarded (< 3)\n"));
1283 				}
1284 				else
1285 				{
1286 					if (leftover < 10)
1287 					{
1288 						/* 3 <= old rest <= 9:
1289 						 * We use the NTSC killer circuitry to get closest match.
1290 						 * (The 'g400_crtc2_set_timing' routine will enable it
1291 						 *  because of the illegal h_total timing we create here.) */
1292 						front_porch += 4;
1293 						h_total += 4;
1294 						LOG(4,("MAVENTV: CRTC2 h_total leftover corrected via killer (> 2, < 10)\n"));
1295 					}
1296 					else
1297 					{
1298 						/* 10 <= old rest <= 15:
1299 						 * Set closest valid CRTC2 match. */
1300 						front_porch += 16;
1301 						h_total += 16;
1302 						LOG(4,("MAVENTV: CRTC2 h_total leftover corrected via increase (> 9, < 16)\n"));
1303 					}
1304 				}
1305 			}
1306 
1307 			/* (linux) fixme: maybe MAVEN has requirement 800 < h_total < 1184 */
1308 			maventv_regs[0x2C] = front_porch;
1309 			maventv_regs[0x0A] = back_porch;
1310 			maventv_regs[0x08] = h_sync_length;
1311 
1312 			/* change h_total to represent 'whole pixelclocks' */
1313 			h_total = h_total >> 1;
1314 
1315 			/* tune display_mode adhering to CRTC2 restrictions */
1316 			tv_target.timing.h_sync_end = (h_total & ~0x07) - 8;
1317 			/* h_total is checked before being programmed! (NTSC killer circuitry) */
1318 			tv_target.timing.h_total = h_total;
1319 		}
1320 
1321 		/* output Y/C and CVBS signals (| $40 needed for SCART) */
1322 		DXIW(TVO_IDX, 0x80);
1323 		DXIW(TVO_DATA, 0x03);
1324 
1325 		/* select input colorspace */
1326 		//fixme?: has no effect on output picture on monitor or TV...
1327 		//DXIW(TVO_IDX, 0x81);
1328 		//DXIW(TVO_DATA, 0x00);
1329 
1330 		/* calculate vertical sync point */
1331 		{
1332 			int upper;
1333 
1334 			/* set 625 lines for PAL or 525 lines for NTSC */
1335 			maventv_regs[0x17] = m_timing.v_total / 4;
1336 			maventv_regs[0x18] = m_timing.v_total & 3;
1337 
1338 			/* calculate upper blanking range in field lines */
1339 			upper = (m_timing.v_total - tv_target.timing.v_sync_end) >> 1;
1340 
1341 			/* blank TVout above the line number calculated */
1342 			maventv_regs[0x33] = upper - 1;
1343 
1344 			/* set calculated vertical sync point */
1345 			DXIW(TVO_IDX, 0x82);
1346 			DXIW(TVO_DATA, (upper & 0xff));
1347 			DXIW(TVO_IDX, 0x83);
1348 			DXIW(TVO_DATA, ((upper >> 8) & 0xff));
1349 			LOG(4,("MAVENTV: TV upper blanking range set is %d\n", upper));
1350 		}
1351 
1352 		/* set fized horizontal sync point */
1353 		DXIW(TVO_IDX, 0x84);
1354 		DXIW(TVO_DATA, 0x01);
1355 		DXIW(TVO_IDX, 0x85);
1356 		DXIW(TVO_DATA, 0x00);
1357 
1358 		/* connect DAC1 to CON1, CRTC2/'DAC2' to CON2 (TVout mode) */
1359 		DXIW(OUTPUTCONN,0x0d);
1360 	}
1361 
1362 	/* program new TVout mode */
1363 	for (val = 0x00; val <= 0x3D; val++)
1364 	{
1365 		if (si->ps.card_type <= G400MAX)
1366 		{
1367 			i2c_maven_write(val, maventv_regs[val]);
1368 		}
1369 		else
1370 		{
1371 			DXIW(TVO_IDX, val);
1372 			DXIW(TVO_DATA, maventv_regs[val]);
1373 		}
1374 	}
1375 
1376 	/* leave mode-program mode */
1377 	if (si->ps.card_type <= G400MAX) MAVW(PGM, 0x00);
1378 	else
1379 	{
1380 		DXIW(TVO_IDX, MGAMAV_PGM);
1381 		DXIW(TVO_DATA, 0x00);
1382 
1383 		/* Select 2.0 Volt MAVEN DAC ref. so we have enough contrast/brightness range */
1384 		DXIW(GENIOCTRL, DXIR(GENIOCTRL) | 0x40);
1385 		DXIW(GENIODATA, 0x00);
1386 	}
1387 
1388 	/* setup CRTC2 timing */
1389 	g400_crtc2_set_timing(tv_target);
1390 
1391 	/* start whole thing if needed */
1392 	if (si->ps.card_type <= G400MAX) MAVW(RESYNC, 0x20);
1393 
1394 	return 0;
1395 }
1396