]> err.no Git - linux-2.6/blobdiff - drivers/char/drm/drm_irq.c
Pull osi into release branch
[linux-2.6] / drivers / char / drm / drm_irq.c
index 4553a3a1e496c463b82b6a93e7cda756aec350a0..871d2fde09b33ee3d1c8b48b5b6f5d74af0cdffa 100644 (file)
 int drm_irq_by_busid(struct inode *inode, struct file *filp,
                     unsigned int cmd, unsigned long arg)
 {
-       drm_file_t *priv = filp->private_data;
-       drm_device_t *dev = priv->head->dev;
-       drm_irq_busid_t __user *argp = (void __user *)arg;
-       drm_irq_busid_t p;
+       struct drm_file *priv = filp->private_data;
+       struct drm_device *dev = priv->head->dev;
+       struct drm_irq_busid __user *argp = (void __user *)arg;
+       struct drm_irq_busid p;
 
        if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
                return -EINVAL;
@@ -87,7 +87,7 @@ int drm_irq_by_busid(struct inode *inode, struct file *filp,
  * \c drm_driver_irq_preinstall() and \c drm_driver_irq_postinstall() functions
  * before and after the installation.
  */
-static int drm_irq_install(drm_device_t * dev)
+static int drm_irq_install(struct drm_device * dev)
 {
        int ret;
        unsigned long sh_flags = 0;
@@ -120,7 +120,8 @@ static int drm_irq_install(drm_device_t * dev)
 
                spin_lock_init(&dev->vbl_lock);
 
-               INIT_LIST_HEAD(&dev->vbl_sigs.head);
+               INIT_LIST_HEAD(&dev->vbl_sigs);
+               INIT_LIST_HEAD(&dev->vbl_sigs2);
 
                dev->vbl_pending = 0;
        }
@@ -154,7 +155,7 @@ static int drm_irq_install(drm_device_t * dev)
  *
  * Calls the driver's \c drm_driver_irq_uninstall() function, and stops the irq.
  */
-int drm_irq_uninstall(drm_device_t * dev)
+int drm_irq_uninstall(struct drm_device * dev)
 {
        int irq_enabled;
 
@@ -175,6 +176,8 @@ int drm_irq_uninstall(drm_device_t * dev)
 
        free_irq(dev->irq, dev);
 
+       dev->locked_tasklet_func = NULL;
+
        return 0;
 }
 
@@ -194,13 +197,13 @@ EXPORT_SYMBOL(drm_irq_uninstall);
 int drm_control(struct inode *inode, struct file *filp,
                unsigned int cmd, unsigned long arg)
 {
-       drm_file_t *priv = filp->private_data;
-       drm_device_t *dev = priv->head->dev;
-       drm_control_t ctl;
+       struct drm_file *priv = filp->private_data;
+       struct drm_device *dev = priv->head->dev;
+       struct drm_control ctl;
 
        /* if we haven't irq we fallback for compatibility reasons - this used to be a separate function in drm_dma.h */
 
-       if (copy_from_user(&ctl, (drm_control_t __user *) arg, sizeof(ctl)))
+       if (copy_from_user(&ctl, (struct drm_control __user *) arg, sizeof(ctl)))
                return -EFAULT;
 
        switch (ctl.func) {
@@ -241,16 +244,13 @@ int drm_control(struct inode *inode, struct file *filp,
  */
 int drm_wait_vblank(DRM_IOCTL_ARGS)
 {
-       drm_file_t *priv = filp->private_data;
-       drm_device_t *dev = priv->head->dev;
-       drm_wait_vblank_t __user *argp = (void __user *)data;
-       drm_wait_vblank_t vblwait;
+       struct drm_file *priv = filp->private_data;
+       struct drm_device *dev = priv->head->dev;
+       union drm_wait_vblank __user *argp = (void __user *)data;
+       union drm_wait_vblank vblwait;
        struct timeval now;
        int ret = 0;
-       unsigned int flags;
-
-       if (!drm_core_check_feature(dev, DRIVER_IRQ_VBL))
-               return -EINVAL;
+       unsigned int flags, seq;
 
        if (!dev->irq)
                return -EINVAL;
@@ -258,9 +258,26 @@ int drm_wait_vblank(DRM_IOCTL_ARGS)
        if (copy_from_user(&vblwait, argp, sizeof(vblwait)))
                return -EFAULT;
 
-       switch (vblwait.request.type & ~_DRM_VBLANK_FLAGS_MASK) {
+       if (vblwait.request.type &
+           ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK)) {
+               DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n",
+                         vblwait.request.type,
+                         (_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK));
+               return -EINVAL;
+       }
+
+       flags = vblwait.request.type & _DRM_VBLANK_FLAGS_MASK;
+
+       if (!drm_core_check_feature(dev, (flags & _DRM_VBLANK_SECONDARY) ?
+                                   DRIVER_IRQ_VBL2 : DRIVER_IRQ_VBL))
+               return -EINVAL;
+
+       seq = atomic_read((flags & _DRM_VBLANK_SECONDARY) ? &dev->vbl_received2
+                         : &dev->vbl_received);
+
+       switch (vblwait.request.type & _DRM_VBLANK_TYPES_MASK) {
        case _DRM_VBLANK_RELATIVE:
-               vblwait.request.sequence += atomic_read(&dev->vbl_received);
+               vblwait.request.sequence += seq;
                vblwait.request.type &= ~_DRM_VBLANK_RELATIVE;
        case _DRM_VBLANK_ABSOLUTE:
                break;
@@ -268,13 +285,16 @@ int drm_wait_vblank(DRM_IOCTL_ARGS)
                return -EINVAL;
        }
 
-       flags = vblwait.request.type & _DRM_VBLANK_FLAGS_MASK;
+       if ((flags & _DRM_VBLANK_NEXTONMISS) &&
+           (seq - vblwait.request.sequence) <= (1<<23)) {
+               vblwait.request.sequence = seq + 1;
+       }
 
        if (flags & _DRM_VBLANK_SIGNAL) {
                unsigned long irqflags;
-               drm_vbl_sig_t *vbl_sig;
-
-               vblwait.reply.sequence = atomic_read(&dev->vbl_received);
+               struct list_head *vbl_sigs = (flags & _DRM_VBLANK_SECONDARY)
+                                     ? &dev->vbl_sigs2 : &dev->vbl_sigs;
+               struct drm_vbl_sig *vbl_sig;
 
                spin_lock_irqsave(&dev->vbl_lock, irqflags);
 
@@ -282,12 +302,13 @@ int drm_wait_vblank(DRM_IOCTL_ARGS)
                 * for the same vblank sequence number; nothing to be done in
                 * that case
                 */
-               list_for_each_entry(vbl_sig, &dev->vbl_sigs.head, head) {
+               list_for_each_entry(vbl_sig, vbl_sigs, head) {
                        if (vbl_sig->sequence == vblwait.request.sequence
                            && vbl_sig->info.si_signo == vblwait.request.signal
                            && vbl_sig->task == current) {
                                spin_unlock_irqrestore(&dev->vbl_lock,
                                                       irqflags);
+                               vblwait.reply.sequence = seq;
                                goto done;
                        }
                }
@@ -303,7 +324,7 @@ int drm_wait_vblank(DRM_IOCTL_ARGS)
 
                if (!
                    (vbl_sig =
-                    drm_alloc(sizeof(drm_vbl_sig_t), DRM_MEM_DRIVER))) {
+                    drm_alloc(sizeof(struct drm_vbl_sig), DRM_MEM_DRIVER))) {
                        return -ENOMEM;
                }
 
@@ -315,11 +336,16 @@ int drm_wait_vblank(DRM_IOCTL_ARGS)
 
                spin_lock_irqsave(&dev->vbl_lock, irqflags);
 
-               list_add_tail((struct list_head *)vbl_sig, &dev->vbl_sigs.head);
+               list_add_tail(&vbl_sig->head, vbl_sigs);
 
                spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+
+               vblwait.reply.sequence = seq;
        } else {
-               if (dev->driver->vblank_wait)
+               if (flags & _DRM_VBLANK_SECONDARY) {
+                       if (dev->driver->vblank_wait2)
+                               ret = dev->driver->vblank_wait2(dev, &vblwait.request.sequence);
+               } else if (dev->driver->vblank_wait)
                        ret =
                            dev->driver->vblank_wait(dev,
                                                     &vblwait.request.sequence);
@@ -345,27 +371,32 @@ int drm_wait_vblank(DRM_IOCTL_ARGS)
  *
  * If a signal is not requested, then calls vblank_wait().
  */
-void drm_vbl_send_signals(drm_device_t * dev)
+void drm_vbl_send_signals(struct drm_device * dev)
 {
-       struct list_head *list, *tmp;
-       drm_vbl_sig_t *vbl_sig;
-       unsigned int vbl_seq = atomic_read(&dev->vbl_received);
        unsigned long flags;
+       int i;
 
        spin_lock_irqsave(&dev->vbl_lock, flags);
 
-       list_for_each_safe(list, tmp, &dev->vbl_sigs.head) {
-               vbl_sig = list_entry(list, drm_vbl_sig_t, head);
-               if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) {
-                       vbl_sig->info.si_code = vbl_seq;
-                       send_sig_info(vbl_sig->info.si_signo, &vbl_sig->info,
-                                     vbl_sig->task);
+       for (i = 0; i < 2; i++) {
+               struct drm_vbl_sig *vbl_sig, *tmp;
+               struct list_head *vbl_sigs = i ? &dev->vbl_sigs2 : &dev->vbl_sigs;
+               unsigned int vbl_seq = atomic_read(i ? &dev->vbl_received2 :
+                                                  &dev->vbl_received);
 
-                       list_del(list);
+               list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) {
+                       if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) {
+                               vbl_sig->info.si_code = vbl_seq;
+                               send_sig_info(vbl_sig->info.si_signo,
+                                             &vbl_sig->info, vbl_sig->task);
 
-                       drm_free(vbl_sig, sizeof(*vbl_sig), DRM_MEM_DRIVER);
+                               list_del(&vbl_sig->head);
 
-                       dev->vbl_pending--;
+                               drm_free(vbl_sig, sizeof(*vbl_sig),
+                                        DRM_MEM_DRIVER);
+
+                               dev->vbl_pending--;
+                       }
                }
        }
 
@@ -373,3 +404,77 @@ 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)
+{
+       struct drm_device *dev = (struct drm_device *)data;
+       unsigned long irqflags;
+
+       spin_lock_irqsave(&dev->tasklet_lock, irqflags);
+
+       if (!dev->locked_tasklet_func ||
+           !drm_lock_take(&dev->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->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(struct drm_device *dev, void (*func)(struct drm_device *))
+{
+       unsigned long irqflags;
+       static DECLARE_TASKLET(drm_tasklet, drm_locked_tasklet_func, 0);
+
+       if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ) ||
+           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);