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