Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2e54a007 authored by Michel Dänzer's avatar Michel Dänzer Committed by airlied
Browse files

drm: Add support for interrupt triggered driver callback with lock held to DRM core.



Signed-off-by: default avatarDave Airlie <airlied@linux.ie>
parent bea5679f
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -715,6 +715,8 @@ typedef struct drm_device {
	drm_vbl_sig_t vbl_sigs;		/**< signal list to send on VBLANK */
	drm_vbl_sig_t vbl_sigs2;	/**< signals to send on secondary VBLANK */
	unsigned int vbl_pending;
	spinlock_t tasklet_lock;	/**< For drm_locked_tasklet */
	void (*locked_tasklet_func)(struct drm_device *dev);

	/*@} */
	cycles_t ctx_start;
@@ -966,6 +968,7 @@ extern int drm_wait_vblank(struct inode *inode, struct file *filp,
			   unsigned int cmd, unsigned long arg);
extern int drm_vblank_wait(drm_device_t * dev, unsigned int *vbl_seq);
extern void drm_vbl_send_signals(drm_device_t * dev);
extern void drm_locked_tasklet(drm_device_t *dev, void(*func)(drm_device_t*));

				/* AGP/GART support (drm_agpsupport.h) */
extern drm_agp_head_t *drm_agp_init(drm_device_t * dev);
+76 −0
Original line number Diff line number Diff line
@@ -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);
+11 −0
Original line number Diff line number Diff line
@@ -155,6 +155,7 @@ int drm_unlock(struct inode *inode, struct file *filp,
	drm_file_t *priv = filp->private_data;
	drm_device_t *dev = priv->head->dev;
	drm_lock_t lock;
	unsigned int irqflags;

	if (copy_from_user(&lock, (drm_lock_t __user *) arg, sizeof(lock)))
		return -EFAULT;
@@ -165,6 +166,16 @@ int drm_unlock(struct inode *inode, struct file *filp,
		return -EINVAL;
	}

	spin_lock_irqsave(&dev->tasklet_lock, irqflags);

	if (dev->locked_tasklet_func) {
		dev->locked_tasklet_func(dev);

		dev->locked_tasklet_func = NULL;
	}

	spin_unlock_irqrestore(&dev->tasklet_lock, irqflags);

	atomic_inc(&dev->counts[_DRM_STAT_UNLOCKS]);

	/* kernel_context_switch isn't used by any of the x86 drm