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