xref: /haiku/src/add-ons/kernel/drivers/graphics/radeon/vip.c (revision 5f6e3f515c38891bf243cc013f5faca7511708dc)
1*5f6e3f51Sshadow303 /*
2*5f6e3f51Sshadow303 	Copyright (c) 2002-04, Thomas Kurschel
3*5f6e3f51Sshadow303 
4*5f6e3f51Sshadow303 
5*5f6e3f51Sshadow303 	Part of Radeon accelerant
6*5f6e3f51Sshadow303 
7*5f6e3f51Sshadow303 	Access to VIP
8*5f6e3f51Sshadow303 
9*5f6e3f51Sshadow303 	This code must be in kernel because we need for FIFO to become empty
10*5f6e3f51Sshadow303 	during VIP access (which in turn requires locking the card, and locking
11*5f6e3f51Sshadow303 	is a dangerous thing in user mode as the app can suddenly die, taking
12*5f6e3f51Sshadow303 	the lock with it)
13*5f6e3f51Sshadow303 */
14*5f6e3f51Sshadow303 
15*5f6e3f51Sshadow303 #include "radeon_driver.h"
16*5f6e3f51Sshadow303 #include "mmio.h"
17*5f6e3f51Sshadow303 #include "vip_regs.h"
18*5f6e3f51Sshadow303 #include "bios_regs.h"
19*5f6e3f51Sshadow303 #include "theatre_regs.h"
20*5f6e3f51Sshadow303 
21*5f6e3f51Sshadow303 
22*5f6e3f51Sshadow303 // moved to bottom to avoid inlining
23*5f6e3f51Sshadow303 static bool Radeon_VIPWaitForIdle( device_info *di );
24*5f6e3f51Sshadow303 
25*5f6e3f51Sshadow303 
26*5f6e3f51Sshadow303 // read data from VIP
27*5f6e3f51Sshadow303 // CP lock must be hold
28*5f6e3f51Sshadow303 static bool do_VIPRead( device_info *di, uint channel, uint address, uint32 *data )
29*5f6e3f51Sshadow303 {
30*5f6e3f51Sshadow303 	vuint8 *regs = di->regs;
31*5f6e3f51Sshadow303 
32*5f6e3f51Sshadow303 	Radeon_WaitForFifo( di, 2 );
33*5f6e3f51Sshadow303 	// the 0x2000 is the nameless "register-read" flag
34*5f6e3f51Sshadow303 	OUTREG( regs, RADEON_VIPH_REG_ADDR, (channel << 14) | address | 0x2000 );
35*5f6e3f51Sshadow303 
36*5f6e3f51Sshadow303 	if( !Radeon_VIPWaitForIdle( di ))
37*5f6e3f51Sshadow303 		return false;
38*5f6e3f51Sshadow303 
39*5f6e3f51Sshadow303 	// enable VIP register cycle reads
40*5f6e3f51Sshadow303 	Radeon_WaitForFifo( di, 2 );
41*5f6e3f51Sshadow303 	OUTREGP( regs, RADEON_VIPH_TIMEOUT_STAT, 0,
42*5f6e3f51Sshadow303 		~RADEON_VIPH_TIMEOUT_STAT_AK_MASK & ~RADEON_VIPH_TIMEOUT_STAT_VIPH_REGR_DIS );
43*5f6e3f51Sshadow303 	//Radeon_WaitForIdle( di, false, false );
44*5f6e3f51Sshadow303 
45*5f6e3f51Sshadow303 	// this read starts a register cycle; the returned value has no meaning
46*5f6e3f51Sshadow303 	INREG( regs, RADEON_VIPH_REG_DATA );
47*5f6e3f51Sshadow303 
48*5f6e3f51Sshadow303 	if( !Radeon_VIPWaitForIdle( di ))
49*5f6e3f51Sshadow303 		return false;
50*5f6e3f51Sshadow303 
51*5f6e3f51Sshadow303 	//Radeon_WaitForIdle( di, false, false );
52*5f6e3f51Sshadow303 
53*5f6e3f51Sshadow303 	// register cycle is done, so disable any further cycle
54*5f6e3f51Sshadow303 	Radeon_WaitForFifo( di, 2 );
55*5f6e3f51Sshadow303 	OUTREGP( regs, RADEON_VIPH_TIMEOUT_STAT, RADEON_VIPH_TIMEOUT_STAT_VIPH_REGR_DIS,
56*5f6e3f51Sshadow303 		~RADEON_VIPH_TIMEOUT_STAT_AK_MASK & ~RADEON_VIPH_TIMEOUT_STAT_VIPH_REGR_DIS );
57*5f6e3f51Sshadow303 	//Radeon_WaitForIdle( di, false, false );
58*5f6e3f51Sshadow303 
59*5f6e3f51Sshadow303 	// get the data
60*5f6e3f51Sshadow303 	*data = INREG( regs, RADEON_VIPH_REG_DATA );
61*5f6e3f51Sshadow303 
62*5f6e3f51Sshadow303 	//SHOW_FLOW( 4, "channel=%d, address=%x, data=%lx", channel, address, *data );
63*5f6e3f51Sshadow303 
64*5f6e3f51Sshadow303 	if( !Radeon_VIPWaitForIdle( di ))
65*5f6e3f51Sshadow303 		return false;
66*5f6e3f51Sshadow303 
67*5f6e3f51Sshadow303 	// disable register cycle again (according to sample code)
68*5f6e3f51Sshadow303 	// IMHO, this is not necessary as it has been done before
69*5f6e3f51Sshadow303 	Radeon_WaitForFifo( di, 2 );
70*5f6e3f51Sshadow303 	OUTREGP( regs, RADEON_VIPH_TIMEOUT_STAT, RADEON_VIPH_TIMEOUT_STAT_VIPH_REGR_DIS,
71*5f6e3f51Sshadow303 		~RADEON_VIPH_TIMEOUT_STAT_AK_MASK & ~RADEON_VIPH_TIMEOUT_STAT_VIPH_REGR_DIS );
72*5f6e3f51Sshadow303 
73*5f6e3f51Sshadow303 	return true;
74*5f6e3f51Sshadow303 }
75*5f6e3f51Sshadow303 
76*5f6e3f51Sshadow303 // public function: read data from VIP
77*5f6e3f51Sshadow303 bool Radeon_VIPRead( device_info *di, uint channel, uint address, uint32 *data )
78*5f6e3f51Sshadow303 {
79*5f6e3f51Sshadow303 	bool res;
80*5f6e3f51Sshadow303 
81*5f6e3f51Sshadow303 	ACQUIRE_BEN( di->si->cp.lock );
82*5f6e3f51Sshadow303 
83*5f6e3f51Sshadow303 	res = do_VIPRead( di, channel, address, data );
84*5f6e3f51Sshadow303 
85*5f6e3f51Sshadow303 	RELEASE_BEN( di->si->cp.lock );
86*5f6e3f51Sshadow303 	return res;
87*5f6e3f51Sshadow303 }
88*5f6e3f51Sshadow303 
89*5f6e3f51Sshadow303 // write data to VIP
90*5f6e3f51Sshadow303 // not must be hold
91*5f6e3f51Sshadow303 static bool do_VIPWrite( device_info *di, uint8 channel, uint address, uint32 data )
92*5f6e3f51Sshadow303 {
93*5f6e3f51Sshadow303 	vuint8 *regs = di->regs;
94*5f6e3f51Sshadow303 	bool res;
95*5f6e3f51Sshadow303 
96*5f6e3f51Sshadow303 	Radeon_WaitForFifo( di, 2 );
97*5f6e3f51Sshadow303 	OUTREG( regs, RADEON_VIPH_REG_ADDR, (channel << 14) | (address & ~0x2000) );
98*5f6e3f51Sshadow303 
99*5f6e3f51Sshadow303 	if( !Radeon_VIPWaitForIdle( di ))
100*5f6e3f51Sshadow303 		return false;
101*5f6e3f51Sshadow303 
102*5f6e3f51Sshadow303 	//SHOW_FLOW( 4, "channel=%d, address=%x, data=%lx", channel, address, data );
103*5f6e3f51Sshadow303 
104*5f6e3f51Sshadow303 	Radeon_WaitForFifo( di, 2 );
105*5f6e3f51Sshadow303 	OUTREG( regs, RADEON_VIPH_REG_DATA, data );
106*5f6e3f51Sshadow303 
107*5f6e3f51Sshadow303 	res = Radeon_VIPWaitForIdle( di );
108*5f6e3f51Sshadow303 
109*5f6e3f51Sshadow303 	return res;
110*5f6e3f51Sshadow303 }
111*5f6e3f51Sshadow303 
112*5f6e3f51Sshadow303 // public function: write data to VIP
113*5f6e3f51Sshadow303 bool Radeon_VIPWrite( device_info *di, uint8 channel, uint address, uint32 data )
114*5f6e3f51Sshadow303 {
115*5f6e3f51Sshadow303 	bool res;
116*5f6e3f51Sshadow303 
117*5f6e3f51Sshadow303 	ACQUIRE_BEN( di->si->cp.lock );
118*5f6e3f51Sshadow303 
119*5f6e3f51Sshadow303 	res = do_VIPWrite( di, channel, address, data );
120*5f6e3f51Sshadow303 
121*5f6e3f51Sshadow303 	RELEASE_BEN( di->si->cp.lock );
122*5f6e3f51Sshadow303 	return res;
123*5f6e3f51Sshadow303 }
124*5f6e3f51Sshadow303 
125*5f6e3f51Sshadow303 
126*5f6e3f51Sshadow303 // reset VIP
127*5f6e3f51Sshadow303 static void VIPReset( device_info *di )
128*5f6e3f51Sshadow303 {
129*5f6e3f51Sshadow303 	vuint8 *regs = di->regs;
130*5f6e3f51Sshadow303 
131*5f6e3f51Sshadow303 	ACQUIRE_BEN( di->si->cp.lock );
132*5f6e3f51Sshadow303 
133*5f6e3f51Sshadow303 	Radeon_WaitForFifo( di, 5 );
134*5f6e3f51Sshadow303 	OUTREG( regs, RADEON_VIPH_CONTROL,
135*5f6e3f51Sshadow303 		4 |
136*5f6e3f51Sshadow303 		(15 << RADEON_VIPH_CONTROL_VIPH_MAX_WAIT_SHIFT) |
137*5f6e3f51Sshadow303 		RADEON_VIPH_CONTROL_VIPH_DMA_MODE |
138*5f6e3f51Sshadow303 		RADEON_VIPH_CONTROL_VIPH_EN );
139*5f6e3f51Sshadow303 	OUTREGP( regs, RADEON_VIPH_TIMEOUT_STAT, RADEON_VIPH_TIMEOUT_STAT_VIPH_REGR_DIS,
140*5f6e3f51Sshadow303 		~RADEON_VIPH_TIMEOUT_STAT_AK_MASK & ~RADEON_VIPH_TIMEOUT_STAT_VIPH_REGR_DIS);
141*5f6e3f51Sshadow303 	OUTREG( regs, RADEON_VIPH_DV_LAT,
142*5f6e3f51Sshadow303 		0xff |
143*5f6e3f51Sshadow303 		(4 << RADEON_VIPH_DV_LAT_VIPH_DV0_LAT_SHIFT) |
144*5f6e3f51Sshadow303 		(4 << RADEON_VIPH_DV_LAT_VIPH_DV1_LAT_SHIFT) |
145*5f6e3f51Sshadow303 		(4 << RADEON_VIPH_DV_LAT_VIPH_DV2_LAT_SHIFT) |
146*5f6e3f51Sshadow303 		(4 << RADEON_VIPH_DV_LAT_VIPH_DV3_LAT_SHIFT));
147*5f6e3f51Sshadow303 	OUTREG( regs, RADEON_VIPH_DMA_CHUNK,
148*5f6e3f51Sshadow303 		1 |
149*5f6e3f51Sshadow303 		(1 << RADEON_VIPH_DMA_CHUNK_VIPH_CH1_CHUNK_SHIFT) |
150*5f6e3f51Sshadow303 		(1 << RADEON_VIPH_DMA_CHUNK_VIPH_CH2_CHUNK_SHIFT) |
151*5f6e3f51Sshadow303 		(1 << RADEON_VIPH_DMA_CHUNK_VIPH_CH3_CHUNK_SHIFT));
152*5f6e3f51Sshadow303 	OUTREGP( regs, RADEON_TEST_DEBUG_CNTL, 0, ~RADEON_TEST_DEBUG_CNTL_OUT_EN );
153*5f6e3f51Sshadow303 
154*5f6e3f51Sshadow303 	RELEASE_BEN( di->si->cp.lock );
155*5f6e3f51Sshadow303 }
156*5f6e3f51Sshadow303 
157*5f6e3f51Sshadow303 
158*5f6e3f51Sshadow303 // find VIP channel of a device
159*5f6e3f51Sshadow303 // return:	>= 0 channel of device
160*5f6e3f51Sshadow303 //			< 0 no device found
161*5f6e3f51Sshadow303 int Radeon_FindVIPDevice( device_info *di, uint32 device_id )
162*5f6e3f51Sshadow303 {
163*5f6e3f51Sshadow303 	uint channel;
164*5f6e3f51Sshadow303 	uint32 cur_device_id;
165*5f6e3f51Sshadow303 
166*5f6e3f51Sshadow303 	// if card has no VIP port, let hardware detection fail;
167*5f6e3f51Sshadow303 	// in this case, noone will bother us again
168*5f6e3f51Sshadow303 	if( !di->has_vip )
169*5f6e3f51Sshadow303 		return -1;
170*5f6e3f51Sshadow303 
171*5f6e3f51Sshadow303 	VIPReset( di );
172*5f6e3f51Sshadow303 
173*5f6e3f51Sshadow303 	// there are up to 4 devices, connected to one of 4 channels
174*5f6e3f51Sshadow303 	for( channel = 0; channel < 4; ++channel ) {
175*5f6e3f51Sshadow303 		// read device id
176*5f6e3f51Sshadow303 		if( !Radeon_VIPRead( di, channel, RADEON_VIP_VENDOR_DEVICE_ID, &cur_device_id ))
177*5f6e3f51Sshadow303 			continue;
178*5f6e3f51Sshadow303 
179*5f6e3f51Sshadow303 		// compare device id directly
180*5f6e3f51Sshadow303 		if( cur_device_id == device_id )
181*5f6e3f51Sshadow303 			return channel;
182*5f6e3f51Sshadow303 	}
183*5f6e3f51Sshadow303 
184*5f6e3f51Sshadow303 	// couldn't find device
185*5f6e3f51Sshadow303 	return -1;
186*5f6e3f51Sshadow303 }
187*5f6e3f51Sshadow303 
188*5f6e3f51Sshadow303 
189*5f6e3f51Sshadow303 // check whether VIP host is idle
190*5f6e3f51Sshadow303 // lock must be hold
191*5f6e3f51Sshadow303 static status_t Radeon_VIPIdle( device_info *di )
192*5f6e3f51Sshadow303 {
193*5f6e3f51Sshadow303 	vuint8 *regs = di->regs;
194*5f6e3f51Sshadow303 	uint32 timeout;
195*5f6e3f51Sshadow303 
196*5f6e3f51Sshadow303 	//Radeon_WaitForIdle( di, false, false );
197*5f6e3f51Sshadow303 
198*5f6e3f51Sshadow303 	// if there is a stuck transaction, acknowledge that
199*5f6e3f51Sshadow303 	timeout = INREG( regs, RADEON_VIPH_TIMEOUT_STAT );
200*5f6e3f51Sshadow303 	if( (timeout & RADEON_VIPH_TIMEOUT_STAT_VIPH_REG_STAT) != 0 ) {
201*5f6e3f51Sshadow303 		OUTREGP( regs, RADEON_VIPH_TIMEOUT_STAT,
202*5f6e3f51Sshadow303 			RADEON_VIPH_TIMEOUT_STAT_VIPH_REG_AK,
203*5f6e3f51Sshadow303 			~RADEON_VIPH_TIMEOUT_STAT_VIPH_REG_AK & ~RADEON_VIPH_TIMEOUT_STAT_AK_MASK );
204*5f6e3f51Sshadow303 		if( (INREG( regs, RADEON_VIPH_CONTROL) & RADEON_VIPH_CONTROL_VIPH_REG_RDY) != 0 )
205*5f6e3f51Sshadow303 			return B_BUSY;
206*5f6e3f51Sshadow303 		else
207*5f6e3f51Sshadow303 			return B_ERROR;
208*5f6e3f51Sshadow303 	}
209*5f6e3f51Sshadow303 
210*5f6e3f51Sshadow303 	//Radeon_WaitForIdle( di, false, false );
211*5f6e3f51Sshadow303 
212*5f6e3f51Sshadow303 	if( (INREG( regs, RADEON_VIPH_CONTROL) & RADEON_VIPH_CONTROL_VIPH_REG_RDY) != 0 )
213*5f6e3f51Sshadow303 		return B_BUSY;
214*5f6e3f51Sshadow303 	else
215*5f6e3f51Sshadow303 		return B_OK;
216*5f6e3f51Sshadow303 }
217*5f6e3f51Sshadow303 
218*5f6e3f51Sshadow303 
219*5f6e3f51Sshadow303 // wait until VIP host is idle
220*5f6e3f51Sshadow303 // lock must be hold
221*5f6e3f51Sshadow303 static bool Radeon_VIPWaitForIdle( device_info *di )
222*5f6e3f51Sshadow303 {
223*5f6e3f51Sshadow303 	int i;
224*5f6e3f51Sshadow303 
225*5f6e3f51Sshadow303 	// wait 100x 1ms before giving up
226*5f6e3f51Sshadow303 	for( i = 0; i < 100; ++i ) {
227*5f6e3f51Sshadow303 		status_t res;
228*5f6e3f51Sshadow303 
229*5f6e3f51Sshadow303 		res = Radeon_VIPIdle( di );
230*5f6e3f51Sshadow303 		if( res != B_BUSY ) {
231*5f6e3f51Sshadow303 			if( res == B_OK )
232*5f6e3f51Sshadow303 				return true;
233*5f6e3f51Sshadow303 			else
234*5f6e3f51Sshadow303 				return false;
235*5f6e3f51Sshadow303 		}
236*5f6e3f51Sshadow303 
237*5f6e3f51Sshadow303 		snooze( 1000 );
238*5f6e3f51Sshadow303 	}
239*5f6e3f51Sshadow303 
240*5f6e3f51Sshadow303 	return false;
241*5f6e3f51Sshadow303 }
242