Add support for interrupt triggered driver callback with lock held to DRM core.
(cherry picked from d817cc1f30
commit)
main
parent
596d7e9984
commit
a7b8c8d523
|
@ -919,6 +919,8 @@ typedef struct drm_device {
|
||||||
drm_vbl_sig_t vbl_sigs; /**< signal list to send on VBLANK */
|
drm_vbl_sig_t vbl_sigs; /**< signal list to send on VBLANK */
|
||||||
drm_vbl_sig_t vbl_sigs2; /**< signals to send on secondary VBLANK */
|
drm_vbl_sig_t vbl_sigs2; /**< signals to send on secondary VBLANK */
|
||||||
unsigned int vbl_pending;
|
unsigned int vbl_pending;
|
||||||
|
spinlock_t tasklet_lock; /**< For drm_locked_tasklet */
|
||||||
|
void (*locked_tasklet_func)(struct drm_device *dev);
|
||||||
|
|
||||||
/*@} */
|
/*@} */
|
||||||
cycles_t ctx_start;
|
cycles_t ctx_start;
|
||||||
|
@ -1230,6 +1232,7 @@ extern int drm_wait_vblank(struct inode *inode, struct file *filp,
|
||||||
unsigned int cmd, unsigned long arg);
|
unsigned int cmd, unsigned long arg);
|
||||||
extern int drm_vblank_wait(drm_device_t * dev, unsigned int *vbl_seq);
|
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_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) */
|
/* AGP/GART support (drm_agpsupport.h) */
|
||||||
extern drm_agp_head_t *drm_agp_init(drm_device_t *dev);
|
extern drm_agp_head_t *drm_agp_init(drm_device_t *dev);
|
||||||
|
|
|
@ -118,6 +118,7 @@ static int drm_irq_install(drm_device_t * dev)
|
||||||
init_waitqueue_head(&dev->vbl_queue);
|
init_waitqueue_head(&dev->vbl_queue);
|
||||||
|
|
||||||
spin_lock_init(&dev->vbl_lock);
|
spin_lock_init(&dev->vbl_lock);
|
||||||
|
spin_lock_init(&dev->tasklet_lock);
|
||||||
|
|
||||||
INIT_LIST_HEAD(&dev->vbl_sigs.head);
|
INIT_LIST_HEAD(&dev->vbl_sigs.head);
|
||||||
INIT_LIST_HEAD(&dev->vbl_sigs2.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);
|
spin_unlock_irqrestore(&dev->vbl_lock, flags);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(drm_vbl_send_signals);
|
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);
|
||||||
|
|
|
@ -155,6 +155,7 @@ int drm_unlock(struct inode *inode, struct file *filp,
|
||||||
drm_file_t *priv = filp->private_data;
|
drm_file_t *priv = filp->private_data;
|
||||||
drm_device_t *dev = priv->head->dev;
|
drm_device_t *dev = priv->head->dev;
|
||||||
drm_lock_t lock;
|
drm_lock_t lock;
|
||||||
|
unsigned int irqflags;
|
||||||
|
|
||||||
if (copy_from_user(&lock, (drm_lock_t __user *) arg, sizeof(lock)))
|
if (copy_from_user(&lock, (drm_lock_t __user *) arg, sizeof(lock)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
@ -165,6 +166,16 @@ int drm_unlock(struct inode *inode, struct file *filp,
|
||||||
return -EINVAL;
|
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]);
|
atomic_inc(&dev->counts[_DRM_STAT_UNLOCKS]);
|
||||||
|
|
||||||
/* kernel_context_switch isn't used by any of the x86 drm
|
/* kernel_context_switch isn't used by any of the x86 drm
|
||||||
|
|
Loading…
Reference in New Issue