diff options
Diffstat (limited to 'drivers/char/drm/drm_irq.c')
-rw-r--r-- | drivers/char/drm/drm_irq.c | 76 |
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 | ||
401 | EXPORT_SYMBOL(drm_vbl_send_signals); | 404 | EXPORT_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 | */ | ||
415 | static 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 | */ | ||
454 | void 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 | } | ||
477 | EXPORT_SYMBOL(drm_locked_tasklet); | ||