diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/char/drm/drm_irq.c |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/char/drm/drm_irq.c')
-rw-r--r-- | drivers/char/drm/drm_irq.c | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/drivers/char/drm/drm_irq.c b/drivers/char/drm/drm_irq.c new file mode 100644 index 000000000000..2e236ebcf27b --- /dev/null +++ b/drivers/char/drm/drm_irq.c | |||
@@ -0,0 +1,370 @@ | |||
1 | /** | ||
2 | * \file drm_irq.h | ||
3 | * IRQ support | ||
4 | * | ||
5 | * \author Rickard E. (Rik) Faith <faith@valinux.com> | ||
6 | * \author Gareth Hughes <gareth@valinux.com> | ||
7 | */ | ||
8 | |||
9 | /* | ||
10 | * Created: Fri Mar 19 14:30:16 1999 by faith@valinux.com | ||
11 | * | ||
12 | * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. | ||
13 | * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. | ||
14 | * All Rights Reserved. | ||
15 | * | ||
16 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
17 | * copy of this software and associated documentation files (the "Software"), | ||
18 | * to deal in the Software without restriction, including without limitation | ||
19 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
20 | * and/or sell copies of the Software, and to permit persons to whom the | ||
21 | * Software is furnished to do so, subject to the following conditions: | ||
22 | * | ||
23 | * The above copyright notice and this permission notice (including the next | ||
24 | * paragraph) shall be included in all copies or substantial portions of the | ||
25 | * Software. | ||
26 | * | ||
27 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
28 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
29 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
30 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
31 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
32 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
33 | * OTHER DEALINGS IN THE SOFTWARE. | ||
34 | */ | ||
35 | |||
36 | #include "drmP.h" | ||
37 | |||
38 | #include <linux/interrupt.h> /* For task queue support */ | ||
39 | |||
40 | /** | ||
41 | * Get interrupt from bus id. | ||
42 | * | ||
43 | * \param inode device inode. | ||
44 | * \param filp file pointer. | ||
45 | * \param cmd command. | ||
46 | * \param arg user argument, pointing to a drm_irq_busid structure. | ||
47 | * \return zero on success or a negative number on failure. | ||
48 | * | ||
49 | * Finds the PCI device with the specified bus id and gets its IRQ number. | ||
50 | * This IOCTL is deprecated, and will now return EINVAL for any busid not equal | ||
51 | * to that of the device that this DRM instance attached to. | ||
52 | */ | ||
53 | int drm_irq_by_busid(struct inode *inode, struct file *filp, | ||
54 | unsigned int cmd, unsigned long arg) | ||
55 | { | ||
56 | drm_file_t *priv = filp->private_data; | ||
57 | drm_device_t *dev = priv->head->dev; | ||
58 | drm_irq_busid_t __user *argp = (void __user *)arg; | ||
59 | drm_irq_busid_t p; | ||
60 | |||
61 | if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) | ||
62 | return -EINVAL; | ||
63 | |||
64 | if (copy_from_user(&p, argp, sizeof(p))) | ||
65 | return -EFAULT; | ||
66 | |||
67 | if ((p.busnum >> 8) != dev->pci_domain || | ||
68 | (p.busnum & 0xff) != dev->pci_bus || | ||
69 | p.devnum != dev->pci_slot || | ||
70 | p.funcnum != dev->pci_func) | ||
71 | return -EINVAL; | ||
72 | |||
73 | p.irq = dev->irq; | ||
74 | |||
75 | DRM_DEBUG("%d:%d:%d => IRQ %d\n", | ||
76 | p.busnum, p.devnum, p.funcnum, p.irq); | ||
77 | if (copy_to_user(argp, &p, sizeof(p))) | ||
78 | return -EFAULT; | ||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | /** | ||
83 | * Install IRQ handler. | ||
84 | * | ||
85 | * \param dev DRM device. | ||
86 | * \param irq IRQ number. | ||
87 | * | ||
88 | * Initializes the IRQ related data, and setups drm_device::vbl_queue. Installs the handler, calling the driver | ||
89 | * \c drm_driver_irq_preinstall() and \c drm_driver_irq_postinstall() functions | ||
90 | * before and after the installation. | ||
91 | */ | ||
92 | int drm_irq_install( drm_device_t *dev ) | ||
93 | { | ||
94 | int ret; | ||
95 | unsigned long sh_flags=0; | ||
96 | |||
97 | if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) | ||
98 | return -EINVAL; | ||
99 | |||
100 | if ( dev->irq == 0 ) | ||
101 | return -EINVAL; | ||
102 | |||
103 | down( &dev->struct_sem ); | ||
104 | |||
105 | /* Driver must have been initialized */ | ||
106 | if ( !dev->dev_private ) { | ||
107 | up( &dev->struct_sem ); | ||
108 | return -EINVAL; | ||
109 | } | ||
110 | |||
111 | if ( dev->irq_enabled ) { | ||
112 | up( &dev->struct_sem ); | ||
113 | return -EBUSY; | ||
114 | } | ||
115 | dev->irq_enabled = 1; | ||
116 | up( &dev->struct_sem ); | ||
117 | |||
118 | DRM_DEBUG( "%s: irq=%d\n", __FUNCTION__, dev->irq ); | ||
119 | |||
120 | if (drm_core_check_feature(dev, DRIVER_IRQ_VBL)) { | ||
121 | init_waitqueue_head(&dev->vbl_queue); | ||
122 | |||
123 | spin_lock_init( &dev->vbl_lock ); | ||
124 | |||
125 | INIT_LIST_HEAD( &dev->vbl_sigs.head ); | ||
126 | |||
127 | dev->vbl_pending = 0; | ||
128 | } | ||
129 | |||
130 | /* Before installing handler */ | ||
131 | dev->driver->irq_preinstall(dev); | ||
132 | |||
133 | /* Install handler */ | ||
134 | if (drm_core_check_feature(dev, DRIVER_IRQ_SHARED)) | ||
135 | sh_flags = SA_SHIRQ; | ||
136 | |||
137 | ret = request_irq( dev->irq, dev->driver->irq_handler, | ||
138 | sh_flags, dev->devname, dev ); | ||
139 | if ( ret < 0 ) { | ||
140 | down( &dev->struct_sem ); | ||
141 | dev->irq_enabled = 0; | ||
142 | up( &dev->struct_sem ); | ||
143 | return ret; | ||
144 | } | ||
145 | |||
146 | /* After installing handler */ | ||
147 | dev->driver->irq_postinstall(dev); | ||
148 | |||
149 | return 0; | ||
150 | } | ||
151 | |||
152 | /** | ||
153 | * Uninstall the IRQ handler. | ||
154 | * | ||
155 | * \param dev DRM device. | ||
156 | * | ||
157 | * Calls the driver's \c drm_driver_irq_uninstall() function, and stops the irq. | ||
158 | */ | ||
159 | int drm_irq_uninstall( drm_device_t *dev ) | ||
160 | { | ||
161 | int irq_enabled; | ||
162 | |||
163 | if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) | ||
164 | return -EINVAL; | ||
165 | |||
166 | down( &dev->struct_sem ); | ||
167 | irq_enabled = dev->irq_enabled; | ||
168 | dev->irq_enabled = 0; | ||
169 | up( &dev->struct_sem ); | ||
170 | |||
171 | if ( !irq_enabled ) | ||
172 | return -EINVAL; | ||
173 | |||
174 | DRM_DEBUG( "%s: irq=%d\n", __FUNCTION__, dev->irq ); | ||
175 | |||
176 | dev->driver->irq_uninstall(dev); | ||
177 | |||
178 | free_irq( dev->irq, dev ); | ||
179 | |||
180 | return 0; | ||
181 | } | ||
182 | EXPORT_SYMBOL(drm_irq_uninstall); | ||
183 | |||
184 | /** | ||
185 | * IRQ control ioctl. | ||
186 | * | ||
187 | * \param inode device inode. | ||
188 | * \param filp file pointer. | ||
189 | * \param cmd command. | ||
190 | * \param arg user argument, pointing to a drm_control structure. | ||
191 | * \return zero on success or a negative number on failure. | ||
192 | * | ||
193 | * Calls irq_install() or irq_uninstall() according to \p arg. | ||
194 | */ | ||
195 | int drm_control( struct inode *inode, struct file *filp, | ||
196 | unsigned int cmd, unsigned long arg ) | ||
197 | { | ||
198 | drm_file_t *priv = filp->private_data; | ||
199 | drm_device_t *dev = priv->head->dev; | ||
200 | drm_control_t ctl; | ||
201 | |||
202 | /* if we haven't irq we fallback for compatibility reasons - this used to be a separate function in drm_dma.h */ | ||
203 | |||
204 | if ( copy_from_user( &ctl, (drm_control_t __user *)arg, sizeof(ctl) ) ) | ||
205 | return -EFAULT; | ||
206 | |||
207 | switch ( ctl.func ) { | ||
208 | case DRM_INST_HANDLER: | ||
209 | if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) | ||
210 | return 0; | ||
211 | if (dev->if_version < DRM_IF_VERSION(1, 2) && | ||
212 | ctl.irq != dev->irq) | ||
213 | return -EINVAL; | ||
214 | return drm_irq_install( dev ); | ||
215 | case DRM_UNINST_HANDLER: | ||
216 | if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) | ||
217 | return 0; | ||
218 | return drm_irq_uninstall( dev ); | ||
219 | default: | ||
220 | return -EINVAL; | ||
221 | } | ||
222 | } | ||
223 | |||
224 | /** | ||
225 | * Wait for VBLANK. | ||
226 | * | ||
227 | * \param inode device inode. | ||
228 | * \param filp file pointer. | ||
229 | * \param cmd command. | ||
230 | * \param data user argument, pointing to a drm_wait_vblank structure. | ||
231 | * \return zero on success or a negative number on failure. | ||
232 | * | ||
233 | * Verifies the IRQ is installed. | ||
234 | * | ||
235 | * If a signal is requested checks if this task has already scheduled the same signal | ||
236 | * for the same vblank sequence number - nothing to be done in | ||
237 | * that case. If the number of tasks waiting for the interrupt exceeds 100 the | ||
238 | * function fails. Otherwise adds a new entry to drm_device::vbl_sigs for this | ||
239 | * task. | ||
240 | * | ||
241 | * If a signal is not requested, then calls vblank_wait(). | ||
242 | */ | ||
243 | int drm_wait_vblank( DRM_IOCTL_ARGS ) | ||
244 | { | ||
245 | drm_file_t *priv = filp->private_data; | ||
246 | drm_device_t *dev = priv->head->dev; | ||
247 | drm_wait_vblank_t __user *argp = (void __user *)data; | ||
248 | drm_wait_vblank_t vblwait; | ||
249 | struct timeval now; | ||
250 | int ret = 0; | ||
251 | unsigned int flags; | ||
252 | |||
253 | if (!drm_core_check_feature(dev, DRIVER_IRQ_VBL)) | ||
254 | return -EINVAL; | ||
255 | |||
256 | if (!dev->irq) | ||
257 | return -EINVAL; | ||
258 | |||
259 | DRM_COPY_FROM_USER_IOCTL( vblwait, argp, sizeof(vblwait) ); | ||
260 | |||
261 | switch ( vblwait.request.type & ~_DRM_VBLANK_FLAGS_MASK ) { | ||
262 | case _DRM_VBLANK_RELATIVE: | ||
263 | vblwait.request.sequence += atomic_read( &dev->vbl_received ); | ||
264 | vblwait.request.type &= ~_DRM_VBLANK_RELATIVE; | ||
265 | case _DRM_VBLANK_ABSOLUTE: | ||
266 | break; | ||
267 | default: | ||
268 | return -EINVAL; | ||
269 | } | ||
270 | |||
271 | flags = vblwait.request.type & _DRM_VBLANK_FLAGS_MASK; | ||
272 | |||
273 | if ( flags & _DRM_VBLANK_SIGNAL ) { | ||
274 | unsigned long irqflags; | ||
275 | drm_vbl_sig_t *vbl_sig; | ||
276 | |||
277 | vblwait.reply.sequence = atomic_read( &dev->vbl_received ); | ||
278 | |||
279 | spin_lock_irqsave( &dev->vbl_lock, irqflags ); | ||
280 | |||
281 | /* Check if this task has already scheduled the same signal | ||
282 | * for the same vblank sequence number; nothing to be done in | ||
283 | * that case | ||
284 | */ | ||
285 | list_for_each_entry( vbl_sig, &dev->vbl_sigs.head, head ) { | ||
286 | if (vbl_sig->sequence == vblwait.request.sequence | ||
287 | && vbl_sig->info.si_signo == vblwait.request.signal | ||
288 | && vbl_sig->task == current) | ||
289 | { | ||
290 | spin_unlock_irqrestore( &dev->vbl_lock, irqflags ); | ||
291 | goto done; | ||
292 | } | ||
293 | } | ||
294 | |||
295 | if ( dev->vbl_pending >= 100 ) { | ||
296 | spin_unlock_irqrestore( &dev->vbl_lock, irqflags ); | ||
297 | return -EBUSY; | ||
298 | } | ||
299 | |||
300 | dev->vbl_pending++; | ||
301 | |||
302 | spin_unlock_irqrestore( &dev->vbl_lock, irqflags ); | ||
303 | |||
304 | if ( !( vbl_sig = drm_alloc( sizeof( drm_vbl_sig_t ), DRM_MEM_DRIVER ) ) ) { | ||
305 | return -ENOMEM; | ||
306 | } | ||
307 | |||
308 | memset( (void *)vbl_sig, 0, sizeof(*vbl_sig) ); | ||
309 | |||
310 | vbl_sig->sequence = vblwait.request.sequence; | ||
311 | vbl_sig->info.si_signo = vblwait.request.signal; | ||
312 | vbl_sig->task = current; | ||
313 | |||
314 | spin_lock_irqsave( &dev->vbl_lock, irqflags ); | ||
315 | |||
316 | list_add_tail( (struct list_head *) vbl_sig, &dev->vbl_sigs.head ); | ||
317 | |||
318 | spin_unlock_irqrestore( &dev->vbl_lock, irqflags ); | ||
319 | } else { | ||
320 | if (dev->driver->vblank_wait) | ||
321 | ret = dev->driver->vblank_wait( dev, &vblwait.request.sequence ); | ||
322 | |||
323 | do_gettimeofday( &now ); | ||
324 | vblwait.reply.tval_sec = now.tv_sec; | ||
325 | vblwait.reply.tval_usec = now.tv_usec; | ||
326 | } | ||
327 | |||
328 | done: | ||
329 | DRM_COPY_TO_USER_IOCTL( argp, vblwait, sizeof(vblwait) ); | ||
330 | |||
331 | return ret; | ||
332 | } | ||
333 | |||
334 | /** | ||
335 | * Send the VBLANK signals. | ||
336 | * | ||
337 | * \param dev DRM device. | ||
338 | * | ||
339 | * Sends a signal for each task in drm_device::vbl_sigs and empties the list. | ||
340 | * | ||
341 | * If a signal is not requested, then calls vblank_wait(). | ||
342 | */ | ||
343 | void drm_vbl_send_signals( drm_device_t *dev ) | ||
344 | { | ||
345 | struct list_head *list, *tmp; | ||
346 | drm_vbl_sig_t *vbl_sig; | ||
347 | unsigned int vbl_seq = atomic_read( &dev->vbl_received ); | ||
348 | unsigned long flags; | ||
349 | |||
350 | spin_lock_irqsave( &dev->vbl_lock, flags ); | ||
351 | |||
352 | list_for_each_safe( list, tmp, &dev->vbl_sigs.head ) { | ||
353 | vbl_sig = list_entry( list, drm_vbl_sig_t, head ); | ||
354 | if ( ( vbl_seq - vbl_sig->sequence ) <= (1<<23) ) { | ||
355 | vbl_sig->info.si_code = vbl_seq; | ||
356 | send_sig_info( vbl_sig->info.si_signo, &vbl_sig->info, vbl_sig->task ); | ||
357 | |||
358 | list_del( list ); | ||
359 | |||
360 | drm_free( vbl_sig, sizeof(*vbl_sig), DRM_MEM_DRIVER ); | ||
361 | |||
362 | dev->vbl_pending--; | ||
363 | } | ||
364 | } | ||
365 | |||
366 | spin_unlock_irqrestore( &dev->vbl_lock, flags ); | ||
367 | } | ||
368 | EXPORT_SYMBOL(drm_vbl_send_signals); | ||
369 | |||
370 | |||