aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
author=?utf-8?q?Michel_D=C3=A4nzer?= <michel@tungstengraphics.com>2006-10-24 09:08:16 -0400
committerairlied <airlied@linux.ie>2006-12-06 23:53:28 -0500
commit2e54a007622ac75d63bdc1dd71d435446293f4a9 (patch)
tree864427aba8fbf2bb8c342d739b2acdec45e43f19 /drivers/char
parentbea5679f9cb97b7e41786c8500df56665cd21e56 (diff)
drm: Add support for interrupt triggered driver callback with lock held to DRM core.
Signed-off-by: Dave Airlie <airlied@linux.ie>
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/drm/drmP.h3
-rw-r--r--drivers/char/drm/drm_irq.c76
-rw-r--r--drivers/char/drm/drm_lock.c11
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);
967extern int drm_vblank_wait(drm_device_t * dev, unsigned int *vbl_seq); 969extern int drm_vblank_wait(drm_device_t * dev, unsigned int *vbl_seq);
968extern void drm_vbl_send_signals(drm_device_t * dev); 970extern void drm_vbl_send_signals(drm_device_t * dev);
971extern 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) */
971extern drm_agp_head_t *drm_agp_init(drm_device_t * dev); 974extern 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
401EXPORT_SYMBOL(drm_vbl_send_signals); 404EXPORT_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 */
415static 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 */
454void 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}
477EXPORT_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