xref: /haiku/src/add-ons/accelerants/radeon/impactv.c (revision 1e36cfc2721ef13a187c6f7354dc9cbc485e89d3)
1 /*
2 	Copyright (c) 2002-04, Thomas Kurschel
3 
4 
5 	Part of Radeon accelerant
6 
7 	ImpacTV programming. As this unit is contained in various chips,
8 	the code to actually access the unit is separated.
9 */
10 
11 #include "radeon_interface.h"
12 #include "radeon_accelerant.h"
13 
14 #include "tv_out_regs.h"
15 #include "utils.h"
16 #include "set_mode.h"
17 
18 #include <string.h>
19 
20 
21 // fixed-point resolution of UV scaler increment
22 #define TV_UV_INC_FIX_SHIFT 14
23 #define TV_UV_INC_FIX_SCALE (1 << TV_UV_INC_FIX_SHIFT)
24 
25 // fixed point resolution of UV scaler initialization (uv_accum_init)
26 #define TV_UV_INIT_FIX_SHIFT 6
27 
28 
29 // calculate time when TV timing must be restarted
30 static void Radeon_CalcImpacTVRestart(
31 	impactv_params *params, const display_mode *mode,
32 	uint16 h_blank, uint16 f_total )
33 {
34 	uint32 h_first, v_first = 0, f_first;
35 	uint32 tmp_uv_accum_sum;
36 	uint16 uv_accum_frac, uv_accum_int;
37 	int line;
38 	uint32 how_early = 0;
39 	int32 first_num, restart_to_first_active_pixel_to_FIFO;
40 	uint32 time_to_active;
41 
42 	// this is all black magic - you are not supposed to understand this
43 	h_first = 9;
44 	f_first = 0;
45 
46 	tmp_uv_accum_sum = params->uv_accum_init << (TV_UV_INC_FIX_SHIFT - TV_UV_INIT_FIX_SHIFT);
47 	uv_accum_frac = tmp_uv_accum_sum & (TV_UV_INC_FIX_SCALE - 1);
48 	uv_accum_int = (tmp_uv_accum_sum >> TV_UV_INC_FIX_SHIFT) & 7;
49 
50 	// at line disp + 18 the accumulator is initialized;
51 	// simulate timing during vertical blank and find the last CRT line where
52 	// a new TV line is started
53 	// (actually, I think this calculation is wrong)
54 	for( line = mode->timing.v_display - 1 + 18; line < mode->timing.v_total - 2; ++line ) {
55 		if( uv_accum_int > 0 ) {
56 			--uv_accum_int;
57 		} else {
58 			v_first = line + 1;
59 			how_early = uv_accum_frac * mode->timing.h_total;
60 			uv_accum_int = ((uv_accum_frac + params->uv_inc) >> TV_UV_INC_FIX_SHIFT) - 1;
61 			uv_accum_frac = (uv_accum_frac + params->uv_inc) & (TV_UV_INC_FIX_SCALE - 1);
62 		}
63 	}
64 
65 	SHOW_FLOW( 3, "f_first=%d, v_first=%d, h_first=%d", f_first, v_first, h_first );
66 
67 	// theoretical time when restart should be started
68 	first_num =
69 		f_first * mode->timing.v_total * mode->timing.h_total
70 		+ v_first * mode->timing.h_total
71 		+ h_first;
72 
73 	first_num += (how_early + TV_UV_INC_FIX_SCALE / 2) >> TV_UV_INC_FIX_SHIFT;
74 
75 	SHOW_FLOW( 3, "first_num=%d", first_num );
76 
77 	// TV logic needs extra clocks to restart
78 	//params->tv_clocks_to_active = 0;
79 	time_to_active = params->tv_clocks_to_active + 3;
80 
81 	SHOW_FLOW( 3, "time_to_active=%d, crt_freq=%d, tv_freq=%d",
82 		time_to_active, params->crt_dividers.freq, params->tv_dividers.freq );
83 
84 	// get delay until first bytes can be read from FIFO
85 	restart_to_first_active_pixel_to_FIFO =
86 		(int)(
87 			(int64)time_to_active * params->crt_dividers.freq / params->tv_dividers.freq
88 			- (int64)h_blank * params->crt_dividers.freq / params->tv_dividers.freq / 2)
89 		- mode->timing.h_display / 2
90 		+ mode->timing.h_total / 2;
91 
92 	// do restart a bit early to compensate delays
93 	first_num -= restart_to_first_active_pixel_to_FIFO;
94 
95 	SHOW_FLOW( 3, "restart_to_first_active_pixel_to_FIFO=%d", restart_to_first_active_pixel_to_FIFO );
96 
97 	SHOW_FLOW( 3, "after delay compensation first_num=%d", first_num );
98 
99 	//first_num = 625592;
100 
101 	// make restart time positive
102 	// ("%" operator doesn't like negative numbers)
103     first_num += f_total * mode->timing.v_total * mode->timing.h_total;
104 
105 	//SHOW_FLOW( 2, "first_num=%d", first_num );
106 
107 	// convert clocks to screen position
108 	params->f_restart = (first_num / (mode->timing.v_total * mode->timing.h_total)) % f_total;
109 	first_num %= mode->timing.v_total * mode->timing.h_total;
110 	params->v_restart = (first_num / mode->timing.h_total) % mode->timing.v_total;
111 	first_num %= mode->timing.h_total;
112 	params->h_restart = first_num;
113 
114 	/*params->v_restart = 623;
115 	params->h_restart = 580;*/
116 
117 	SHOW_FLOW( 2, "Restart in frame %d, line %d, pixel %d",
118 		params->f_restart, params->v_restart, params->h_restart );
119 }
120 
121 
122 // thresholds for flicker fixer algorithm
123 static int8 y_flicker_removal[5] = { 6, 5, 4, 3, 2 };
124 
125 // associated filter parameters scaled by 8(!)
126 static int8 y_saw_tooth_slope[5] = { 1, 2, 2, 4, 8 };
127 // these values are not scaled
128 static int8 y_coeff_value[5] = { 2, 2, 0, 4, 0 };
129 static bool y_coeff_enable[5] = { 1, 1, 0, 1, 0 };
130 
131 #define countof( a ) (sizeof( (a) ) / sizeof( (a)[0] ))
132 
133 // fixed point resolution of saw filter parameters
134 #define TV_SAW_FILTER_FIX_SHIFT 13
135 #define TV_SAW_FILTER_FIX_SCALE (1 << TV_SAW_FILTER_FIX_SHIFT)
136 
137 // fixed point resolution of flat filter parameter
138 #define TV_Y_COEFF_FIX_SHIFT 8
139 #define TV_Y_COEFF_FIX_SCALE (1 << TV_Y_COEFF_FIX_SHIFT)
140 
141 
142 // calculate flicker fixer parameters
143 static void Radeon_CalcImpacTVFlickerFixer(
144 	impactv_params *params )
145 {
146 	int flicker_removal;
147 	uint i;
148 	int lower_border, upper_border;
149 
150 	// flicker_removal must be within [uv_inc..uv_inc*2); take care of fraction
151 	lower_border = ((params->uv_inc + TV_UV_INC_FIX_SCALE - 1) >> TV_UV_INC_FIX_SHIFT);
152 	upper_border = ((2 * params->uv_inc) >> TV_UV_INC_FIX_SHIFT);
153 
154 	for( i = 0; i < countof( y_flicker_removal ); ++i ) {
155 		if( lower_border <= y_flicker_removal[i] &&
156 			upper_border > y_flicker_removal[i] )
157 			break;
158 	}
159 
160 	// use least aggresive filtering if not in list
161 	if( i >= countof( y_flicker_removal ))
162 		i = countof( y_flicker_removal ) - 1;
163 
164 	flicker_removal = y_flicker_removal[i];
165 
166 	SHOW_FLOW( 3, "flicker removal=%d", flicker_removal );
167 
168 	params->y_saw_tooth_slope = y_saw_tooth_slope[i] * (TV_SAW_FILTER_FIX_SCALE / 8);
169 	params->y_saw_tooth_amp = ((uint32)params->y_saw_tooth_slope * params->uv_inc) >> TV_UV_INC_FIX_SHIFT;
170 	params->y_fall_accum_init = ((uint32)params->y_saw_tooth_slope * params->uv_accum_init) >> TV_UV_INIT_FIX_SHIFT;
171 
172 	SHOW_FLOW( 3, "%d < %d ?",
173 		(flicker_removal << 16) - ((int32)params->uv_inc << (16 - TV_UV_INC_FIX_SHIFT)),
174 		((int32)params->uv_accum_init << (16 - TV_UV_INIT_FIX_SHIFT)) );
175 
176 	if( (flicker_removal << 16) - ((int32)params->uv_inc << (16 - TV_UV_INC_FIX_SHIFT))
177 		< ((int32)params->uv_accum_init << (16 - TV_UV_INIT_FIX_SHIFT)))
178 	{
179 		params->y_rise_accum_init =
180 			(((flicker_removal << TV_UV_INIT_FIX_SHIFT) - params->uv_accum_init) *
181 			params->y_saw_tooth_slope) >> TV_UV_INIT_FIX_SHIFT;
182 	} else {
183 		params->y_rise_accum_init =
184 			(((flicker_removal << TV_UV_INIT_FIX_SHIFT) - params->uv_accum_init - params->y_accum_init) *
185 			params->y_saw_tooth_slope) >> TV_UV_INIT_FIX_SHIFT;
186 	}
187 
188 	params->y_coeff_enable = y_coeff_enable[i];
189 	params->y_coeff_value = y_coeff_value[i] * TV_Y_COEFF_FIX_SCALE / 8;
190 }
191 
192 
193 // correct sync position after tweaking total size
194 static void Radeon_AdoptSync(
195 	const display_mode *mode, display_mode *tweaked_mode )
196 {
197 	uint16
198 		h_over_plus, h_sync_width, tweaked_h_over_plus,
199 		v_over_plus, v_sync_width, tweaked_v_over_plus;
200 
201 	h_over_plus = mode->timing.h_sync_start - mode->timing.h_display;
202 	h_sync_width = mode->timing.h_sync_end - mode->timing.h_sync_start;
203 
204 	// we want start of sync at same relative position of blank
205 	tweaked_h_over_plus = (uint32)h_over_plus *
206 		(tweaked_mode->timing.h_total - mode->timing.h_display - h_sync_width ) /
207 		(mode->timing.h_total - mode->timing.h_display - h_sync_width);
208 
209 	tweaked_mode->timing.h_sync_start = mode->timing.h_display + tweaked_h_over_plus;
210 	tweaked_mode->timing.h_sync_end = tweaked_mode->timing.h_sync_start + h_sync_width;
211 
212 	v_over_plus = mode->timing.v_sync_start - mode->timing.v_display;
213 	v_sync_width = mode->timing.v_sync_end - mode->timing.v_sync_start;
214 
215 	tweaked_v_over_plus = (uint32)v_over_plus *
216 		(tweaked_mode->timing.v_total - mode->timing.v_display - v_sync_width ) /
217 		(mode->timing.v_total - mode->timing.v_display - v_sync_width);
218 
219 	// we really should verify whether the resulting mode is still valid;
220 	// this is a start
221 	tweaked_v_over_plus = min( 1, tweaked_v_over_plus );
222 
223 	tweaked_mode->timing.v_sync_start = mode->timing.v_display + tweaked_v_over_plus;
224 	tweaked_mode->timing.v_sync_end = tweaked_mode->timing.v_sync_start + v_sync_width;
225 }
226 
227 
228 static const uint16 hor_timing_NTSC[RADEON_TV_TIMING_SIZE] = {
229 	// moved to left as much as possible
230 	0x0007, 0x003f, 0x0263, 0x0a24, 0x2a6b, 0x0a36, 0x126d-100, 0x1bfe,
231 	0x1a8f+100, 0x1ec7, 0x3863, 0x1bfe, 0x1bfe, 0x1a2a, 0x1e95, 0x0e31,
232 	0x201b, 0
233 };
234 
235 static const uint16 vert_timing_NTSC[RADEON_TV_TIMING_SIZE] = {
236 	0x2001, 0x200d, 0x1006, 0x0c06, 0x1006, 0x1818, 0x21e3, 0x1006,
237 	0x0c06, 0x1006, 0x1817, 0x21d4, 0x0002, 0
238 };
239 
240 static const uint16 hor_timing_PAL[RADEON_TV_TIMING_SIZE] = {
241 	0x0007, 0x0058, 0x027c, 0x0a31, 0x2a77, 0x0a95, 0x124f - 60, 0x1bfe,
242 	0x1b22 + 60, 0x1ef9, 0x387c, 0x1bfe, 0x1bfe, 0x1b31, 0x1eb5, 0x0e43,
243 	0x201b, 0
244 };
245 
246 static const uint16 vert_timing_PAL[RADEON_TV_TIMING_SIZE] = {
247 	0x2001, 0x200c, 0x1005, 0x0c05, 0x1005, 0x1401, 0x1821, 0x2240,
248 	0x1005, 0x0c05, 0x1005, 0x1401, 0x1822, 0x2230, 0x0002, 0
249 };
250 
251 static const uint16 *hor_timings[] = {
252 	hor_timing_NTSC,
253 	hor_timing_PAL,
254 	hor_timing_NTSC,		// palm: looks similar to NTSC, but differs slightly
255 	hor_timing_NTSC,		// palnc: looks a bit like NTSC, probably won't work
256 	hor_timing_PAL,			// scart pal
257 	hor_timing_PAL			// pal 60: horizontally, it is PAL
258 };
259 
260 static const uint16 *vert_timings[] = {
261 	vert_timing_NTSC,
262 	vert_timing_PAL,
263 	vert_timing_NTSC,		// palm: vertically, this is PAL
264 	vert_timing_PAL,		// palnc: a bit like PAL, but not really
265 	vert_timing_PAL,		// scart pal
266 	vert_timing_NTSC		// pal 60: vertically, it is NTSC
267 };
268 
269 
270 // timing of TV standards;
271 // the index is of type tv_standard
272 static const tv_timing radeon_std_tv_timing[6] = {
273 	// TK: hand-tuned v_active_lines and horizontal zoom
274 	{42954540, 2730, 200, 28, 200, 110, 2170, 525, 466/*440*/, 525, 2, 1, 0, 0.95/*0.88*/ * FIX_SCALE/2},	/* ntsc */
275 	// TK: frame_size_adjust was -6, but using 12 leads to perfect 25 Hz
276 	{53203425, 3405, 250, 28, 320, 80, 2627, 625, 555/*498*/, 625, 2, 3, 12, 0.98/*0.91*/ * FIX_SCALE/2},	/* pal */
277 	{42907338, 2727, 200, 28, 200, 110, 2170, 525, 440, 525, 2, 1, 0, 0.91 * FIX_SCALE/2},	/* palm */
278 	{42984675, 2751, 202, 28, 202, 110, 2190, 625, 510, 625, 2, 3, 0, 0.91 * FIX_SCALE/2},	/* palnc */
279 	{53203425, 3405, 250, 28, 320, 80, 2627, 625, 498, 625, 2, 3, 0, 0.91 * FIX_SCALE/2},	/* scart pal ??? */
280 	{53203425, 3405, 250, 28, 320, 80, 2627, 525, 440, 525, 2, 1, 0, 0.91 * FIX_SCALE/2},	/* pal 60 */
281 };
282 
283 
284 // adjust timing so it fills the entire visible area;
285 // the result may not be CRT compatible!
286 static void Radeon_MakeOverscanMode(
287 	display_timing *timing, tv_standard_e tv_format )
288 {
289 	const tv_timing *tv_timing = &radeon_std_tv_timing[tv_format-1];
290 
291 	// vertical is easy: ratio of displayed lines and blank must be
292 	// according to TV standard, having the sync delay of 1 line and
293 	// sync len of 3 lines is used by most VESA modes
294 	timing->v_total = timing->v_display * tv_timing->v_total / tv_timing->v_active_lines;
295 	timing->v_sync_start = timing->v_display + 1;
296 	timing->v_sync_end = timing->v_sync_start + 3;
297 
298 	// horizontal is tricky: the ratio may not be important (as it's
299 	// scaled by the TV-out unit anyway), but the sync position and length
300 	// is pure guessing - VESA modes don't exhibit particular scheme
301 	timing->h_total = timing->h_display * tv_timing->h_total / tv_timing->h_active_len;
302 	timing->h_sync_start = min( timing->h_total * 30 / 1000, 2 * 8 ) + timing->h_display;
303 	timing->h_sync_end = min( timing->h_total * 80 / 1000, 3 * 8 ) + timing->h_sync_start;
304 
305 	// set some pixel clock - it's replaced during fine tuning anyway
306 	timing->pixel_clock = timing->h_total * timing->v_total * 60;
307 	// most (but not all) 60 Hz modes have all negative sync, so use that too
308 	timing->flags = 0;
309 
310 	SHOW_INFO0( 4, "got:" );
311 	SHOW_INFO( 4, "H: %4d %4d %4d %4d",
312 		timing->h_display, timing->h_sync_start,
313 		timing->h_sync_end, timing->h_total );
314 	SHOW_INFO( 4, "V: %4d %4d %4d %4d",
315 		timing->v_display, timing->v_sync_start,
316 		timing->v_sync_end, timing->v_total );
317 	SHOW_INFO( 4, "clk: %ld", timing->pixel_clock );
318 }
319 
320 
321 #define TV_VERT_LEAD_IN_LINES 2
322 #define TV_UV_ADR_INI 0xc8
323 
324 // calculate TV parameters
325 void Radeon_CalcImpacTVParams(
326 	const general_pll_info *general_pll, impactv_params *params,
327 	tv_standard_e tv_format, bool internal_encoder,
328 	const display_mode *mode, display_mode *tweaked_mode )
329 {
330 	pll_info tv_pll, crt_pll;
331 	uint16 start_line, lines_before_active;
332 	const tv_timing *tv_timing = &radeon_std_tv_timing[tv_format-1];
333 
334 	SHOW_FLOW( 2, "internal_encoder=%s, format=%d",
335 		internal_encoder ? "yes" : "no", tv_format );
336 
337 	if( tv_format < ts_ntsc || tv_format > ts_max )
338 		tv_format = ts_ntsc;
339 
340 	params->mode888 = true;
341 	params->timing = *tv_timing;
342 
343 	Radeon_GetTVPLLConfiguration( general_pll, &tv_pll, internal_encoder );
344 	Radeon_CalcPLLDividers( &tv_pll, tv_timing->freq, 0, &params->tv_dividers );
345 
346 	Radeon_GetTVCRTPLLConfiguration( general_pll, &crt_pll, internal_encoder );
347 
348 	// initially, we try to keep to requested mode
349 	*tweaked_mode = *mode;
350 
351 	Radeon_MakeOverscanMode( &tweaked_mode->timing, tv_format );
352 
353 	// tweak CRT mode if necessary to match TV frame timing
354 	Radeon_MatchCRTPLL(
355 		&crt_pll,
356 		tv_timing->v_total, tv_timing->h_total, tv_timing->frame_size_adjust,
357 		tv_timing->freq,
358 		tweaked_mode, 2, 40,
359 		internal_encoder ? 0/*6*/ : 0, 2 + params->mode888,
360 		&params->crt_dividers, tweaked_mode );
361 
362 	// adopt synchronization to make tweaked mode look like original mode
363 	Radeon_AdoptSync( mode, tweaked_mode );
364 
365 	// timing magic
366 	start_line =
367 		tv_timing->h_sync_len
368 		+ tv_timing->h_setup_delay
369 		+ tv_timing->h_active_delay
370 		- tv_timing->h_genclk_delay;
371 
372 	lines_before_active =
373 		(tv_timing->v_field_total - tv_timing->v_active_lines) / 2 - 1
374 		- TV_VERT_LEAD_IN_LINES + 1;
375 
376 	SHOW_FLOW( 3, "lines_before_active=%d, start_line=%d", lines_before_active, start_line );
377 
378 	params->tv_clocks_to_active = (uint32)lines_before_active * tv_timing->h_total + start_line;
379 
380 	// calculate scaling.
381 	// this must be done before CalcTVRestart() or TVFlickerFixer() is called
382 	// start accumulator always with 0.25
383 	params->uv_accum_init = 0x10;
384 	// this value seems to be fixed (it's not written to any register but used
385 	// at some calculations)
386 	params->y_accum_init = 0;
387 	// for scaling ratio, take care that v_field_total is for full, not for half frames,
388 	// therefore we devide v_field_total by 2
389 	params->uv_inc = (tweaked_mode->timing.v_total << TV_UV_INC_FIX_SHIFT)
390 		* 2 / tv_timing->v_field_total;
391 
392 	SHOW_FLOW( 3, "uv_inc=%d", params->uv_inc );
393 
394 	params->h_inc =
395 		((int64)tweaked_mode->timing.h_display * 4096 /
396 		(tv_timing->h_active_len + tv_timing->h_active_delay) << (FIX_SHIFT - 1)) / tv_timing->scale;
397 
398 	Radeon_CalcImpacTVRestart( params, tweaked_mode,
399 		tv_timing->h_total - tv_timing->h_active_len, tv_timing->f_total );
400 	Radeon_CalcImpacTVFlickerFixer( params );
401 }
402 
403 
404 // standard upsample coefficients (external Theatre only)
405 static uint32 std_upsample_filter_coeff[RADEON_TV_UPSAMP_COEFF_NUM] = {
406 	0x3f010000, 0x7b008002, 0x00003f01,
407 	0x341b7405, 0x7f3a7617, 0x00003d04,
408 	0x2d296c0a, 0x0e316c2c,	0x00003e7d,
409 	0x2d1f7503, 0x2927643b, 0x0000056f,
410 	0x29257205, 0x25295050, 0x00000572
411 };
412 
413 
414 // compose TV register content
415 // as TV-Out uses a CRTC, it reprograms a PLL to create an unscaled image;
416 // as a result, you must not call Radeon_CalcPLLRegisters() afterwards
417 // TBD: what's special in terms of PLL in TV-Out mode?
418 void Radeon_CalcImpacTVRegisters(
419 	accelerator_info *ai, display_mode *mode,
420 	impactv_params *params, impactv_regs *values, int crtc_idx,
421 	bool internal_encoder, tv_standard_e tv_format, display_device_e display_device )
422 {
423 	const tv_timing *timing = &params->timing;
424 
425 	SHOW_FLOW0( 2, "" );
426 
427 	if( tv_format < ts_ntsc || tv_format > ts_max )
428 		tv_format = ts_ntsc;
429 
430 	values->tv_ftotal = timing->f_total;
431 
432 	// RE: UV_THINNER should affect sharpness only, but the only effect is that
433 	// the colour fades out, so I leave it zero
434 	values->tv_vscaler_cntl1 = RADEON_TV_VSCALER_CNTL1_Y_W_EN;
435 
436 	values->tv_vscaler_cntl1 =
437 		(values->tv_vscaler_cntl1 & 0xe3ff0000) |
438 		params->uv_inc;
439 
440 	if( internal_encoder ) {
441 		// RE: was on - update: disabling it breaks restart
442 		values->tv_vscaler_cntl1 |= RADEON_TV_VSCALER_CNTL1_RESTART_FIELD;
443 		if( mode->timing.h_display == 1024 )
444 			values->tv_vscaler_cntl1 |= 4 << RADEON_TV_VSCALER_CNTL1_Y_DEL_W_SIG_SHIFT;
445 		else
446 			values->tv_vscaler_cntl1 |= 2 << RADEON_TV_VSCALER_CNTL1_Y_DEL_W_SIG_SHIFT;
447 	} else {
448 		values->tv_vscaler_cntl1 |= 2 << RADEON_TV_VSCALER_CNTL1_Y_DEL_W_SIG_SHIFT;
449 	}
450 
451 	values->tv_y_saw_tooth_cntl =
452 		params->y_saw_tooth_amp |
453 		(params->y_saw_tooth_slope << RADEON_TV_Y_SAW_TOOTH_CNTL_SLOPE_SHIFT);
454 
455 	values->tv_y_fall_cntl =
456 		params->y_fall_accum_init |
457 		RADEON_TV_Y_FALL_CNTL_Y_FALL_PING_PONG |
458 		(params->y_coeff_enable ? RADEON_TV_Y_FALL_CNTL_Y_COEFF_EN : 0) |
459 		(params->y_coeff_value << RADEON_TV_Y_FALL_CNTL_Y_COEFF_VALUE_SHIFT);
460 
461 	values->tv_y_rise_cntl =
462 		params->y_rise_accum_init |
463 		RADEON_TV_Y_RISE_CNTL_Y_RISE_PING_PONG;
464 
465 	// RE: all dither flags/values were zero
466 	values->tv_vscaler_cntl2 =
467 		(values->tv_vscaler_cntl2 & 0x00fffff0) |
468 		(params->uv_accum_init << RADEON_TV_VSCALER_CNTL2_UV_ACCUM_INIT_SHIFT);
469 
470 	if( internal_encoder ) {
471 		values->tv_vscaler_cntl2 |=
472 			RADEON_TV_VSCALER_CNTL2_DITHER_MODE |
473 			RADEON_TV_VSCALER_CNTL2_Y_OUTPUT_DITHER_EN |
474 			RADEON_TV_VSCALER_CNTL2_UV_OUTPUT_DITHER_EN |
475 			RADEON_TV_VSCALER_CNTL2_UV_TO_BUF_DITHER_EN;
476 	}
477 
478 	values->tv_hrestart = params->h_restart;
479 	values->tv_vrestart = params->v_restart;
480 	values->tv_frestart = params->f_restart;
481 
482 	values->tv_tv_pll_cntl =
483 		(params->tv_dividers.ref & RADEON_TV_PLL_CNTL_TV_M0_LO_MASK) |
484 		((params->tv_dividers.feedback & RADEON_TV_PLL_CNTL_TV_N0_LO_MASK)
485 			<< RADEON_TV_PLL_CNTL_TV_N0_LO_SHIFT) |
486 		((params->tv_dividers.ref >> RADEON_TV_PLL_CNTL_TV_M0_LO_BITS)
487 			<< RADEON_TV_PLL_CNTL_TV_M0_HI_SHIFT) |
488 		((params->tv_dividers.feedback >> RADEON_TV_PLL_CNTL_TV_N0_LO_BITS)
489 			<< RADEON_TV_PLL_CNTL_TV_N0_HI_SHIFT) |
490 		// RE: was on
491 		//RADEON_TV_PLL_CNTL_TV_SLIP_EN |
492 		(params->tv_dividers.post << RADEON_TV_PLL_CNTL_TV_P_SHIFT); //|
493 		// RE: was on
494 		//RADEON_TV_PLL_CNTL_TV_DTO_EN;
495 	values->tv_crt_pll_cntl =
496 		(params->crt_dividers.ref & RADEON_TV_CRT_PLL_CNTL_M0_LO_MASK) |
497 		((params->crt_dividers.feedback & RADEON_TV_CRT_PLL_CNTL_N0_LO_MASK)
498 			<< RADEON_TV_CRT_PLL_CNTL_N0_LO_SHIFT) |
499 		((params->crt_dividers.ref >> RADEON_TV_CRT_PLL_CNTL_M0_LO_BITS)
500 			<< RADEON_TV_CRT_PLL_CNTL_M0_HI_SHIFT) |
501 		((params->crt_dividers.feedback >> RADEON_TV_CRT_PLL_CNTL_N0_LO_BITS)
502 			<< RADEON_TV_CRT_PLL_CNTL_N0_HI_SHIFT) |
503 		(params->crt_dividers.extra_post == 2 ? RADEON_TV_CRT_PLL_CNTL_CLKBY2 : 0);
504 
505 	// TK: from Gatos
506 	// in terms of byte clock devider, I have no clue how that works,
507 	// but leaving it 1 seems to be save
508 	values->tv_clock_sel_cntl =
509 		0x33 |
510 		((/*params->crt_dividers.post_code - 1*/0) << RADEON_TV_CLOCK_SEL_CNTL_BYTCLK_SHIFT) |
511 		(1 << RADEON_TV_CLOCK_SEL_CNTL_BYTCLKD_SHIFT);
512 
513     values->tv_clkout_cntl = 0x09;
514     if( !internal_encoder )
515     	values->tv_clkout_cntl |= 1 << 5;
516 
517 	values->tv_htotal = mode->timing.h_total - 1;
518 	values->tv_hsize = mode->timing.h_display;
519 	values->tv_hdisp = mode->timing.h_display - 1;
520 	values->tv_hstart =
521 		// TK: was -12, but this cuts off the left border of the image
522 		internal_encoder ?
523 		values->tv_hdisp + 1 - params->mode888 + 12 :
524 		values->tv_hdisp + 1 - params->mode888 + 12;
525 
526 	values->tv_vtotal = mode->timing.v_total - 1;
527 	values->tv_vdisp = mode->timing.v_display - 1;
528 	values->tv_sync_size = mode->timing.h_display + 8;
529 
530 	values->tv_timing_cntl =
531 		(values->tv_timing_cntl & 0xfffff000) |
532 		params->h_inc;
533 
534 	if( ai->si->asic >= rt_r300 ) {
535 		// this is a hack to fix improper UV scaling
536 		// (at least this is what the sample code says)
537 		values->tv_timing_cntl =
538 			(values->tv_timing_cntl & 0x00ffffff) |
539 			((0x72 * 640 / mode->timing.h_display)
540 				<< RADEON_TV_TIMING_CNTL_UV_OUTPUT_POST_SCALE_SHIFT);
541 	}
542 
543 	if( internal_encoder ) {
544 		// tell TV-DAC to generate proper NTSC/PAL signal
545 		values->tv_dac_cntl =
546 			RADEON_TV_DAC_CNTL_NBLANK |
547 			RADEON_TV_DAC_CNTL_NHOLD |
548 			(8 << RADEON_TV_DAC_CNTL_BGADJ_SHIFT) |
549 			(6 << RADEON_TV_DAC_CNTL_DACADJ_SHIFT);
550 
551 		switch( tv_format ) {
552 		case ts_ntsc:
553 			values->tv_dac_cntl |= RADEON_TV_DAC_CNTL_STD_NTSC;
554 			break;
555 
556 		case ts_pal_bdghi:
557 		case ts_pal_m:
558 		case ts_pal_nc:
559 		case ts_scart_pal:
560 		case ts_pal_60:
561 			values->tv_dac_cntl |= RADEON_TV_DAC_CNTL_STD_PAL;
562 			break;
563 		default:
564 			;
565 		}
566 
567 		// enable composite or S-Video DAC
568 		values->tv_dac_cntl |=
569 			RADEON_TV_DAC_CNTL_RDACPD |
570 			RADEON_TV_DAC_CNTL_GDACPD |
571 			RADEON_TV_DAC_CNTL_BDACPD;
572 
573 		if( (display_device & dd_ctv) != 0 ) {
574 			values->tv_dac_cntl &=
575 				~RADEON_TV_DAC_CNTL_BDACPD;
576 		}
577 
578 		if( (display_device & dd_stv) != 0 ) {
579 			values->tv_dac_cntl &=
580 				~(RADEON_TV_DAC_CNTL_RDACPD |
581 				  RADEON_TV_DAC_CNTL_GDACPD);
582 		}
583 	} else {
584 		values->tv_dac_cntl =
585 			(values->tv_dac_cntl & ~(RADEON_TV_DAC_CNTL_STD_NTSC | 0x88 |
586 				RADEON_TV_DAC_CNTL_BGSLEEP | RADEON_TV_DAC_CNTL_PEDESTAL)) |
587 			RADEON_TV_DAC_CNTL_DETECT |
588 			RADEON_TV_DAC_CNTL_NBLANK |
589 			RADEON_TV_DAC_CNTL_NHOLD;
590 	}
591 
592 	values->tv_modulator_cntl1 &= ~(
593 		RADEON_TV_MODULATOR_CNTL1_ALT_PHASE_EN |
594 		RADEON_TV_MODULATOR_CNTL1_SYNC_TIP_LEVEL |
595 		RADEON_TV_MODULATOR_CNTL1_SET_UP_LEVEL_MASK |
596 		RADEON_TV_MODULATOR_CNTL1_BLANK_LEVEL_MASK);
597 
598 	switch( tv_format ) {
599 	case ts_ntsc:
600 		// RE: typo?
601 		//values->tv_dac_cntl |=
602 		values->tv_modulator_cntl1 |=
603 			RADEON_TV_MODULATOR_CNTL1_SYNC_TIP_LEVEL |
604 			(0x46 << RADEON_TV_MODULATOR_CNTL1_SET_UP_LEVEL_SHIFT) |
605 			(0x3b << RADEON_TV_MODULATOR_CNTL1_BLANK_LEVEL_SHIFT);
606 		values->tv_modulator_cntl2 =
607 			(-111 & TV_MODULATOR_CNTL2_U_BURST_LEVEL_MASK) |
608 			((0 & TV_MODULATOR_CNTL2_V_BURST_LEVEL_MASK) << TV_MODULATOR_CNTL2_V_BURST_LEVEL_SHIFT);
609 		break;
610 
611 	case ts_pal_bdghi:
612 		values->tv_modulator_cntl1 |=
613 			RADEON_TV_MODULATOR_CNTL1_ALT_PHASE_EN |
614 			RADEON_TV_MODULATOR_CNTL1_SYNC_TIP_LEVEL |
615 			(0x3b << RADEON_TV_MODULATOR_CNTL1_SET_UP_LEVEL_SHIFT) |
616 			(0x3b << RADEON_TV_MODULATOR_CNTL1_BLANK_LEVEL_SHIFT);
617 		values->tv_modulator_cntl2 =
618 			(-78 & TV_MODULATOR_CNTL2_U_BURST_LEVEL_MASK) |
619 			((62 & TV_MODULATOR_CNTL2_V_BURST_LEVEL_MASK) << TV_MODULATOR_CNTL2_V_BURST_LEVEL_SHIFT);
620 		break;
621 
622 	case ts_scart_pal:
623 		// from register spec
624 		values->tv_modulator_cntl1 |=
625 			RADEON_TV_MODULATOR_CNTL1_ALT_PHASE_EN |
626 			RADEON_TV_MODULATOR_CNTL1_SYNC_TIP_LEVEL;
627 		values->tv_modulator_cntl2 =
628 			(0 & TV_MODULATOR_CNTL2_U_BURST_LEVEL_MASK) |
629 			((0 & TV_MODULATOR_CNTL2_V_BURST_LEVEL_MASK) << TV_MODULATOR_CNTL2_V_BURST_LEVEL_SHIFT);
630 		break;
631 
632     default:
633 		// there are many formats missing, sigh...
634 		;
635     }
636 
637     // RE:
638     values->tv_modulator_cntl1 |=
639     	RADEON_TV_MODULATOR_CNTL1_YFLT_EN |
640     	RADEON_TV_MODULATOR_CNTL1_UVFLT_EN |
641     	RADEON_TV_MODULATOR_CNTL1_SLEW_RATE_LIMIT |
642     	(2 << RADEON_TV_MODULATOR_CNTL1_CY_FILT_BLEND_SHIFT);
643 
644 	if( internal_encoder ) {
645 		values->tv_data_delay_a = 0x0b0c0a06;
646 		values->tv_data_delay_b = 0x070a0a0c;
647 	} else {
648 		values->tv_data_delay_a = 0x07080604;
649 		values->tv_data_delay_b = 0x03070607;
650 	}
651 
652 	values->tv_frame_lock_cntl = internal_encoder ? 0 : 0xf0f;
653 
654 	if( internal_encoder ) {
655 		values->tv_pll_cntl1 =
656 			(4 << RADEON_TV_PLL_CNTL1_TVPCP_SHIFT) |
657 			(4 << RADEON_TV_PLL_CNTL1_TVPVG_SHIFT) |
658 			// RE: was 2
659 			(1 << RADEON_TV_PLL_CNTL1_TVPDC_SHIFT) |
660 			RADEON_TV_PLL_CNTL1_TVCLK_SRC_SEL_TVPLLCLK |
661 			RADEON_TV_PLL_CNTL1_TVPLL_TEST;
662 
663 		values->tv_rgb_cntl =
664 			((crtc_idx == 1 ? 2 : 0) << RADEON_TV_RGB_CNTL_RGB_SRC_SEL_SHIFT) |
665 			RADEON_TV_RGB_CNTL_RGB_DITHER_EN |
666 			(0xb << RADEON_TV_RGB_CNTL_UVRAM_READ_MARGIN_SHIFT) |
667 			(7 << RADEON_TV_RGB_CNTL_FIFORAM_FIFOMACRO_READ_MARGIN_SHIFT);
668 
669 		// RE:
670 		values->tv_rgb_cntl |= 0x4000000;
671 
672 		values->tv_pre_dac_mux_cntl =
673 			RADEON_TV_PRE_DAC_MUX_CNTL_Y_RED_EN |
674 			RADEON_TV_PRE_DAC_MUX_CNTL_C_GRN_EN |
675 			RADEON_TV_PRE_DAC_MUX_CNTL_CMP_BLU_EN |
676 			RADEON_TV_PRE_DAC_MUX_CNTL_DAC_DITHER_EN;
677 
678 		// RE:
679 		/* |
680 			(0x2c << RADEON_TV_PRE_DAC_MUX_CNTL_FORCE_DAC_DATA_SHIFT);*/
681 	} else {
682 		// this register seems to have completely different meaning on Theatre chip
683 		values->tv_pll_cntl1 =
684 			(1 << 3) | (1 << 4) | (4 << 8) | (1 << 11)
685 		    | (5 << 13) | (4 << 16) | (1 << 19) | (5 << 21);
686 
687 		// this one too
688 		values->tv_rgb_cntl = params->mode888;
689 
690 		values->tv_pre_dac_mux_cntl =
691 			RADEON_TV_PRE_DAC_MUX_CNTL_Y_RED_EN |
692 			RADEON_TV_PRE_DAC_MUX_CNTL_C_GRN_EN |
693 			RADEON_TV_PRE_DAC_MUX_CNTL_CMP_BLU_EN |
694 			RADEON_TV_PRE_DAC_MUX_CNTL_DAC_DITHER_EN;
695 			// RE:
696 			/*(0xaf << RADEON_TV_PRE_DAC_MUX_CNTL_FORCE_DAC_DATA_SHIFT);*/
697 	}
698 
699 	values->tv_pll_fine_cntl = 0;
700 
701 	// TBD: this is certainly broken
702 	// (they do an ((orig & 0xe0) & 0x600) which is constant zero)
703 	values->tv_master_cntl =
704 		RADEON_TV_MASTER_CNTL_CRT_FIFO_CE_EN |
705 		RADEON_TV_MASTER_CNTL_TV_FIFO_CE_EN;
706 
707 	if( tv_format == ts_ntsc )
708 		values->tv_master_cntl |= RADEON_TV_MASTER_CNTL_RESTART_PHASE_FIX;
709 	else
710 		values->tv_master_cntl &= ~RADEON_TV_MASTER_CNTL_RESTART_PHASE_FIX;
711 
712 	// this is missing in the sample code
713 	if( internal_encoder )
714 		values->tv_master_cntl |= RADEON_TV_MASTER_CNTL_TV_ON;
715 	else
716 		values->tv_master_cntl |=
717 			RADEON_TV_MASTER_CNTL_RESTART_PHASE_FIX | // RE: guessed
718 
719 			RADEON_TV_MASTER_CNTL_VIN_ASYNC_RST |
720 			RADEON_TV_MASTER_CNTL_AUD_ASYNC_RST |
721 			RADEON_TV_MASTER_CNTL_DVS_ASYNC_RST;
722 
723 	values->tv_gain_limit_settings = 0x017f05ff;
724 	values->tv_linear_gain_settings = 0x01000100;
725 	values->tv_upsamp_and_gain_cntl = 0x00000005;
726 	values->tv_crc_cntl = 0;
727 
728 	SHOW_FLOW( 2, "tv_master_cntl=%x", values->tv_master_cntl );
729 
730 	memcpy( values->tv_upsample_filter_coeff, std_upsample_filter_coeff,
731 		RADEON_TV_UPSAMP_COEFF_NUM * sizeof( uint32 ));
732 
733 	// setup output timing
734 	memcpy( values->tv_hor_timing, hor_timings[tv_format-1],
735 		RADEON_TV_TIMING_SIZE * sizeof( uint16 ));
736 	memcpy( values->tv_vert_timing, vert_timings[tv_format-1],
737 		RADEON_TV_TIMING_SIZE * sizeof( uint16 ) );
738 
739 	// arbitrary position of vertical timing table in FIFO
740 	values->tv_uv_adr = TV_UV_ADR_INI;
741 }
742 
743 
744 // get address of horizontal timing table in FIFO
745 static uint16 getHorTimingTableAddr(
746 	impactv_regs *values, bool internal_encoder )
747 {
748 	switch( (values->tv_uv_adr & RADEON_TV_UV_ADR_HCODE_TABLE_SEL_MASK)
749 		>> RADEON_TV_UV_ADR_HCODE_TABLE_SEL_SHIFT )
750 	{
751 	case 0:
752 		return internal_encoder ? RADEON_TV_MAX_FIFO_ADDR_INTERN : RADEON_TV_MAX_FIFO_ADDR;
753 
754 	case 1:
755 		return ((values->tv_uv_adr & RADEON_TV_UV_ADR_TABLE1_BOT_ADR_MASK)
756 			>> RADEON_TV_UV_ADR_TABLE1_BOT_ADR_SHIFT) * 2;
757 	case 2:
758 		return ((values->tv_uv_adr & RADEON_TV_UV_ADR_TABLE3_TOP_ADR_MASK)
759 			>> RADEON_TV_UV_ADR_TABLE3_TOP_ADR_SHIFT) * 2;
760 
761 	default:
762 		return 0;
763 	}
764 }
765 
766 // get address of vertical timing table in FIFO
767 static uint16 getVertTimingTableAddr(
768 	impactv_regs *values )
769 {
770 	switch( (values->tv_uv_adr & RADEON_TV_UV_ADR_VCODE_TABLE_SEL_MASK)
771 		>> RADEON_TV_UV_ADR_VCODE_TABLE_SEL_SHIFT )
772 	{
773 	case 0:
774 		return ((values->tv_uv_adr & RADEON_TV_UV_ADR_MAX_UV_ADR_MASK)
775 			>> RADEON_TV_UV_ADR_MAX_UV_ADR_SHIFT) * 2 + 1;
776 
777 	case 1:
778 		return ((values->tv_uv_adr & RADEON_TV_UV_ADR_TABLE1_BOT_ADR_MASK)
779 			>> RADEON_TV_UV_ADR_TABLE1_BOT_ADR_SHIFT) * 2 + 1;
780 
781 	case 2:
782 		return ((values->tv_uv_adr & RADEON_TV_UV_ADR_TABLE3_TOP_ADR_MASK)
783 			>> RADEON_TV_UV_ADR_TABLE3_TOP_ADR_SHIFT) * 2 + 1;
784 
785 	default:
786 		return 0;
787 	}
788 }
789 
790 
791 // write horizontal timing table
792 void Radeon_ImpacTVwriteHorTimingTable(
793 	accelerator_info *ai, impactv_write_FIFO write, impactv_regs *values, bool internal_encoder )
794 {
795 	uint16 addr = getHorTimingTableAddr( values, internal_encoder );
796 	int i;
797 
798 	for( i = 0; i < RADEON_TV_TIMING_SIZE; i += 2, --addr ) {
799 		uint32 value =
800 			((uint32)values->tv_hor_timing[i] << 14) |
801 			values->tv_hor_timing[i + 1];
802 
803 		write( ai, addr, value );
804 
805 		if( values->tv_hor_timing[i] == 0 ||
806 			values->tv_hor_timing[i + 1] == 0 )
807 			break;
808 	}
809 }
810 
811 
812 // write vertical timing table
813 void Radeon_ImpacTVwriteVertTimingTable(
814 	accelerator_info *ai, impactv_write_FIFO write, impactv_regs *values )
815 {
816 	uint16 addr = getVertTimingTableAddr( values );
817 	int i;
818 
819 	for( i = 0; i < RADEON_TV_TIMING_SIZE; i += 2 , ++addr ) {
820 		uint32 value =
821 			((uint32)values->tv_vert_timing[i + 1] << 14) |
822 			values->tv_vert_timing[i];
823 
824 		write( ai, addr, value );
825 
826 		if( values->tv_vert_timing[i + 1] == 0 ||
827 			values->tv_vert_timing[i] == 0 )
828 			break;
829 	}
830 }
831