|
@@ -119,6 +119,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);
|
|
@@ -176,6 +177,8 @@ int drm_irq_uninstall(drm_device_t * dev)
|
|
|
|
|
|
free_irq(dev->irq, dev);
|
|
|
|
|
|
+ dev->locked_tasklet_func = NULL;
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -399,3 +402,76 @@ void drm_vbl_send_signals(drm_device_t * dev)
|
|
|
}
|
|
|
|
|
|
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 long 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 long 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);
|