1 /* 2 Copyright (c) 2002, 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 Radeon_DisableIRQ( device_info *di ) 20 { 21 OUTREG( di->regs, RADEON_GEN_INT_CNTL, 0 ); 22 } 23 24 25 static uint32 thread_interrupt_work( vuint8 *regs, device_info *di, uint32 int_status ) 26 { 27 shared_info *si = di->si; 28 uint32 handled = B_HANDLED_INTERRUPT; 29 30 if( (int_status & RADEON_CRTC_VBLANK_STAT) != 0 && 31 si->ports[0].vblank >= 0 ) 32 { 33 int32 blocked; 34 35 ++di->vbi_count[0]; 36 37 if( (get_sem_count( si->ports[0].vblank, &blocked ) == B_OK) && (blocked < 0) ) { 38 //SHOW_FLOW( 3, "Signalled VBI 0 (%ldx)", -blocked ); 39 release_sem_etc( si->ports[0].vblank, -blocked, B_DO_NOT_RESCHEDULE ); 40 handled = B_INVOKE_SCHEDULER; 41 } 42 } 43 44 if( (int_status & RADEON_CRTC2_VBLANK_STAT) != 0 && 45 si->ports[1].vblank >= 0 ) 46 { 47 int32 blocked; 48 49 ++di->vbi_count[1]; 50 51 if( (get_sem_count( si->ports[1].vblank, &blocked ) == B_OK) && (blocked < 0) ) { 52 //SHOW_FLOW( 3, "Signalled VBI 1 (%ldx)", -blocked ); 53 release_sem_etc( si->ports[1].vblank, -blocked, B_DO_NOT_RESCHEDULE ); 54 handled = B_INVOKE_SCHEDULER; 55 } 56 } 57 58 return handled; 59 } 60 61 62 static int32 63 Radeon_Interrupt(void *data) 64 { 65 int32 handled = B_UNHANDLED_INTERRUPT; 66 device_info *di = (device_info *)data; 67 vuint8 *regs = di->regs; 68 int32 int_status; 69 70 int_status = INREG( regs, RADEON_GEN_INT_STATUS ); 71 72 if( int_status != 0 ) { 73 ++di->interrupt_count; 74 75 handled = thread_interrupt_work( regs, di, int_status ); 76 OUTREG( regs, RADEON_GEN_INT_STATUS, int_status ); 77 } 78 79 return handled; 80 } 81 82 static int32 timer_interrupt_func( timer *te ) 83 { 84 bigtime_t now = system_time(); 85 /* get the pointer to the device we're handling this time */ 86 device_info *di = ((timer_info *)te)->di; 87 shared_info *si = di->si; 88 vuint8 *regs = di->regs; 89 uint32 vbl_status = 0 /* read vertical blank status */; 90 int32 result = B_HANDLED_INTERRUPT; 91 92 /* are we suppoesed to handle interrupts still? */ 93 if( !di->shutdown_virtual_irq ) { 94 /* reschedule with same period by default */ 95 bigtime_t when = si->refresh_period; 96 timer *to; 97 98 /* if interrupts are "enabled", do our thing */ 99 if( si->enable_virtual_irq ) { 100 /* insert code to sync to interrupts here */ 101 if (!vbl_status) { 102 when -= si->blank_period - 4; 103 } 104 /* do the things we do when we notice a vertical retrace */ 105 result = thread_interrupt_work( regs, di, 106 RADEON_CRTC_VBLANK_STAT | 107 (di->has_crtc2 ? RADEON_CRTC_VBLANK_STAT : 0 )); 108 } 109 110 /* pick the "other" timer */ 111 to = (timer *)&(di->ti_a); 112 if (to == te) to = (timer *)&(di->ti_b); 113 /* our guess as to when we should be back */ 114 ((timer_info *)to)->when_target = now + when; 115 /* reschedule the interrupt */ 116 add_timer(to, timer_interrupt_func, ((timer_info *)to)->when_target, B_ONE_SHOT_ABSOLUTE_TIMER); 117 /* remember the currently active timer */ 118 di->current_timer = (timer_info *)to; 119 } 120 121 return result; 122 } 123 124 status_t Radeon_SetupIRQ( device_info *di, char *buffer ) 125 { 126 shared_info *si = di->si; 127 status_t result; 128 thread_id thid; 129 thread_info thinfo; 130 131 sprintf( buffer, "%04X_%04X_%02X%02X%02X VBI 0", 132 di->pcii.vendor_id, di->pcii.device_id, 133 di->pcii.bus, di->pcii.device, di->pcii.function ); 134 si->ports[0].vblank = create_sem( 0, buffer ); 135 if( si->ports[0].vblank < 0 ) { 136 result = si->ports[0].vblank; 137 goto err1; 138 } 139 140 sprintf( buffer, "%04X_%04X_%02X%02X%02X VBI 1", 141 di->pcii.vendor_id, di->pcii.device_id, 142 di->pcii.bus, di->pcii.device, di->pcii.function ); 143 si->ports[1].vblank = create_sem( 0, buffer ); 144 if( si->ports[1].vblank < 0 ) { 145 result = si->ports[1].vblank; 146 goto err2; 147 } 148 149 /* change the owner of the semaphores to the opener's team */ 150 /* this is required because apps can't aquire kernel semaphores */ 151 thid = find_thread(NULL); 152 get_thread_info(thid, &thinfo); 153 set_sem_owner(si->ports[0].vblank, thinfo.team); 154 set_sem_owner(si->ports[1].vblank, thinfo.team); 155 156 /* disable and clear any pending interrupts */ 157 Radeon_DisableIRQ( di ); 158 159 /* if we're faking interrupts */ 160 if ((di->pcii.u.h0.interrupt_pin == 0x00) || (di->pcii.u.h0.interrupt_line == 0xff)){ 161 SHOW_INFO0( 3, "We like to fake IRQ" ); 162 /* fake some kind of interrupt with a timer */ 163 di->shutdown_virtual_irq = false; 164 si->refresh_period = 16666; /* fake 60Hz to start */ 165 si->blank_period = si->refresh_period / 20; 166 167 di->ti_a.di = di; /* refer to ourself */ 168 di->ti_b.di = di; 169 di->current_timer = &(di->ti_a); 170 171 /* program the first timer interrupt, and it will handle the rest */ 172 result = add_timer((timer *)(di->current_timer), timer_interrupt_func, 173 si->refresh_period, B_ONE_SHOT_RELATIVE_TIMER); 174 if( result != B_OK ) 175 goto err3; 176 } else { 177 /* otherwise install our interrupt handler */ 178 result = install_io_interrupt_handler(di->pcii.u.h0.interrupt_line, 179 Radeon_Interrupt, (void *)di, 0); 180 if( result != B_OK ) 181 goto err3; 182 183 SHOW_INFO( 3, "installed IRQ @ %d", di->pcii.u.h0.interrupt_line ); 184 } 185 186 return B_OK; 187 188 err3: 189 delete_sem( si->ports[1].vblank ); 190 err2: 191 delete_sem( si->ports[0].vblank ); 192 err1: 193 return result; 194 } 195 196 197 void Radeon_CleanupIRQ( device_info *di ) 198 { 199 shared_info *si = di->si; 200 201 Radeon_DisableIRQ( di ); 202 203 /* if we were faking the interrupts */ 204 if ((di->pcii.u.h0.interrupt_pin == 0x00) || (di->pcii.u.h0.interrupt_line == 0xff)){ 205 /* stop our interrupt faking thread */ 206 di->shutdown_virtual_irq = true; 207 /* cancel the timer */ 208 /* we don't know which one is current, so cancel them both and ignore any error */ 209 cancel_timer((timer *)&(di->ti_a)); 210 cancel_timer((timer *)&(di->ti_b)); 211 } else { 212 /* remove interrupt handler */ 213 remove_io_interrupt_handler(di->pcii.u.h0.interrupt_line, Radeon_Interrupt, di); 214 } 215 216 delete_sem( si->ports[1].vblank ); 217 delete_sem( si->ports[0].vblank ); 218 219 si->ports[1].vblank = si->ports[0].vblank = 0; 220 } 221