diff options
author | =?utf-8?q?Michel_D=C3=A4nzer?= <michel@tungstengraphics.com> | 2006-10-24 09:08:16 -0400 |
---|---|---|
committer | airlied <airlied@linux.ie> | 2006-12-06 23:53:28 -0500 |
commit | 2e54a007622ac75d63bdc1dd71d435446293f4a9 (patch) | |
tree | 864427aba8fbf2bb8c342d739b2acdec45e43f19 | |
parent | bea5679f9cb97b7e41786c8500df56665cd21e56 (diff) |
drm: Add support for interrupt triggered driver callback with lock held to DRM core.
Signed-off-by: Dave Airlie <airlied@linux.ie>
-rw-r--r-- | drivers/char/drm/drmP.h | 3 | ||||
-rw-r--r-- | drivers/char/drm/drm_irq.c | 76 | ||||
-rw-r--r-- | drivers/char/drm/drm_lock.c | 11 |
3 files changed, 90 insertions, 0 deletions
diff --git a/drivers/char/drm/drmP.h b/drivers/char/drm/drmP.h index 01e1f2528659..2f18329c5eb8 100644 --- a/drivers/char/drm/drmP.h +++ b/drivers/char/drm/drmP.h | |||
@@ -715,6 +715,8 @@ typedef struct drm_device { | |||
715 | drm_vbl_sig_t vbl_sigs; /**< signal list to send on VBLANK */ | 715 | drm_vbl_sig_t vbl_sigs; /**< signal list to send on VBLANK */ |
716 | drm_vbl_sig_t vbl_sigs2; /**< signals to send on secondary VBLANK */ | 716 | drm_vbl_sig_t vbl_sigs2; /**< signals to send on secondary VBLANK */ |
717 | unsigned int vbl_pending; | 717 | unsigned int vbl_pending; |
718 | spinlock_t tasklet_lock; /**< For drm_locked_tasklet */ | ||
719 | void (*locked_tasklet_func)(struct drm_device *dev); | ||
718 | 720 | ||
719 | /*@} */ | 721 | /*@} */ |
720 | cycles_t ctx_start; | 722 | cycles_t ctx_start; |
@@ -966,6 +968,7 @@ extern int drm_wait_vblank(struct inode *inode, struct file *filp, | |||
966 | unsigned int cmd, unsigned long arg); | 968 | unsigned int cmd, unsigned long arg); |
967 | extern int drm_vblank_wait(drm_device_t * dev, unsigned int *vbl_seq); | 969 | extern int drm_vblank_wait(drm_device_t * dev, unsigned int *vbl_seq); |
968 | extern void drm_vbl_send_signals(drm_device_t * dev); | 970 | extern void drm_vbl_send_signals(drm_device_t * dev); |
971 | extern void drm_locked_tasklet(drm_device_t *dev, void(*func)(drm_device_t*)); | ||
969 | 972 | ||
970 | /* AGP/GART support (drm_agpsupport.h) */ | 973 | /* AGP/GART support (drm_agpsupport.h) */ |
971 | extern drm_agp_head_t *drm_agp_init(drm_device_t * dev); | 974 | extern drm_agp_head_t *drm_agp_init(drm_device_t * dev); |
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); | ||
diff --git a/drivers/char/drm/drm_lock.c b/drivers/char/drm/drm_lock.c index f9e45303498d..e0abe5c68423 100644 --- a/drivers/char/drm/drm_lock.c +++ b/drivers/char/drm/drm_lock.c | |||
@@ -155,6 +155,7 @@ int drm_unlock(struct inode *inode, struct file *filp, | |||
155 | drm_file_t *priv = filp->private_data; | 155 | drm_file_t *priv = filp->private_data; |
156 | drm_device_t *dev = priv->head->dev; | 156 | drm_device_t *dev = priv->head->dev; |
157 | drm_lock_t lock; | 157 | drm_lock_t lock; |
158 | unsigned int irqflags; | ||
158 | 159 | ||
159 | if (copy_from_user(&lock, (drm_lock_t __user *) arg, sizeof(lock))) | 160 | if (copy_from_user(&lock, (drm_lock_t __user *) arg, sizeof(lock))) |
160 | return -EFAULT; | 161 | return -EFAULT; |
@@ -165,6 +166,16 @@ int drm_unlock(struct inode *inode, struct file *filp, | |||
165 | return -EINVAL; | 166 | return -EINVAL; |
166 | } | 167 | } |
167 | 168 | ||
169 | spin_lock_irqsave(&dev->tasklet_lock, irqflags); | ||
170 | |||
171 | if (dev->locked_tasklet_func) { | ||
172 | dev->locked_tasklet_func(dev); | ||
173 | |||
174 | dev->locked_tasklet_func = NULL; | ||
175 | } | ||
176 | |||
177 | spin_unlock_irqrestore(&dev->tasklet_lock, irqflags); | ||
178 | |||
168 | atomic_inc(&dev->counts[_DRM_STAT_UNLOCKS]); | 179 | atomic_inc(&dev->counts[_DRM_STAT_UNLOCKS]); |
169 | 180 | ||
170 | /* kernel_context_switch isn't used by any of the x86 drm | 181 | /* kernel_context_switch isn't used by any of the x86 drm |