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