1 /* 2 Copyright (c) 2002-2004, Thomas Kurschel 3 4 5 Part of Radeon kernel driver 6 7 Interrupt handling. Currently, none of this is used 8 as I haven't got the HW spec. 9 */ 10 11 #include "radeon_driver.h" 12 #include <stdio.h> 13 14 #include "mmio.h" 15 #include "rbbm_regs.h" 16 17 18 // disable all interrupts 19 static void 20 Radeon_DisableIRQ( device_info *di ) 21 { 22 OUTREG( di->regs, RADEON_GEN_INT_CNTL, 0 ); 23 } 24 25 26 // interrupt worker routine 27 // handles standard interrupts, i.e. VBI and DMA 28 static uint32 29 Radeon_ThreadInterruptWork( vuint8 *regs, device_info *di, uint32 int_status ) 30 { 31 shared_info *si = di->si; 32 uint32 handled = B_HANDLED_INTERRUPT; 33 34 if( (int_status & RADEON_CRTC_VBLANK_STAT) != 0 && 35 si->crtc[0].vblank >= 0 ) 36 { 37 int32 blocked; 38 39 ++di->vbi_count[0]; 40 41 if( (get_sem_count( si->crtc[0].vblank, &blocked ) == B_OK) && (blocked < 0) ) { 42 release_sem_etc( si->crtc[0].vblank, -blocked, B_DO_NOT_RESCHEDULE ); 43 handled = B_INVOKE_SCHEDULER; 44 } 45 } 46 47 if( (int_status & RADEON_CRTC2_VBLANK_STAT) != 0 && 48 si->crtc[1].vblank >= 0 ) 49 { 50 int32 blocked; 51 52 ++di->vbi_count[1]; 53 54 if( (get_sem_count( si->crtc[1].vblank, &blocked ) == B_OK) && (blocked < 0) ) { 55 release_sem_etc( si->crtc[1].vblank, -blocked, B_DO_NOT_RESCHEDULE ); 56 handled = B_INVOKE_SCHEDULER; 57 } 58 } 59 60 if( (int_status & RADEON_VIDDMA_STAT ) != 0 ) { 61 release_sem_etc( di->dma_sem, 1, B_DO_NOT_RESCHEDULE ); 62 handled = B_INVOKE_SCHEDULER; 63 } 64 65 return handled; 66 } 67 68 69 // Capture interrupt handler 70 static int32 71 Radeon_HandleCaptureInterrupt( vuint8 *regs, device_info *di, uint32 cap_status ) 72 { 73 int32 blocked; 74 uint32 handled = B_HANDLED_INTERRUPT; 75 76 cpu_status prev_irq_state = disable_interrupts(); 77 acquire_spinlock( &di->cap_spinlock ); 78 79 ++di->cap_counter; 80 di->cap_int_status = cap_status; 81 di->cap_timestamp = system_time(); 82 83 release_spinlock( &di->cap_spinlock ); 84 restore_interrupts( prev_irq_state ); 85 86 // don't release if semaphore count is positive, i.e. notifications are piling up 87 if( (get_sem_count( di->cap_sem, &blocked ) == B_OK) && (blocked <= 0) ) { 88 release_sem_etc( di->cap_sem, 1, B_DO_NOT_RESCHEDULE ); 89 handled = B_INVOKE_SCHEDULER; 90 } 91 92 // acknowledge IRQ 93 OUTREG( regs, RADEON_CAP_INT_STATUS, cap_status ); 94 95 return handled; 96 } 97 98 // Main interrupt handler 99 static int32 100 Radeon_Interrupt(void *data) 101 { 102 int32 handled = B_UNHANDLED_INTERRUPT; 103 device_info *di = (device_info *)data; 104 vuint8 *regs = di->regs; 105 int32 full_int_status, int_status; 106 107 // read possible IRQ reasons, ignoring any masked IRQs 108 full_int_status = INREG( regs, RADEON_GEN_INT_STATUS ); 109 int_status = full_int_status & INREG( regs, RADEON_GEN_INT_CNTL ); 110 111 if( int_status != 0 ) { 112 ++di->interrupt_count; 113 114 handled = Radeon_ThreadInterruptWork( regs, di, int_status ); 115 116 // acknowledge IRQ 117 OUTREG( regs, RADEON_GEN_INT_STATUS, int_status ); 118 } 119 120 // capture interrupt have no mask in GEN_INT_CNTL register; 121 // probably, ATI wanted to make capture interrupt control independant of main control 122 if( (full_int_status & RADEON_CAP0_INT_ACTIVE) != 0 ) { 123 int32 cap_status; 124 125 // same as before: only regard enabled IRQ reasons 126 cap_status = INREG( regs, RADEON_CAP_INT_STATUS ); 127 cap_status &= INREG( regs, RADEON_CAP_INT_CNTL ); 128 129 if( cap_status != 0 ) { 130 int32 cap_handled; 131 132 cap_handled = Radeon_HandleCaptureInterrupt( regs, di, cap_status ); 133 134 if( cap_handled == B_INVOKE_SCHEDULER || handled == B_INVOKE_SCHEDULER ) 135 handled = B_INVOKE_SCHEDULER; 136 else if( cap_handled == B_HANDLED_INTERRUPT ) 137 handled = B_HANDLED_INTERRUPT; 138 } 139 } 140 141 return handled; 142 } 143 144 static int32 timer_interrupt_func( timer *te ) 145 { 146 bigtime_t now = system_time(); 147 /* get the pointer to the device we're handling this time */ 148 device_info *di = ((timer_info *)te)->di; 149 shared_info *si = di->si; 150 vuint8 *regs = di->regs; 151 uint32 vbl_status = 0 /* read vertical blank status */; 152 int32 result = B_HANDLED_INTERRUPT; 153 154 /* are we suppoesed to handle interrupts still? */ 155 if( !di->shutdown_virtual_irq ) { 156 /* reschedule with same period by default */ 157 bigtime_t when = si->refresh_period; 158 timer *to; 159 160 /* if interrupts are "enabled", do our thing */ 161 if( si->enable_virtual_irq ) { 162 /* insert code to sync to interrupts here */ 163 if (!vbl_status) { 164 when -= si->blank_period - 4; 165 } 166 /* do the things we do when we notice a vertical retrace */ 167 result = Radeon_ThreadInterruptWork( regs, di, 168 RADEON_CRTC_VBLANK_STAT | 169 (di->num_crtc > 1 ? RADEON_CRTC2_VBLANK_STAT : 0 )); 170 } 171 172 /* pick the "other" timer */ 173 to = (timer *)&(di->ti_a); 174 if (to == te) to = (timer *)&(di->ti_b); 175 /* our guess as to when we should be back */ 176 ((timer_info *)to)->when_target = now + when; 177 /* reschedule the interrupt */ 178 add_timer(to, timer_interrupt_func, ((timer_info *)to)->when_target, B_ONE_SHOT_ABSOLUTE_TIMER); 179 /* remember the currently active timer */ 180 di->current_timer = (timer_info *)to; 181 } 182 183 return result; 184 } 185 186 187 // setup IRQ handlers. 188 // includes an VBI emulator via a timer (according to sample code), 189 // though this makes sense for one CRTC only 190 status_t 191 Radeon_SetupIRQ( device_info *di, char *buffer ) 192 { 193 shared_info *si = di->si; 194 status_t result; 195 thread_id thid; 196 thread_info thinfo; 197 198 sprintf( buffer, "%04X_%04X_%02X%02X%02X VBI 1", 199 di->pcii.vendor_id, di->pcii.device_id, 200 di->pcii.bus, di->pcii.device, di->pcii.function ); 201 si->crtc[0].vblank = create_sem( 0, buffer ); 202 if( si->crtc[0].vblank < 0 ) { 203 result = si->crtc[0].vblank; 204 goto err1; 205 } 206 207 si->crtc[1].vblank = 0; 208 209 if( di->num_crtc > 1 ) { 210 sprintf( buffer, "%04X_%04X_%02X%02X%02X VBI 2", 211 di->pcii.vendor_id, di->pcii.device_id, 212 di->pcii.bus, di->pcii.device, di->pcii.function ); 213 si->crtc[1].vblank = create_sem( 0, buffer ); 214 if( si->crtc[1].vblank < 0 ) { 215 result = si->crtc[1].vblank; 216 goto err2; 217 } 218 } 219 220 sprintf( buffer, "%04X_%04X_%02X%02X%02X Cap I", 221 di->pcii.vendor_id, di->pcii.device_id, 222 di->pcii.bus, di->pcii.device, di->pcii.function ); 223 di->cap_sem = create_sem( 0, buffer ); 224 if( di->cap_sem < 0 ) { 225 result = di->cap_sem; 226 goto err3; 227 } 228 229 di->cap_spinlock = 0; 230 231 sprintf( buffer, "%04X_%04X_%02X%02X%02X DMA I", 232 di->pcii.vendor_id, di->pcii.device_id, 233 di->pcii.bus, di->pcii.device, di->pcii.function ); 234 di->dma_sem = create_sem( 0, buffer ); 235 if( di->dma_sem < 0 ) { 236 result = di->dma_sem; 237 goto err4; 238 } 239 240 /* change the owner of the semaphores to the opener's team */ 241 /* this is required because apps can't aquire kernel semaphores */ 242 thid = find_thread(NULL); 243 get_thread_info(thid, &thinfo); 244 set_sem_owner(si->crtc[0].vblank, thinfo.team); 245 if( di->num_crtc > 1 ) 246 set_sem_owner(si->crtc[1].vblank, thinfo.team); 247 //set_sem_owner(di->cap_sem, thinfo.team); 248 249 /* disable all interrupts */ 250 Radeon_DisableIRQ( di ); 251 252 /* if we're faking interrupts */ 253 if ((di->pcii.u.h0.interrupt_pin == 0x00) || (di->pcii.u.h0.interrupt_line == 0xff)){ 254 SHOW_INFO0( 3, "We like to fake IRQ" ); 255 /* fake some kind of interrupt with a timer */ 256 di->shutdown_virtual_irq = false; 257 si->refresh_period = 16666; /* fake 60Hz to start */ 258 si->blank_period = si->refresh_period / 20; 259 260 di->ti_a.di = di; /* refer to ourself */ 261 di->ti_b.di = di; 262 di->current_timer = &(di->ti_a); 263 264 /* program the first timer interrupt, and it will handle the rest */ 265 result = add_timer((timer *)(di->current_timer), timer_interrupt_func, 266 si->refresh_period, B_ONE_SHOT_RELATIVE_TIMER); 267 if( result != B_OK ) 268 goto err5; 269 } else { 270 /* otherwise install our interrupt handler */ 271 result = install_io_interrupt_handler(di->pcii.u.h0.interrupt_line, 272 Radeon_Interrupt, (void *)di, 0); 273 if( result != B_OK ) 274 goto err5; 275 276 SHOW_INFO( 3, "installed IRQ @ %d", di->pcii.u.h0.interrupt_line ); 277 } 278 279 return B_OK; 280 281 err5: 282 delete_sem( di->dma_sem ); 283 err4: 284 delete_sem( di->cap_sem ); 285 err3: 286 if( di->num_crtc > 1 ) 287 delete_sem( si->crtc[1].vblank ); 288 err2: 289 delete_sem( si->crtc[0].vblank ); 290 err1: 291 return result; 292 } 293 294 295 // clean-up interrupt handling 296 void 297 Radeon_CleanupIRQ( device_info *di ) 298 { 299 shared_info *si = di->si; 300 301 Radeon_DisableIRQ( di ); 302 303 /* if we were faking the interrupts */ 304 if ((di->pcii.u.h0.interrupt_pin == 0x00) || (di->pcii.u.h0.interrupt_line == 0xff)){ 305 /* stop our interrupt faking thread */ 306 di->shutdown_virtual_irq = true; 307 /* cancel the timer */ 308 /* we don't know which one is current, so cancel them both and ignore any error */ 309 cancel_timer((timer *)&(di->ti_a)); 310 cancel_timer((timer *)&(di->ti_b)); 311 } else { 312 /* remove interrupt handler */ 313 remove_io_interrupt_handler(di->pcii.u.h0.interrupt_line, Radeon_Interrupt, di); 314 } 315 316 delete_sem( si->crtc[0].vblank ); 317 318 if( di->num_crtc > 1 ) 319 delete_sem( si->crtc[1].vblank ); 320 321 delete_sem( di->cap_sem ); 322 delete_sem( di->dma_sem ); 323 324 di->cap_sem = si->crtc[1].vblank = si->crtc[0].vblank = 0; 325 } 326