61ed5158f78383a1f5497372b770892227db92b9
[linux-2.6-block.git] / drivers / gpu / drm / drm_irq.c
1 /**
2  * \file drm_irq.c
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 file_priv DRM file private.
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 drm_device *dev, void *data,
54                      struct drm_file *file_priv)
55 {
56         struct drm_irq_busid *p = data;
57
58         if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
59                 return -EINVAL;
60
61         if ((p->busnum >> 8) != drm_get_pci_domain(dev) ||
62             (p->busnum & 0xff) != dev->pdev->bus->number ||
63             p->devnum != PCI_SLOT(dev->pdev->devfn) || p->funcnum != PCI_FUNC(dev->pdev->devfn))
64                 return -EINVAL;
65
66         p->irq = dev->pdev->irq;
67
68         DRM_DEBUG("%d:%d:%d => IRQ %d\n", p->busnum, p->devnum, p->funcnum,
69                   p->irq);
70
71         return 0;
72 }
73
74 /**
75  * Install IRQ handler.
76  *
77  * \param dev DRM device.
78  * \param irq IRQ number.
79  *
80  * Initializes the IRQ related data, and setups drm_device::vbl_queue. Installs the handler, calling the driver
81  * \c drm_driver_irq_preinstall() and \c drm_driver_irq_postinstall() functions
82  * before and after the installation.
83  */
84 static int drm_irq_install(struct drm_device * dev)
85 {
86         int ret;
87         unsigned long sh_flags = 0;
88
89         if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
90                 return -EINVAL;
91
92         if (dev->pdev->irq == 0)
93                 return -EINVAL;
94
95         mutex_lock(&dev->struct_mutex);
96
97         /* Driver must have been initialized */
98         if (!dev->dev_private) {
99                 mutex_unlock(&dev->struct_mutex);
100                 return -EINVAL;
101         }
102
103         if (dev->irq_enabled) {
104                 mutex_unlock(&dev->struct_mutex);
105                 return -EBUSY;
106         }
107         dev->irq_enabled = 1;
108         mutex_unlock(&dev->struct_mutex);
109
110         DRM_DEBUG("irq=%d\n", dev->pdev->irq);
111
112         if (drm_core_check_feature(dev, DRIVER_IRQ_VBL)) {
113                 init_waitqueue_head(&dev->vbl_queue);
114
115                 spin_lock_init(&dev->vbl_lock);
116
117                 INIT_LIST_HEAD(&dev->vbl_sigs);
118                 INIT_LIST_HEAD(&dev->vbl_sigs2);
119
120                 dev->vbl_pending = 0;
121         }
122
123         /* Before installing handler */
124         dev->driver->irq_preinstall(dev);
125
126         /* Install handler */
127         if (drm_core_check_feature(dev, DRIVER_IRQ_SHARED))
128                 sh_flags = IRQF_SHARED;
129
130         ret = request_irq(dev->pdev->irq, dev->driver->irq_handler,
131                           sh_flags, dev->devname, dev);
132         /* Expose the device irq number to drivers that want to export it for
133          * whatever reason.
134          */
135         dev->irq = dev->pdev->irq;
136         if (ret < 0) {
137                 mutex_lock(&dev->struct_mutex);
138                 dev->irq_enabled = 0;
139                 mutex_unlock(&dev->struct_mutex);
140                 return ret;
141         }
142
143         /* After installing handler */
144         dev->driver->irq_postinstall(dev);
145
146         return 0;
147 }
148
149 /**
150  * Uninstall the IRQ handler.
151  *
152  * \param dev DRM device.
153  *
154  * Calls the driver's \c drm_driver_irq_uninstall() function, and stops the irq.
155  */
156 int drm_irq_uninstall(struct drm_device * dev)
157 {
158         int irq_enabled;
159
160         if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
161                 return -EINVAL;
162
163         mutex_lock(&dev->struct_mutex);
164         irq_enabled = dev->irq_enabled;
165         dev->irq_enabled = 0;
166         mutex_unlock(&dev->struct_mutex);
167
168         if (!irq_enabled)
169                 return -EINVAL;
170
171         DRM_DEBUG("irq=%d\n", dev->pdev->irq);
172
173         dev->driver->irq_uninstall(dev);
174
175         free_irq(dev->pdev->irq, dev);
176
177         dev->locked_tasklet_func = NULL;
178
179         return 0;
180 }
181
182 EXPORT_SYMBOL(drm_irq_uninstall);
183
184 /**
185  * IRQ control ioctl.
186  *
187  * \param inode device inode.
188  * \param file_priv DRM file private.
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 drm_device *dev, void *data,
196                 struct drm_file *file_priv)
197 {
198         struct drm_control *ctl = data;
199
200         /* if we haven't irq we fallback for compatibility reasons - this used to be a separate function in drm_dma.h */
201
202
203         switch (ctl->func) {
204         case DRM_INST_HANDLER:
205                 if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
206                         return 0;
207                 if (dev->if_version < DRM_IF_VERSION(1, 2) &&
208                     ctl->irq != dev->pdev->irq)
209                         return -EINVAL;
210                 return drm_irq_install(dev);
211         case DRM_UNINST_HANDLER:
212                 if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
213                         return 0;
214                 return drm_irq_uninstall(dev);
215         default:
216                 return -EINVAL;
217         }
218 }
219
220 /**
221  * Wait for VBLANK.
222  *
223  * \param inode device inode.
224  * \param file_priv DRM file private.
225  * \param cmd command.
226  * \param data user argument, pointing to a drm_wait_vblank structure.
227  * \return zero on success or a negative number on failure.
228  *
229  * Verifies the IRQ is installed.
230  *
231  * If a signal is requested checks if this task has already scheduled the same signal
232  * for the same vblank sequence number - nothing to be done in
233  * that case. If the number of tasks waiting for the interrupt exceeds 100 the
234  * function fails. Otherwise adds a new entry to drm_device::vbl_sigs for this
235  * task.
236  *
237  * If a signal is not requested, then calls vblank_wait().
238  */
239 int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_priv)
240 {
241         union drm_wait_vblank *vblwait = data;
242         struct timeval now;
243         int ret = 0;
244         unsigned int flags, seq;
245
246         if ((!dev->pdev->irq) || (!dev->irq_enabled))
247                 return -EINVAL;
248
249         if (vblwait->request.type &
250             ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK)) {
251                 DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n",
252                           vblwait->request.type,
253                           (_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK));
254                 return -EINVAL;
255         }
256
257         flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK;
258
259         if (!drm_core_check_feature(dev, (flags & _DRM_VBLANK_SECONDARY) ?
260                                     DRIVER_IRQ_VBL2 : DRIVER_IRQ_VBL))
261                 return -EINVAL;
262
263         seq = atomic_read((flags & _DRM_VBLANK_SECONDARY) ? &dev->vbl_received2
264                           : &dev->vbl_received);
265
266         switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) {
267         case _DRM_VBLANK_RELATIVE:
268                 vblwait->request.sequence += seq;
269                 vblwait->request.type &= ~_DRM_VBLANK_RELATIVE;
270         case _DRM_VBLANK_ABSOLUTE:
271                 break;
272         default:
273                 return -EINVAL;
274         }
275
276         if ((flags & _DRM_VBLANK_NEXTONMISS) &&
277             (seq - vblwait->request.sequence) <= (1<<23)) {
278                 vblwait->request.sequence = seq + 1;
279         }
280
281         if (flags & _DRM_VBLANK_SIGNAL) {
282                 unsigned long irqflags;
283                 struct list_head *vbl_sigs = (flags & _DRM_VBLANK_SECONDARY)
284                                       ? &dev->vbl_sigs2 : &dev->vbl_sigs;
285                 struct drm_vbl_sig *vbl_sig;
286
287                 spin_lock_irqsave(&dev->vbl_lock, irqflags);
288
289                 /* Check if this task has already scheduled the same signal
290                  * for the same vblank sequence number; nothing to be done in
291                  * that case
292                  */
293                 list_for_each_entry(vbl_sig, vbl_sigs, head) {
294                         if (vbl_sig->sequence == vblwait->request.sequence
295                             && vbl_sig->info.si_signo ==
296                             vblwait->request.signal
297                             && vbl_sig->task == current) {
298                                 spin_unlock_irqrestore(&dev->vbl_lock,
299                                                        irqflags);
300                                 vblwait->reply.sequence = seq;
301                                 goto done;
302                         }
303                 }
304
305                 if (dev->vbl_pending >= 100) {
306                         spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
307                         return -EBUSY;
308                 }
309
310                 dev->vbl_pending++;
311
312                 spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
313
314                 if (!
315                     (vbl_sig =
316                      drm_alloc(sizeof(struct drm_vbl_sig), DRM_MEM_DRIVER))) {
317                         return -ENOMEM;
318                 }
319
320                 memset((void *)vbl_sig, 0, sizeof(*vbl_sig));
321
322                 vbl_sig->sequence = vblwait->request.sequence;
323                 vbl_sig->info.si_signo = vblwait->request.signal;
324                 vbl_sig->task = current;
325
326                 spin_lock_irqsave(&dev->vbl_lock, irqflags);
327
328                 list_add_tail(&vbl_sig->head, vbl_sigs);
329
330                 spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
331
332                 vblwait->reply.sequence = seq;
333         } else {
334                 if (flags & _DRM_VBLANK_SECONDARY) {
335                         if (dev->driver->vblank_wait2)
336                                 ret = dev->driver->vblank_wait2(dev, &vblwait->request.sequence);
337                 } else if (dev->driver->vblank_wait)
338                         ret =
339                             dev->driver->vblank_wait(dev,
340                                                      &vblwait->request.sequence);
341
342                 do_gettimeofday(&now);
343                 vblwait->reply.tval_sec = now.tv_sec;
344                 vblwait->reply.tval_usec = now.tv_usec;
345         }
346
347       done:
348         return ret;
349 }
350
351 /**
352  * Send the VBLANK signals.
353  *
354  * \param dev DRM device.
355  *
356  * Sends a signal for each task in drm_device::vbl_sigs and empties the list.
357  *
358  * If a signal is not requested, then calls vblank_wait().
359  */
360 void drm_vbl_send_signals(struct drm_device * dev)
361 {
362         unsigned long flags;
363         int i;
364
365         spin_lock_irqsave(&dev->vbl_lock, flags);
366
367         for (i = 0; i < 2; i++) {
368                 struct drm_vbl_sig *vbl_sig, *tmp;
369                 struct list_head *vbl_sigs = i ? &dev->vbl_sigs2 : &dev->vbl_sigs;
370                 unsigned int vbl_seq = atomic_read(i ? &dev->vbl_received2 :
371                                                    &dev->vbl_received);
372
373                 list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) {
374                         if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) {
375                                 vbl_sig->info.si_code = vbl_seq;
376                                 send_sig_info(vbl_sig->info.si_signo,
377                                               &vbl_sig->info, vbl_sig->task);
378
379                                 list_del(&vbl_sig->head);
380
381                                 drm_free(vbl_sig, sizeof(*vbl_sig),
382                                          DRM_MEM_DRIVER);
383
384                                 dev->vbl_pending--;
385                         }
386                 }
387         }
388
389         spin_unlock_irqrestore(&dev->vbl_lock, flags);
390 }
391
392 EXPORT_SYMBOL(drm_vbl_send_signals);
393
394 /**
395  * Tasklet wrapper function.
396  *
397  * \param data DRM device in disguise.
398  *
399  * Attempts to grab the HW lock and calls the driver callback on success. On
400  * failure, leave the lock marked as contended so the callback can be called
401  * from drm_unlock().
402  */
403 static void drm_locked_tasklet_func(unsigned long data)
404 {
405         struct drm_device *dev = (struct drm_device *)data;
406         unsigned long irqflags;
407         void (*tasklet_func)(struct drm_device *);
408         
409         spin_lock_irqsave(&dev->tasklet_lock, irqflags);
410         tasklet_func = dev->locked_tasklet_func;
411         spin_unlock_irqrestore(&dev->tasklet_lock, irqflags);
412
413         if (!tasklet_func ||
414             !drm_lock_take(&dev->lock,
415                            DRM_KERNEL_CONTEXT)) {
416                 return;
417         }
418
419         dev->lock.lock_time = jiffies;
420         atomic_inc(&dev->counts[_DRM_STAT_LOCKS]);
421
422         spin_lock_irqsave(&dev->tasklet_lock, irqflags);
423         tasklet_func = dev->locked_tasklet_func;
424         dev->locked_tasklet_func = NULL;
425         spin_unlock_irqrestore(&dev->tasklet_lock, irqflags);
426         
427         if (tasklet_func != NULL)
428                 tasklet_func(dev);
429
430         drm_lock_free(&dev->lock,
431                       DRM_KERNEL_CONTEXT);
432 }
433
434 /**
435  * Schedule a tasklet to call back a driver hook with the HW lock held.
436  *
437  * \param dev DRM device.
438  * \param func Driver callback.
439  *
440  * This is intended for triggering actions that require the HW lock from an
441  * interrupt handler. The lock will be grabbed ASAP after the interrupt handler
442  * completes. Note that the callback may be called from interrupt or process
443  * context, it must not make any assumptions about this. Also, the HW lock will
444  * be held with the kernel context or any client context.
445  */
446 void drm_locked_tasklet(struct drm_device *dev, void (*func)(struct drm_device *))
447 {
448         unsigned long irqflags;
449         static DECLARE_TASKLET(drm_tasklet, drm_locked_tasklet_func, 0);
450
451         if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ) ||
452             test_bit(TASKLET_STATE_SCHED, &drm_tasklet.state))
453                 return;
454
455         spin_lock_irqsave(&dev->tasklet_lock, irqflags);
456
457         if (dev->locked_tasklet_func) {
458                 spin_unlock_irqrestore(&dev->tasklet_lock, irqflags);
459                 return;
460         }
461
462         dev->locked_tasklet_func = func;
463
464         spin_unlock_irqrestore(&dev->tasklet_lock, irqflags);
465
466         drm_tasklet.data = (unsigned long)dev;
467
468         tasklet_hi_schedule(&drm_tasklet);
469 }
470 EXPORT_SYMBOL(drm_locked_tasklet);