summaryrefslogtreecommitdiff
path: root/linux-core/drm_irq.c
diff options
context:
space:
mode:
authorMichel Dänzer <michel@tungstengraphics.com>2006-08-16 15:47:22 +0200
committerMichel Dänzer <michel@tungstengraphics.com>2006-09-29 12:55:08 +0200
commita7b8c8d523d7f726b8fb74cb37f807d2316cf5dd (patch)
treef5a51341055bd6898cbff5f79af04c39449e17b8 /linux-core/drm_irq.c
parent596d7e998403f565a796c431dbbcaf9e0c49908b (diff)
Add support for interrupt triggered driver callback with lock held to DRM core.
(cherry picked from d817cc1f30060fcc4a85a05b2de8a2a1687421b5 commit)
Diffstat (limited to 'linux-core/drm_irq.c')
-rw-r--r--linux-core/drm_irq.c74
1 files changed, 74 insertions, 0 deletions
diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c
index d4e5fbd5..b57fffb0 100644
--- a/linux-core/drm_irq.c
+++ b/linux-core/drm_irq.c
@@ -118,6 +118,7 @@ static int drm_irq_install(drm_device_t * dev)
init_waitqueue_head(&dev->vbl_queue);
spin_lock_init(&dev->vbl_lock);
+ spin_lock_init(&dev->tasklet_lock);
INIT_LIST_HEAD(&dev->vbl_sigs.head);
INIT_LIST_HEAD(&dev->vbl_sigs2.head);
@@ -396,3 +397,76 @@ void drm_vbl_send_signals(drm_device_t * dev)
spin_unlock_irqrestore(&dev->vbl_lock, flags);
}
EXPORT_SYMBOL(drm_vbl_send_signals);
+
+/**
+ * Tasklet wrapper function.
+ *
+ * \param data DRM device in disguise.
+ *
+ * Attempts to grab the HW lock and calls the driver callback on success. On
+ * failure, leave the lock marked as contended so the callback can be called
+ * from drm_unlock().
+ */
+static void drm_locked_tasklet_func(unsigned long data)
+{
+ drm_device_t *dev = (drm_device_t*)data;
+ unsigned int irqflags;
+
+ spin_lock_irqsave(&dev->tasklet_lock, irqflags);
+
+ if (!dev->locked_tasklet_func ||
+ !drm_lock_take(&dev->lock.hw_lock->lock,
+ DRM_KERNEL_CONTEXT)) {
+ spin_unlock_irqrestore(&dev->tasklet_lock, irqflags);
+ return;
+ }
+
+ dev->lock.lock_time = jiffies;
+ atomic_inc(&dev->counts[_DRM_STAT_LOCKS]);
+
+ dev->locked_tasklet_func(dev);
+
+ drm_lock_free(dev, &dev->lock.hw_lock->lock,
+ DRM_KERNEL_CONTEXT);
+
+ dev->locked_tasklet_func = NULL;
+
+ spin_unlock_irqrestore(&dev->tasklet_lock, irqflags);
+}
+
+/**
+ * Schedule a tasklet to call back a driver hook with the HW lock held.
+ *
+ * \param dev DRM device.
+ * \param func Driver callback.
+ *
+ * This is intended for triggering actions that require the HW lock from an
+ * interrupt handler. The lock will be grabbed ASAP after the interrupt handler
+ * completes. Note that the callback may be called from interrupt or process
+ * context, it must not make any assumptions about this. Also, the HW lock will
+ * be held with the kernel context or any client context.
+ */
+void drm_locked_tasklet(drm_device_t *dev, void (*func)(drm_device_t*))
+{
+ unsigned int irqflags;
+ static DECLARE_TASKLET(drm_tasklet, drm_locked_tasklet_func, 0);
+
+ if (test_bit(TASKLET_STATE_SCHED, &drm_tasklet.state))
+ return;
+
+ spin_lock_irqsave(&dev->tasklet_lock, irqflags);
+
+ if (dev->locked_tasklet_func) {
+ spin_unlock_irqrestore(&dev->tasklet_lock, irqflags);
+ return;
+ }
+
+ dev->locked_tasklet_func = func;
+
+ spin_unlock_irqrestore(&dev->tasklet_lock, irqflags);
+
+ drm_tasklet.data = (unsigned long)dev;
+
+ tasklet_hi_schedule(&drm_tasklet);
+}
+EXPORT_SYMBOL(drm_locked_tasklet);