#include "radeon_driver.h"
#include "mmio.h"
#include "vip_regs.h"
#include "bios_regs.h"
#include "theatre_regs.h"
static bool Radeon_VIPWaitForIdle( device_info *di );
static status_t RADEON_VIPFifoIdle(device_info *di, uint8 channel);
static bool do_VIPRead(
device_info *di, uint 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;
Radeon_WaitForFifo( di, 2 );
OUTREGP( regs, RADEON_VIPH_TIMEOUT_STAT, 0,
~RADEON_VIPH_TIMEOUT_STAT_AK_MASK & ~RADEON_VIPH_TIMEOUT_STAT_VIPH_REGR_DIS );
INREG( regs, RADEON_VIPH_REG_DATA );
if( !Radeon_VIPWaitForIdle( di ))
return false;
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 );
*data = INREG( regs, RADEON_VIPH_REG_DATA );
if( !Radeon_VIPWaitForIdle( di ))
return false;
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;
}
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 );
return res;
}
static bool do_VIPFifoRead(device_info *di, uint8 channel, uint32 address, uint32 count, uint8 *buffer)
{
vuint8 *regs = di->regs;
status_t status;
uint32 tmp;
if(count!=1)
{
SHOW_FLOW0( 2, "Attempt to access VIP bus with non-stadard transaction length\n");
return false;
}
SHOW_FLOW( 2, "address=%" B_PRIx32 ", count=%" B_PRIu32 " ",
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;
SHOW_FLOW0( 2, "4");
Radeon_WaitForFifo( di, 2 );
SHOW_FLOW0( 2, "5");
OUTREG( regs, RADEON_VIPH_TIMEOUT_STAT,
INREG( regs, RADEON_VIPH_TIMEOUT_STAT) &
(0xffffff00 & ~RADEON_VIPH_TIMEOUT_STAT_VIPH_REGR_DIS) );
SHOW_FLOW0( 2, "6");
Radeon_WaitForFifo( di, 2 );
INREG( regs, RADEON_VIPH_REG_DATA);
SHOW_FLOW0( 2, "7");
while(B_BUSY == (status = RADEON_VIPFifoIdle( di , 0xff)));
if(B_OK != status) return false;
SHOW_FLOW0( 2, "8");
Radeon_WaitForFifo( di, 2 );
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 );
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;
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 );
return res;
}
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;
Radeon_WaitForFifo( di, 2 );
OUTREG( regs, RADEON_VIPH_REG_DATA, data );
return Radeon_VIPWaitForIdle( di );
}
bool Radeon_VIPWrite(device_info *di, uint8 channel, uint address, uint32 data, bool lock )
{
bool res;
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;
status_t status;
uint32 i;
SHOW_FLOW( 2, "address=%" B_PRIx32 ", count=%" B_PRIu32 ", ",
address, count );
Radeon_WaitForFifo( di, 2 );
OUTREG( regs, RADEON_VIPH_REG_ADDR,
((channel << 14) | address | 0x1000) & ~0x2000 );
SHOW_FLOW0( 2, "1");
do {
status = RADEON_VIPFifoIdle(di, 0x0f);
} while (status == B_BUSY);
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 %" B_PRIu32, count);
OUTREG( regs, RADEON_VIPH_REG_DATA, *(uint32*)(buffer + i));
do {
status = RADEON_VIPFifoIdle(di, 0x0f);
} while (status == B_BUSY);
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;
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;
}
void Radeon_VIPReset(
device_info *di, bool lock )
{
vuint8 *regs = di->regs;
if( lock )
ACQUIRE_BEN( di->si->cp.lock );
Radeon_WaitForFifo( di, 5 );
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 );
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));
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 );
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));
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 );
}
static status_t Radeon_VIPIdle(
device_info *di )
{
vuint8 *regs = di->regs;
uint32 timeout;
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)
{
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 ;
}
static bool Radeon_VIPWaitForIdle(
device_info *di )
{
int i;
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;
}
int Radeon_FindVIPDevice(
device_info *di, uint32 device_id )
{
uint channel;
uint32 cur_device_id;
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 );
for( channel = 0; channel < 4; ++channel ) {
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;
}
if( cur_device_id == device_id ) {
SHOW_FLOW( 3, "Device %08" B_PRIx32 " found on channel %d",
device_id, channel);
RELEASE_BEN( di->si->cp.lock );
return channel;
}
}
RELEASE_BEN( di->si->cp.lock );
return -1;
}