1/*
2	Copyright (c) 2002/03, Thomas Kurschel
3
4
5	Part of Radeon accelerant
6
7	Programming of TV-out via Rage Theatre
8*/
9
10
11#include "radeon_interface.h"
12#include "radeon_accelerant.h"
13
14#include "theatre_regs.h"
15#include "tv_out_regs.h"
16#include "set_mode.h"
17
18#include <stdlib.h>
19
20
21// mapping of offset in impactv_regs to register address
22typedef struct register_mapping {
23	uint16		address;			// register address
24	uint16		offset;				// offset in impactv_regs
25} register_mapping;
26
27
28// Rage Theatre TV-Out:
29
30// registers to write at first
31static const register_mapping theatre_reg_mapping_start[] = {
32	{ THEATRE_VIP_MASTER_CNTL,		offsetof( impactv_regs, tv_master_cntl ) },
33	{ THEATRE_VIP_TVO_DATA_DELAY_A,	offsetof( impactv_regs, tv_data_delay_a ) },
34	{ THEATRE_VIP_TVO_DATA_DELAY_B,	offsetof( impactv_regs, tv_data_delay_b ) },
35
36	{ THEATRE_VIP_CLKOUT_CNTL,		offsetof( impactv_regs, tv_clkout_cntl ) },
37	{ THEATRE_VIP_PLL_CNTL0,		offsetof( impactv_regs, tv_pll_cntl1 ) },
38
39	{ THEATRE_VIP_HRESTART,			offsetof( impactv_regs, tv_hrestart ) },
40	{ THEATRE_VIP_VRESTART,			offsetof( impactv_regs, tv_vrestart ) },
41	{ THEATRE_VIP_FRESTART,			offsetof( impactv_regs, tv_frestart ) },
42	{ THEATRE_VIP_FTOTAL,			offsetof( impactv_regs, tv_ftotal ) },
43
44	{ THEATRE_VIP_CLOCK_SEL_CNTL, 	offsetof( impactv_regs, tv_clock_sel_cntl ) },
45	{ THEATRE_VIP_TV_PLL_CNTL,		offsetof( impactv_regs, tv_tv_pll_cntl ) },
46	{ THEATRE_VIP_CRT_PLL_CNTL,		offsetof( impactv_regs, tv_crt_pll_cntl ) },
47
48	{ THEATRE_VIP_HTOTAL,			offsetof( impactv_regs, tv_htotal ) },
49	{ THEATRE_VIP_HSIZE,			offsetof( impactv_regs, tv_hsize ) },
50	{ THEATRE_VIP_HDISP,			offsetof( impactv_regs, tv_hdisp ) },
51	{ THEATRE_VIP_HSTART,			offsetof( impactv_regs, tv_hstart ) },
52	{ THEATRE_VIP_VTOTAL,			offsetof( impactv_regs, tv_vtotal ) },
53	{ THEATRE_VIP_VDISP,			offsetof( impactv_regs, tv_vdisp ) },
54
55	{ THEATRE_VIP_TIMING_CNTL,		offsetof( impactv_regs, tv_timing_cntl ) },
56
57	{ THEATRE_VIP_VSCALER_CNTL,		offsetof( impactv_regs, tv_vscaler_cntl1 ) },
58	{ THEATRE_VIP_VSCALER_CNTL2,	offsetof( impactv_regs, tv_vscaler_cntl2 ) },
59	{ THEATRE_VIP_SYNC_SIZE,		offsetof( impactv_regs, tv_sync_size ) },
60	{ THEATRE_VIP_Y_SAW_TOOTH_CNTL, offsetof( impactv_regs, tv_y_saw_tooth_cntl ) },
61	{ THEATRE_VIP_Y_RISE_CNTL,		offsetof( impactv_regs, tv_y_rise_cntl ) },
62	{ THEATRE_VIP_Y_FALL_CNTL,		offsetof( impactv_regs, tv_y_fall_cntl ) },
63
64	{ THEATRE_VIP_MODULATOR_CNTL1,	offsetof( impactv_regs, tv_modulator_cntl1 ) },
65	{ THEATRE_VIP_MODULATOR_CNTL2,	offsetof( impactv_regs, tv_modulator_cntl2 ) },
66
67	{ THEATRE_VIP_RGB_CNTL,			offsetof( impactv_regs, tv_rgb_cntl ) },
68
69	{ THEATRE_VIP_UV_ADR,			offsetof( impactv_regs, tv_uv_adr ) },
70
71	{ THEATRE_VIP_PRE_DAC_MUX_CNTL, offsetof( impactv_regs, tv_pre_dac_mux_cntl ) },
72	{ THEATRE_VIP_FRAME_LOCK_CNTL,	offsetof( impactv_regs, tv_frame_lock_cntl ) },
73	{ THEATRE_VIP_CRC_CNTL,			offsetof( impactv_regs, tv_crc_cntl ) },
74	{ 0, 0 }
75};
76
77// registers to write when things settled down
78static const register_mapping theatre_reg_mapping_finish[] = {
79	{ THEATRE_VIP_UPSAMP_COEFF0_0,	offsetof( impactv_regs, tv_upsample_filter_coeff[0*3+0] ) },
80	{ THEATRE_VIP_UPSAMP_COEFF0_1,	offsetof( impactv_regs, tv_upsample_filter_coeff[0*3+1] ) },
81	{ THEATRE_VIP_UPSAMP_COEFF0_2,	offsetof( impactv_regs, tv_upsample_filter_coeff[0*3+2] ) },
82	{ THEATRE_VIP_UPSAMP_COEFF1_0,	offsetof( impactv_regs, tv_upsample_filter_coeff[1*3+0] ) },
83	{ THEATRE_VIP_UPSAMP_COEFF1_1,	offsetof( impactv_regs, tv_upsample_filter_coeff[1*3+1] ) },
84	{ THEATRE_VIP_UPSAMP_COEFF1_2,	offsetof( impactv_regs, tv_upsample_filter_coeff[1*3+2] ) },
85	{ THEATRE_VIP_UPSAMP_COEFF2_0,	offsetof( impactv_regs, tv_upsample_filter_coeff[2*3+0] ) },
86	{ THEATRE_VIP_UPSAMP_COEFF2_1,	offsetof( impactv_regs, tv_upsample_filter_coeff[2*3+1] ) },
87	{ THEATRE_VIP_UPSAMP_COEFF2_2,	offsetof( impactv_regs, tv_upsample_filter_coeff[2*3+2] ) },
88	{ THEATRE_VIP_UPSAMP_COEFF3_0,	offsetof( impactv_regs, tv_upsample_filter_coeff[3*3+0] ) },
89	{ THEATRE_VIP_UPSAMP_COEFF3_1,	offsetof( impactv_regs, tv_upsample_filter_coeff[3*3+1] ) },
90	{ THEATRE_VIP_UPSAMP_COEFF3_2,	offsetof( impactv_regs, tv_upsample_filter_coeff[3*3+2] ) },
91	{ THEATRE_VIP_UPSAMP_COEFF4_0,	offsetof( impactv_regs, tv_upsample_filter_coeff[4*3+0] ) },
92	{ THEATRE_VIP_UPSAMP_COEFF4_1,	offsetof( impactv_regs, tv_upsample_filter_coeff[4*3+1] ) },
93	{ THEATRE_VIP_UPSAMP_COEFF4_2,	offsetof( impactv_regs, tv_upsample_filter_coeff[4*3+2] ) },
94
95	{ THEATRE_VIP_GAIN_LIMIT_SETTINGS,  offsetof( impactv_regs, tv_gain_limit_settings ) },
96	{ THEATRE_VIP_LINEAR_GAIN_SETTINGS, offsetof( impactv_regs, tv_linear_gain_settings ) },
97	{ THEATRE_VIP_UPSAMP_AND_GAIN_CNTL, offsetof( impactv_regs, tv_upsamp_and_gain_cntl ) },
98
99	{ THEATRE_VIP_TV_DAC_CNTL,		offsetof( impactv_regs, tv_dac_cntl ) },
100	{ THEATRE_VIP_MASTER_CNTL,		offsetof( impactv_regs, tv_master_cntl ) },
101	{ 0, 0 }
102};
103
104
105// write list of Rage Theatre registers
106static void writeTheatreRegList(
107	accelerator_info *ai, impactv_regs *values, const register_mapping *mapping )
108{
109	for( ; mapping->address != 0 || mapping->offset != 0; ++mapping ) {
110		Radeon_VIPWrite( ai, ai->si->theatre_channel, mapping->address,
111			*(uint32 *)((char *)(values) + mapping->offset) );
112
113/*		SHOW_FLOW( 2, "%x=%x", mapping->address,
114			*(uint32 *)((char *)(values) + mapping->offset) );*/
115	}
116}
117
118
119// read timing FIFO
120uint32 Radeon_TheatreReadFIFO(
121	accelerator_info *ai, uint16 addr )
122{
123	bigtime_t start_time;
124	uint32 res = ~0;
125
126	//SHOW_FLOW( 2, "addr=%d", addr );
127
128	Radeon_VIPWrite( ai, ai->si->theatre_channel,
129		THEATRE_VIP_HOST_RD_WT_CNTL, addr | RADEON_TV_HOST_RD_WT_CNTL_RD );
130
131	start_time = system_time();
132
133	do {
134		uint32 status;
135
136		Radeon_VIPRead( ai, ai->si->theatre_channel, THEATRE_VIP_HOST_RD_WT_CNTL, &status );
137
138		if( (status & RADEON_TV_HOST_RD_WT_CNTL_RD_ACK) != 0 )
139			break;
140	} while( system_time() - start_time < 2000000 );
141
142	Radeon_VIPWrite( ai, ai->si->theatre_channel, THEATRE_VIP_HOST_RD_WT_CNTL, 0);
143	Radeon_VIPRead( ai, ai->si->theatre_channel, THEATRE_VIP_HOST_READ_DATA, &res );
144
145	return res;
146}
147
148
149// write to timing FIFO
150void Radeon_TheatreWriteFIFO(
151	accelerator_info *ai, uint16 addr, uint32 value )
152{
153	bigtime_t start_time;
154
155	//readFIFO( ai, addr, internal_encoder );
156
157	//SHOW_FLOW( 2, "addr=%d, value=%x %x", addr, value >> 14, value & 0x3fff );
158
159	Radeon_VIPWrite( ai, ai->si->theatre_channel, THEATRE_VIP_HOST_WRITE_DATA, value);
160	Radeon_VIPWrite( ai, ai->si->theatre_channel,
161		THEATRE_VIP_HOST_RD_WT_CNTL, addr | RADEON_TV_HOST_RD_WT_CNTL_WT);
162
163	start_time = system_time();
164
165	do {
166		uint32 status;
167
168		Radeon_VIPRead( ai, ai->si->theatre_channel, THEATRE_VIP_HOST_RD_WT_CNTL, &status );
169
170		if( (status & RADEON_TV_HOST_RD_WT_CNTL_WT_ACK) != 0 )
171			break;
172	} while( system_time() - start_time < 2000000 );
173
174	Radeon_VIPWrite( ai, ai->si->theatre_channel, THEATRE_VIP_HOST_RD_WT_CNTL, 0 );
175}
176
177
178// program TV-Out registers
179void Radeon_TheatreProgramTVRegisters(
180	accelerator_info *ai, impactv_regs *values )
181{
182	uint32 orig_tv_master_cntl = values->tv_master_cntl;
183
184	SHOW_FLOW0( 2, "" );
185
186	// disable TV-out when registers are setup
187	// it gets enabled again when things have settled down
188	values->tv_master_cntl |=
189		RADEON_TV_MASTER_CNTL_TV_ASYNC_RST |
190		RADEON_TV_MASTER_CNTL_CRT_ASYNC_RST |
191		RADEON_TV_MASTER_CNTL_TV_FIFO_ASYNC_RST |
192
193		RADEON_TV_MASTER_CNTL_VIN_ASYNC_RST |
194		RADEON_TV_MASTER_CNTL_AUD_ASYNC_RST |
195		RADEON_TV_MASTER_CNTL_DVS_ASYNC_RST;
196
197	writeTheatreRegList( ai, values, theatre_reg_mapping_start );
198
199	// un-reset FIFO to access timing table
200	Radeon_VIPWrite( ai, ai->si->theatre_channel, THEATRE_VIP_MASTER_CNTL,
201		orig_tv_master_cntl |
202		RADEON_TV_MASTER_CNTL_TV_ASYNC_RST |
203		RADEON_TV_MASTER_CNTL_CRT_ASYNC_RST |
204
205		RADEON_TV_MASTER_CNTL_VIN_ASYNC_RST |
206		RADEON_TV_MASTER_CNTL_AUD_ASYNC_RST |
207		RADEON_TV_MASTER_CNTL_DVS_ASYNC_RST );
208
209	Radeon_ImpacTVwriteHorTimingTable( ai, Radeon_TheatreWriteFIFO, values, false );
210	Radeon_ImpacTVwriteVertTimingTable( ai, Radeon_TheatreWriteFIFO, values );
211
212	snooze( 50000 );
213
214	values->tv_master_cntl = orig_tv_master_cntl;
215	writeTheatreRegList( ai, values, theatre_reg_mapping_finish );
216}
217
218
219// read list of Rage Theatre registers
220static void readTheatreRegList(
221	accelerator_info *ai, impactv_regs *values, const register_mapping *mapping )
222{
223	for( ; mapping->address != 0 || mapping->offset != 0; ++mapping ) {
224		Radeon_VIPRead( ai, ai->si->theatre_channel, mapping->address,
225			(uint32 *)((char *)(values) + mapping->offset) );
226
227		/*SHOW_FLOW( 2, "%x=%x", mapping->address,
228			*(uint32 *)((char *)(values) + mapping->offset) );*/
229	}
230
231	//snooze( 1000000 );
232}
233
234
235// read TV-Out registers
236void Radeon_TheatreReadTVRegisters(
237	accelerator_info *ai, impactv_regs *values )
238{
239	readTheatreRegList( ai, values, theatre_reg_mapping_start );
240	readTheatreRegList( ai, values, theatre_reg_mapping_finish );
241
242	//snooze( 1000000 );
243}
244
245
246// detect TV-Out encoder
247void Radeon_DetectTVOut(
248	accelerator_info *ai )
249{
250	shared_info *si = ai->si;
251
252	SHOW_FLOW0( 0, "" );
253
254	switch( si->tv_chip ) {
255	case tc_external_rt1: {
256		// for external encoder, we need the VIP channel
257		int channel = Radeon_FindVIPDevice( ai, THEATRE_ID );
258
259		if( channel < 0 ) {
260			SHOW_ERROR0( 2, "This card needs a Rage Theatre for TV-Out, but there is none." );
261			si->tv_chip = tc_none;
262		} else {
263			SHOW_INFO( 2, "Rage Theatre found on VIP channel %d", channel );
264			si->theatre_channel = channel;
265		}
266		break; }
267	default:
268		// for internal encoder, we don't have to look farther - it must be there
269		;
270	}
271}
272