OSDN Git Service

common ioctl to wait for vertical blank IRQs
[android-x86/external-libdrm.git] / shared-core / radeon_irq.c
1 /* radeon_irq.c -- IRQ handling for radeon -*- linux-c -*-
2  *
3  * Copyright (C) The Weather Channel, Inc.  2002.  All Rights Reserved.
4  * 
5  * The Weather Channel (TM) funded Tungsten Graphics to develop the
6  * initial release of the Radeon 8500 driver under the XFree86 license.
7  * This notice must be preserved.
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a
10  * copy of this software and associated documentation files (the "Software"),
11  * to deal in the Software without restriction, including without limitation
12  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13  * and/or sell copies of the Software, and to permit persons to whom the
14  * Software is furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice (including the next
17  * paragraph) shall be included in all copies or substantial portions of the
18  * Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23  * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
24  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
25  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  *
28  * Authors:
29  *    Keith Whitwell <keith@tungstengraphics.com>
30  */
31
32 #include "radeon.h"
33 #include "drmP.h"
34 #include "drm.h"
35 #include "radeon_drm.h"
36 #include "radeon_drv.h"
37
38 /* Interrupts - Used for device synchronization and flushing in the
39  * following circumstances:
40  *
41  * - Exclusive FB access with hw idle:
42  *    - Wait for GUI Idle (?) interrupt, then do normal flush.
43  *
44  * - Frame throttling, NV_fence:
45  *    - Drop marker irq's into command stream ahead of time.
46  *    - Wait on irq's with lock *not held*
47  *    - Check each for termination condition
48  *
49  * - Internally in cp_getbuffer, etc:
50  *    - as above, but wait with lock held???
51  *
52  * NOTE: These functions are misleadingly named -- the irq's aren't
53  * tied to dma at all, this is just a hangover from dri prehistory.
54  */
55
56 void DRM(dma_service)( DRM_IRQ_ARGS )
57 {
58         drm_device_t *dev = (drm_device_t *) arg;
59         drm_radeon_private_t *dev_priv = 
60            (drm_radeon_private_t *)dev->dev_private;
61         u32 stat, ack = 0;
62
63         /* Need to wait for fifo to drain?
64          */
65         stat = RADEON_READ(RADEON_GEN_INT_STATUS);
66
67         /* SW interrupt */
68         if (stat & RADEON_SW_INT_TEST) {
69                 ack |= RADEON_SW_INT_TEST_ACK;
70                 atomic_inc(&dev_priv->swi_received);
71 #ifdef __linux__
72                 wake_up_interruptible(&dev_priv->swi_queue);
73 #endif
74 #ifdef __FreeBSD__
75                 wakeup(&dev->vbl_queue);
76 #endif
77         }
78
79         /* VBLANK interrupt */
80         if (stat & RADEON_CRTC_VBLANK_STAT) {
81                 ack |= RADEON_CRTC_VBLANK_STAT_ACK;
82                 atomic_inc(&dev->vbl_received);
83 #ifdef __linux__
84                 wake_up_interruptible(&dev->vbl_queue);
85 #endif
86 #ifdef __FreeBSD__
87                 wakeup(&dev->vbl_queue);
88 #endif
89         }
90
91         if (ack)
92                 RADEON_WRITE(RADEON_GEN_INT_STATUS, ack);
93 }
94  
95
96 int radeon_emit_irq(drm_device_t *dev)
97 {
98         drm_radeon_private_t *dev_priv = dev->dev_private;
99         RING_LOCALS;
100
101         atomic_inc(&dev_priv->swi_emitted);
102
103         BEGIN_RING(2); 
104         OUT_RING( CP_PACKET0( RADEON_GEN_INT_STATUS, 0 ) );
105         OUT_RING( RADEON_SW_INT_FIRE );
106         ADVANCE_RING(); 
107         COMMIT_RING();
108
109         return atomic_read(&dev_priv->swi_emitted);
110 }
111
112
113 int radeon_wait_irq(drm_device_t *dev, int swi_nr)
114 {
115         drm_radeon_private_t *dev_priv = 
116            (drm_radeon_private_t *)dev->dev_private;
117 #ifdef __linux__
118         DECLARE_WAITQUEUE(entry, current);
119         unsigned long end = jiffies + HZ*3;
120 #endif /* __linux__ */
121         int ret = 0;
122
123         if (atomic_read(&dev_priv->swi_received) >= swi_nr)  
124                 return 0; 
125
126         dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE;
127
128 #ifdef __linux__
129         add_wait_queue(&dev_priv->swi_queue, &entry);
130
131         for (;;) {
132                 current->state = TASK_INTERRUPTIBLE;
133                 if (atomic_read(&dev_priv->swi_received) >= swi_nr) 
134                    break;
135                 if((signed)(end - jiffies) <= 0) {
136                         ret = -EBUSY;   /* Lockup?  Missed irq? */
137                         break;
138                 }
139                 schedule_timeout(HZ*3);
140                 if (signal_pending(current)) {
141                         ret = -EINTR;
142                         break;
143                 }
144         }
145
146         current->state = TASK_RUNNING;
147         remove_wait_queue(&dev_priv->swi_queue, &entry);
148         return ret;
149 #endif /* __linux__ */
150         
151 #ifdef __FreeBSD__
152         ret = tsleep( &dev_priv->swi_queue, PZERO | PCATCH, \
153                 "rdnirq", 3*hz );
154         if ( (ret == EWOULDBLOCK) || (ret == EINTR) )
155                 return DRM_ERR(EBUSY);
156         return ret;
157 #endif /* __FreeBSD__ */
158 }
159
160 int radeon_emit_and_wait_irq(drm_device_t *dev)
161 {
162         return radeon_wait_irq( dev, radeon_emit_irq(dev) );
163 }
164
165
166 /* Needs the lock as it touches the ring.
167  */
168 int radeon_irq_emit( DRM_IOCTL_ARGS )
169 {
170         DRM_DEVICE;
171         drm_radeon_private_t *dev_priv = dev->dev_private;
172         drm_radeon_irq_emit_t emit;
173         int result;
174
175         LOCK_TEST_WITH_RETURN( dev );
176
177         if ( !dev_priv ) {
178                 DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ );
179                 return DRM_ERR(EINVAL);
180         }
181
182         DRM_COPY_FROM_USER_IOCTL( emit, (drm_radeon_irq_emit_t *)data,
183                                   sizeof(emit) );
184
185         result = radeon_emit_irq( dev );
186
187         if ( DRM_COPY_TO_USER( emit.irq_seq, &result, sizeof(int) ) ) {
188                 DRM_ERROR( "copy_to_user\n" );
189                 return DRM_ERR(EFAULT);
190         }
191
192         return 0;
193 }
194
195
196 /* Doesn't need the hardware lock.
197  */
198 int radeon_irq_wait( DRM_IOCTL_ARGS )
199 {
200         DRM_DEVICE;
201         drm_radeon_private_t *dev_priv = dev->dev_private;
202         drm_radeon_irq_wait_t irqwait;
203
204         if ( !dev_priv ) {
205                 DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ );
206                 return DRM_ERR(EINVAL);
207         }
208
209         DRM_COPY_FROM_USER_IOCTL( irqwait, (drm_radeon_irq_wait_t *)data,
210                                   sizeof(irqwait) );
211
212         return radeon_wait_irq( dev, irqwait.irq_seq );
213 }
214
215  
216 void DRM(driver_irq_preinstall)( drm_device_t *dev ) {
217         drm_radeon_private_t *dev_priv =
218                 (drm_radeon_private_t *)dev->dev_private;
219         u32 tmp;
220
221         /* Disable *all* interrupts */
222         RADEON_WRITE( RADEON_GEN_INT_CNTL, 0 );
223
224         /* Clear bits if they're already high */
225         tmp = RADEON_READ( RADEON_GEN_INT_STATUS );
226         RADEON_WRITE( RADEON_GEN_INT_STATUS, tmp );
227 }
228
229 void DRM(driver_irq_postinstall)( drm_device_t *dev ) {
230         drm_radeon_private_t *dev_priv =
231                 (drm_radeon_private_t *)dev->dev_private;
232
233         atomic_set(&dev_priv->swi_received, 0);
234         atomic_set(&dev_priv->swi_emitted, 0);
235 #ifdef __linux__
236         init_waitqueue_head(&dev_priv->swi_queue);
237 #endif
238
239         /* Turn on SW and VBL ints */
240         RADEON_WRITE( RADEON_GEN_INT_CNTL,
241                       RADEON_CRTC_VBLANK_MASK | 
242                       RADEON_SW_INT_ENABLE );
243 }
244
245 void DRM(driver_irq_uninstall)( drm_device_t *dev ) {
246         drm_radeon_private_t *dev_priv =
247                 (drm_radeon_private_t *)dev->dev_private;
248         if ( dev_priv ) {
249                 /* Disable *all* interrupts */
250                 RADEON_WRITE( RADEON_GEN_INT_CNTL, 0 );
251         }
252 }