aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/drm/drm_irq.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char/drm/drm_irq.c')
-rw-r--r--drivers/char/drm/drm_irq.c76
1 files changed, 76 insertions, 0 deletions
diff --git a/drivers/char/drm/drm_irq.c b/drivers/char/drm/drm_irq.c
index 3c77756aad9c..2b10e5b60cfa 100644
--- a/drivers/char/drm/drm_irq.c
+++ b/drivers/char/drm/drm_irq.c
@@ -119,6 +119,7 @@ static int drm_irq_install(drm_device_t * dev)
119 init_waitqueue_head(&dev->vbl_queue); 119 init_waitqueue_head(&dev->vbl_queue);
120 120
121 spin_lock_init(&dev->vbl_lock); 121 spin_lock_init(&dev->vbl_lock);
122 spin_lock_init(&dev->tasklet_lock);
122 123
123 INIT_LIST_HEAD(&dev->vbl_sigs.head); 124 INIT_LIST_HEAD(&dev->vbl_sigs.head);
124 INIT_LIST_HEAD(&dev->vbl_sigs2.head); 125 INIT_LIST_HEAD(&dev->vbl_sigs2.head);
@@ -176,6 +177,8 @@ int drm_irq_uninstall(drm_device_t * dev)
176 177
177 free_irq(dev->irq, dev); 178 free_irq(dev->irq, dev);
178 179
180 dev->locked_tasklet_func = NULL;
181
179 return 0; 182 return 0;
180} 183}
181 184
@@ -399,3 +402,76 @@ void drm_vbl_send_signals(drm_device_t * dev)
399} 402}
400 403
401EXPORT_SYMBOL(drm_vbl_send_signals); 404EXPORT_SYMBOL(drm_vbl_send_signals);
405
406/**
407 * Tasklet wrapper function.
408 *
409 * \param data DRM device in disguise.
410 *
411 * Attempts to grab the HW lock and calls the driver callback on success. On
412 * failure, leave the lock marked as contended so the callback can be called
413 * from drm_unlock().
414 */
415static void drm_locked_tasklet_func(unsigned long data)
416{
417 drm_device_t *dev = (drm_device_t*)data;
418 unsigned long irqflags;
419
420 spin_lock_irqsave(&dev->tasklet_lock, irqflags);
421
422 if (!dev->locked_tasklet_func ||
423 !drm_lock_take(&dev->lock.hw_lock->lock,
424 DRM_KERNEL_CONTEXT)) {
425 spin_unlock_irqrestore(&dev->tasklet_lock, irqflags);
426 return;
427 }
428
429 dev->lock.lock_time = jiffies;
430 atomic_inc(&dev->counts[_DRM_STAT_LOCKS]);
431
432 dev->locked_tasklet_func(dev);
433
434 drm_lock_free(dev, &dev->lock.hw_lock->lock,
435 DRM_KERNEL_CONTEXT);
436
437 dev->locked_tasklet_func = NULL;
438
439 spin_unlock_irqrestore(&dev->tasklet_lock, irqflags);
440}
441
442/**
443 * Schedule a tasklet to call back a driver hook with the HW lock held.
444 *
445 * \param dev DRM device.
446 * \param func Driver callback.
447 *
448 * This is intended for triggering actions that require the HW lock from an
449 * interrupt handler. The lock will be grabbed ASAP after the interrupt handler
450 * completes. Note that the callback may be called from interrupt or process
451 * context, it must not make any assumptions about this. Also, the HW lock will
452 * be held with the kernel context or any client context.
453 */
454void drm_locked_tasklet(drm_device_t *dev, void (*func)(drm_device_t*))
455{
456 unsigned long irqflags;
457 static DECLARE_TASKLET(drm_tasklet, drm_locked_tasklet_func, 0);
458
459 if (test_bit(TASKLET_STATE_SCHED, &drm_tasklet.state))
460 return;
461
462 spin_lock_irqsave(&dev->tasklet_lock, irqflags);
463
464 if (dev->locked_tasklet_func) {
465 spin_unlock_irqrestore(&dev->tasklet_lock, irqflags);
466 return;
467 }
468
469 dev->locked_tasklet_func = func;
470
471 spin_unlock_irqrestore(&dev->tasklet_lock, irqflags);
472
473 drm_tasklet.data = (unsigned long)dev;
474
475 tasklet_hi_schedule(&drm_tasklet);
476}
477EXPORT_SYMBOL(drm_locked_tasklet);