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
12typedef 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 */
28status_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) */
298static 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) */
452static 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
608static 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
625static 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
642int 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