xref: /haiku/src/add-ons/kernel/drivers/graphics/radeon/irq.c (revision 93aeb8c3bc3f13cb1f282e3e749258a23790d947)
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