/*
	Copyright (c) 2002-05, Thomas Kurschel
	
 
	Part of Radeon accelerant
		
	Access to VIP
	
	This code must be in kernel because we need for FIFO to become empty
	during VIP access (which in turn requires locking the card, and locking
	is a dangerous thing in user mode as the app can suddenly die, taking
	the lock with it)
*/
 
#include "radeon_driver.h"
#include "mmio.h"
#include "vip_regs.h"
#include "bios_regs.h"
#include "theatre_regs.h"
 
 
// moved to bottom to avoid inlining
static bool Radeon_VIPWaitForIdle( device_info *di );
static status_t RADEON_VIPFifoIdle(device_info *di, uint8 channel);
 
 
// read data from VIP
// CP lock must be hold
static bool do_VIPRead( 
	device_info *di, uint channel, uint address, uint32 *data )
{
	vuint8 *regs = di->regs;
 
	Radeon_WaitForFifo( di, 2 );
	// the 0x2000 is the nameless "register-read" flag
	OUTREG( regs, RADEON_VIPH_REG_ADDR, (channel << 14) | address | 0x2000 );
	
	if( !Radeon_VIPWaitForIdle( di ))
		return false;
 
	// enable VIP register cycle reads
	Radeon_WaitForFifo( di, 2 );
	OUTREGP( regs, RADEON_VIPH_TIMEOUT_STAT, 0, 
		~RADEON_VIPH_TIMEOUT_STAT_AK_MASK & ~RADEON_VIPH_TIMEOUT_STAT_VIPH_REGR_DIS );
	//Radeon_WaitForIdle( di, false, false );
	
	// this read starts a register cycle; the returned value has no meaning
	INREG( regs, RADEON_VIPH_REG_DATA );
	
	if( !Radeon_VIPWaitForIdle( di ))
		return false;
		
	//Radeon_WaitForIdle( di, false, false );
	
	// register cycle is done, so disable any further cycle
	Radeon_WaitForFifo( di, 2 );
	OUTREGP( regs, RADEON_VIPH_TIMEOUT_STAT, RADEON_VIPH_TIMEOUT_STAT_VIPH_REGR_DIS, 
		~RADEON_VIPH_TIMEOUT_STAT_AK_MASK & ~RADEON_VIPH_TIMEOUT_STAT_VIPH_REGR_DIS );
	//Radeon_WaitForIdle( di, false, false );
	
	// get the data
	*data = INREG( regs, RADEON_VIPH_REG_DATA );
	
	//SHOW_FLOW( 4, "channel=%d, address=%x, data=%lx", channel, address, *data );
 
	if( !Radeon_VIPWaitForIdle( di ))
		return false;
 
	// disable register cycle again (according to sample code)
	// IMHO, this is not necessary as it has been done before
	Radeon_WaitForFifo( di, 2 );
	OUTREGP( regs, RADEON_VIPH_TIMEOUT_STAT, RADEON_VIPH_TIMEOUT_STAT_VIPH_REGR_DIS, 
		~RADEON_VIPH_TIMEOUT_STAT_AK_MASK & ~RADEON_VIPH_TIMEOUT_STAT_VIPH_REGR_DIS );
 
	return true;
}
 
// public function: read data from VIP
bool Radeon_VIPRead( 
	device_info *di, uint channel, uint address, uint32 *data, bool lock )
{
	bool res;
 
	if( lock )
		ACQUIRE_BEN( di->si->cp.lock );
	
	res = do_VIPRead( di, channel, address, data );
 
	if( lock )
		RELEASE_BEN( di->si->cp.lock );
		
//	SHOW_FLOW( 2, "address=%x, data=%lx, lock=%d", address, *data, lock );
 
	return res;
}
 
static bool do_VIPFifoRead(device_info *di, uint8 channel, uint32 address, uint32 count, uint8 *buffer)
{
   	vuint8 *regs = di->regs;
	uint32 status, tmp;
 
	if(count!=1)
	{
		SHOW_FLOW0( 2, "Attempt to access VIP bus with non-stadard transaction length\n");
		return false;
	}
		
	SHOW_FLOW( 2, "address=%lx, count=%ld ", address, count );
	
	Radeon_WaitForFifo( di, 2);
	SHOW_FLOW0( 2, "1");
	OUTREG( regs, RADEON_VIPH_REG_ADDR,  (channel << 14) | address | 0x3000);
	SHOW_FLOW0( 2, "3");
	while(B_BUSY == (status = RADEON_VIPFifoIdle( di , 0xff)));
	if(B_OK != status) return false;
	
	//	disable VIPH_REGR_DIS to enable VIP cycle.
	//	The LSB of VIPH_TIMEOUT_STAT are set to 0
	//	because 1 would have acknowledged various VIP
	//	interrupts unexpectedly       
	
	SHOW_FLOW0( 2, "4");
	Radeon_WaitForFifo( di, 2 ); // Radeon_WaitForIdle( di, false, false );
	SHOW_FLOW0( 2, "5");
	OUTREG( regs, RADEON_VIPH_TIMEOUT_STAT, 
		INREG( regs, RADEON_VIPH_TIMEOUT_STAT) & 
			(0xffffff00 & ~RADEON_VIPH_TIMEOUT_STAT_VIPH_REGR_DIS) );
	
	//	the value returned here is garbage.  The read merely initiates
	//	a register cycle
	SHOW_FLOW0( 2, "6");
	Radeon_WaitForFifo( di, 2 ); // Radeon_WaitForIdle( di, false, false );
	INREG( regs, RADEON_VIPH_REG_DATA);
	SHOW_FLOW0( 2, "7");
	while(B_BUSY == (status = RADEON_VIPFifoIdle( di , 0xff)));
	if(B_OK != status)  return false;
	
	//	set VIPH_REGR_DIS so that the read won't take too long.
	SHOW_FLOW0( 2, "8");
	Radeon_WaitForFifo( di, 2 ); // Radeon_WaitForIdle( di, false, false );
	SHOW_FLOW0( 2, "9");
	tmp = INREG( regs, RADEON_VIPH_TIMEOUT_STAT);
	OUTREG( regs, RADEON_VIPH_TIMEOUT_STAT, (tmp & 0xffffff00) | RADEON_VIPH_TIMEOUT_STAT_VIPH_REGR_DIS);
	
	SHOW_FLOW0( 2, "10");
	Radeon_WaitForFifo( di, 2 ); // Radeon_WaitForIdle( di, false, false );
	switch(count){
	   case 1:
	        *buffer=(uint8)(INREG( regs, RADEON_VIPH_REG_DATA) & 0xff);
	        break;
	   case 2:
	        *(uint16 *)buffer=(uint16) (INREG( regs, RADEON_VIPH_REG_DATA) & 0xffff);
	        break;
	   case 4:
	        *(uint32 *)buffer=(uint32) ( INREG( regs, RADEON_VIPH_REG_DATA) & 0xffffffff);
	        break;
	   }
	SHOW_FLOW0( 2, "11");
	while(B_BUSY == (status = RADEON_VIPFifoIdle( di , 0xff)));
	if(B_OK != status) return false;
	
	// so that reading VIPH_REG_DATA would not trigger unnecessary vip cycles.
	SHOW_FLOW0( 2, "12");
	OUTREG( regs, RADEON_VIPH_TIMEOUT_STAT, 
		(INREG( regs, RADEON_VIPH_TIMEOUT_STAT) & 0xffffff00) | RADEON_VIPH_TIMEOUT_STAT_VIPH_REGR_DIS);
	return true;
		
}
 
bool Radeon_VIPFifoRead(device_info *di, uint8 channel, uint32 address, uint32 count, uint8 *buffer, bool lock)
{
	bool res;
 
	if( lock )
		ACQUIRE_BEN( di->si->cp.lock );
	
	res = do_VIPFifoRead( di, channel, address, count, buffer );
	
	if( lock )
		RELEASE_BEN( di->si->cp.lock );
		
	//SHOW_FLOW( 2, "address=%x, data=%lx, lock=%d", address, *data, lock );
 
	return res;
}
 
// write data to VIP
// CP must be hold
static bool do_VIPWrite( device_info *di, uint8 channel, uint address, uint32 data )
{
	vuint8 *regs = di->regs;
 
	Radeon_WaitForFifo( di, 2 );
	OUTREG( regs, RADEON_VIPH_REG_ADDR, (channel << 14) | (address & ~0x2000) );
 
	if( !Radeon_VIPWaitForIdle( di )) return false;
		
	//SHOW_FLOW( 4, "channel=%d, address=%x, data=%lx", channel, address, data );
		
	Radeon_WaitForFifo( di, 2 );
	OUTREG( regs, RADEON_VIPH_REG_DATA, data );
	
	return Radeon_VIPWaitForIdle( di );
		
}
 
// public function: write data to VIP
bool Radeon_VIPWrite(device_info *di, uint8 channel, uint address, uint32 data, bool lock )
{
	bool res;
 
	//SHOW_FLOW( 2, "address=%x, data=%lx, lock=%d", address, data, lock );
 
	if( lock )
		ACQUIRE_BEN( di->si->cp.lock );
	
	res = do_VIPWrite( di, channel, address, data );
	
	if( lock )
		RELEASE_BEN( di->si->cp.lock );
	
	return res;
}
 
 
static bool do_VIPFifoWrite(device_info *di, uint8 channel, uint32 address, uint32 count, uint8 *buffer)
{
	vuint8 *regs = di->regs;
    
    uint32 status;
	uint32 i;
	
	SHOW_FLOW( 2, "address=%lx, count=%ld, ", address, count );
 
    Radeon_WaitForFifo( di, 2 );
    OUTREG( regs, RADEON_VIPH_REG_ADDR, ((channel << 14) | address | 0x1000) & ~0x2000 );
   	SHOW_FLOW0( 2, "1");
    while(B_BUSY == (status = RADEON_VIPFifoIdle( di, 0x0f)));
	
    
    if(B_OK != status){
		SHOW_FLOW( 2 ,"cannot write %x to VIPH_REG_ADDR\n", (unsigned int)address);
		return false;
	}
    
    SHOW_FLOW0( 2, "2");
	for (i = 0; i < count; i+=4)
	{
		Radeon_WaitForFifo( di, 2);
		SHOW_FLOW( 2, "count %ld", count);
		OUTREG( regs, RADEON_VIPH_REG_DATA, *(uint32*)(buffer + i));
		while(B_BUSY == (status = RADEON_VIPFifoIdle( di, 0x0f)));
    	if(B_OK != status)
		{
    		SHOW_FLOW0( 2 , "cannot write to VIPH_REG_DATA\n");
			return false;
	 	}
	}
				
    return true;
}
 
bool Radeon_VIPFifoWrite(device_info *di, uint8 channel, uint32 address, uint32 count, uint8 *buffer, bool lock)
{
    bool res;
 
	//SHOW_FLOW( 2, "address=%x, data=%lx, lock=%d", address, data, lock );
 
	if( lock )
		ACQUIRE_BEN( di->si->cp.lock );
	
	Radeon_VIPReset( di, false);
	res = do_VIPFifoWrite( di, channel, address, count, buffer );
	
	if( lock )
		RELEASE_BEN( di->si->cp.lock );
	
	return res;
}
 
 
// reset VIP
void Radeon_VIPReset( 
	device_info *di, bool lock )
{
	vuint8 *regs = di->regs;
 
	if( lock )
		ACQUIRE_BEN( di->si->cp.lock );
 
	Radeon_WaitForFifo( di, 5 ); // Radeon_WaitForIdle( di, false, false );
	switch(di->asic){
	    case rt_r200:
	    case rt_rs200:
	    case rt_rv200:
	    case rt_rs100:
		case rt_rv100:
		case rt_r100:
	    OUTREG( regs, RADEON_VIPH_CONTROL, 4 | 	(15 << RADEON_VIPH_CONTROL_VIPH_MAX_WAIT_SHIFT) |
			RADEON_VIPH_CONTROL_VIPH_DMA_MODE |	RADEON_VIPH_CONTROL_VIPH_EN ); // slowest, timeout in 16 phases
	    OUTREG( regs, RADEON_VIPH_TIMEOUT_STAT, (INREG( regs, RADEON_VIPH_TIMEOUT_STAT) & 0xFFFFFF00) | 
	    	RADEON_VIPH_TIMEOUT_STAT_VIPH_REGR_DIS);
	    OUTREG( regs, RADEON_VIPH_DV_LAT, 
	    		0xff |
				(4 << RADEON_VIPH_DV_LAT_VIPH_DV0_LAT_SHIFT) |
				(4 << RADEON_VIPH_DV_LAT_VIPH_DV1_LAT_SHIFT) |
				(4 << RADEON_VIPH_DV_LAT_VIPH_DV2_LAT_SHIFT) |
				(4 << RADEON_VIPH_DV_LAT_VIPH_DV3_LAT_SHIFT)); // set timeslice
	    OUTREG( regs, RADEON_VIPH_DMA_CHUNK, 0x151);
	    OUTREG( regs, RADEON_TEST_DEBUG_CNTL, INREG( regs, RADEON_TEST_DEBUG_CNTL) & (~RADEON_TEST_DEBUG_CNTL_OUT_EN));
	default:
		    OUTREG( regs, RADEON_VIPH_CONTROL, 9 | 	(15 << RADEON_VIPH_CONTROL_VIPH_MAX_WAIT_SHIFT) |
				RADEON_VIPH_CONTROL_VIPH_DMA_MODE |	RADEON_VIPH_CONTROL_VIPH_EN ); // slowest, timeout in 16 phases
	    OUTREG( regs, RADEON_VIPH_TIMEOUT_STAT, (INREG( regs, RADEON_VIPH_TIMEOUT_STAT) & 0xFFFFFF00) | 
		    	RADEON_VIPH_TIMEOUT_STAT_VIPH_REGR_DIS);
	    OUTREG( regs, RADEON_VIPH_DV_LAT, 
			    0xff |
				(4 << RADEON_VIPH_DV_LAT_VIPH_DV0_LAT_SHIFT) |
				(4 << RADEON_VIPH_DV_LAT_VIPH_DV1_LAT_SHIFT) |
				(4 << RADEON_VIPH_DV_LAT_VIPH_DV2_LAT_SHIFT) |
				(4 << RADEON_VIPH_DV_LAT_VIPH_DV3_LAT_SHIFT)); // set timeslice
	    OUTREG( regs, RADEON_VIPH_DMA_CHUNK, 0x0);
	    OUTREG( regs, RADEON_TEST_DEBUG_CNTL, INREG( regs, RADEON_TEST_DEBUG_CNTL) & (~RADEON_TEST_DEBUG_CNTL_OUT_EN));
	    break;
 
	} 
		
	if( lock )
		RELEASE_BEN( di->si->cp.lock );
}
 
 
// check whether VIP host is idle
// lock must be hold
static status_t Radeon_VIPIdle( 
	device_info *di )
{
	vuint8 *regs = di->regs;
	uint32 timeout;
	
	//Radeon_WaitForIdle( di, false, false );
	
	// if there is a stuck transaction, acknowledge that
	timeout = INREG( regs, RADEON_VIPH_TIMEOUT_STAT );
	if( (timeout & RADEON_VIPH_TIMEOUT_STAT_VIPH_REG_STAT) != 0 ) 
	{
		OUTREG( regs, RADEON_VIPH_TIMEOUT_STAT, 
			(timeout & 0xffffff00) | RADEON_VIPH_TIMEOUT_STAT_VIPH_REG_AK);
		return (INREG( regs, RADEON_VIPH_CONTROL) & 0x2000) ? B_BUSY : B_ERROR;
	}
	return (INREG( regs, RADEON_VIPH_CONTROL) & 0x2000) ? B_BUSY : B_OK;
}
 
static status_t RADEON_VIPFifoIdle(device_info *di, uint8 channel)
{
	vuint8 *regs = di->regs;
	uint32 timeout;
 
	timeout = INREG( regs, RADEON_VIPH_TIMEOUT_STAT);
	if((timeout & 0x0000000f) & channel) /* lockup ?? */
	{
		OUTREG( regs, RADEON_VIPH_TIMEOUT_STAT, (timeout & 0xfffffff0) | channel);
		return (INREG( regs, RADEON_VIPH_CONTROL) & 0x2000) ? B_BUSY : B_ERROR;
	}
	return (INREG( regs, RADEON_VIPH_CONTROL) & 0x2000) ? B_BUSY : B_OK ;
}
  	 
  	 
// wait until VIP host is idle
// lock must be hold
static bool Radeon_VIPWaitForIdle( 
	device_info *di )
{
	int i;
	
	// wait 100x 1ms before giving up
	for( i = 0; i < 100; ++i ) {
		status_t res;
		
		res = Radeon_VIPIdle( di );
		if( res != B_BUSY ) {
			if( res == B_OK )
				return true;
			else
				return false;
		}
			
		snooze( 1000 );
	}
	
	return false;
}
 
 
// find VIP channel of a device
// return:	>= 0 channel of device
//			< 0 no device found
int Radeon_FindVIPDevice( 
	device_info *di, uint32 device_id )
{
	uint channel;
	uint32 cur_device_id;
	
	// if card has no VIP port, let hardware detection fail;
	// in this case, noone will bother us again
	if( !di->has_vip ) {
		SHOW_FLOW0( 3, "This Device has no VIP Bus.");
		return -1;
	}
 
	ACQUIRE_BEN( di->si->cp.lock );
 
	Radeon_VIPReset( di, false );
 
	// there are up to 4 devices, connected to one of 4 channels	
	for( channel = 0; channel < 4; ++channel ) {
	
		// read device id
		if( !Radeon_VIPRead( di, channel, RADEON_VIP_VENDOR_DEVICE_ID, &cur_device_id, false ))	{
			SHOW_FLOW( 3, "No device found on channel %d", channel);
			continue;
		}
		
		// compare device id directly
		if( cur_device_id == device_id ) {
			SHOW_FLOW( 3, "Device %08lx found on channel %d", device_id, channel);
			RELEASE_BEN( di->si->cp.lock );
			return channel;
		}
	}
	
	RELEASE_BEN( di->si->cp.lock );
	
	// couldn't find device
	return -1;
}

V529 Odd semicolon ';' after 'while' operator.

V576 Incorrect format. Consider checking the fourth actual argument of the 'dprintf' function. The memsize type argument is expected.

V576 Incorrect format. Consider checking the fourth actual argument of the 'dprintf' function. The memsize type argument is expected.

V576 Incorrect format. Consider checking the fourth actual argument of the 'dprintf' function. The memsize type argument is expected.

V576 Incorrect format. Consider checking the fifth actual argument of the 'dprintf' function. The memsize type argument is expected.

V576 Incorrect format. Consider checking the fourth actual argument of the 'dprintf' function. The memsize type argument is expected.

V576 Incorrect format. Consider checking the fifth actual argument of the 'dprintf' function. The memsize type argument is expected.