]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/gpu/drm/drm_fops.c
drm: Add async event synchronization for drmWaitVblank
[mv-sheeva.git] / drivers / gpu / drm / drm_fops.c
index 251bc0e3b5ecd92acbb0244634ca46787bb0284d..8ac7fbf6b2b77197b80e02d5d6ce75aaab11ee9d 100644 (file)
@@ -257,6 +257,9 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
 
        INIT_LIST_HEAD(&priv->lhead);
        INIT_LIST_HEAD(&priv->fbs);
+       INIT_LIST_HEAD(&priv->event_list);
+       init_waitqueue_head(&priv->event_wait);
+       priv->event_space = 4096; /* set aside 4k for event buffer */
 
        if (dev->driver->driver_features & DRIVER_GEM)
                drm_gem_open(dev, priv);
@@ -413,6 +416,30 @@ static void drm_master_release(struct drm_device *dev, struct file *filp)
        }
 }
 
+static void drm_events_release(struct drm_file *file_priv)
+{
+       struct drm_device *dev = file_priv->minor->dev;
+       struct drm_pending_event *e, *et;
+       struct drm_pending_vblank_event *v, *vt;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+
+       /* Remove pending flips */
+       list_for_each_entry_safe(v, vt, &dev->vblank_event_list, base.link)
+               if (v->base.file_priv == file_priv) {
+                       list_del(&v->base.link);
+                       drm_vblank_put(dev, v->pipe);
+                       v->base.destroy(&v->base);
+               }
+
+       /* Remove unconsumed events */
+       list_for_each_entry_safe(e, et, &file_priv->event_list, link)
+               e->destroy(e);
+
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
 /**
  * Release file.
  *
@@ -451,6 +478,8 @@ int drm_release(struct inode *inode, struct file *filp)
        if (file_priv->minor->master)
                drm_master_release(dev, filp);
 
+       drm_events_release(file_priv);
+
        if (dev->driver->driver_features & DRIVER_GEM)
                drm_gem_release(dev, file_priv);
 
@@ -544,9 +573,74 @@ int drm_release(struct inode *inode, struct file *filp)
 }
 EXPORT_SYMBOL(drm_release);
 
-/** No-op. */
+static bool
+drm_dequeue_event(struct drm_file *file_priv,
+                 size_t total, size_t max, struct drm_pending_event **out)
+{
+       struct drm_device *dev = file_priv->minor->dev;
+       struct drm_pending_event *e;
+       unsigned long flags;
+       bool ret = false;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+
+       *out = NULL;
+       if (list_empty(&file_priv->event_list))
+               goto out;
+       e = list_first_entry(&file_priv->event_list,
+                            struct drm_pending_event, link);
+       if (e->event->length + total > max)
+               goto out;
+
+       file_priv->event_space += e->event->length;
+       list_del(&e->link);
+       *out = e;
+       ret = true;
+
+out:
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+       return ret;
+}
+
+ssize_t drm_read(struct file *filp, char __user *buffer,
+                size_t count, loff_t *offset)
+{
+       struct drm_file *file_priv = filp->private_data;
+       struct drm_pending_event *e;
+       size_t total;
+       ssize_t ret;
+
+       ret = wait_event_interruptible(file_priv->event_wait,
+                                      !list_empty(&file_priv->event_list));
+       if (ret < 0)
+               return ret;
+
+       total = 0;
+       while (drm_dequeue_event(file_priv, total, count, &e)) {
+               if (copy_to_user(buffer + total,
+                                e->event, e->event->length)) {
+                       total = -EFAULT;
+                       break;
+               }
+
+               total += e->event->length;
+               e->destroy(e);
+       }
+
+       return total;
+}
+EXPORT_SYMBOL(drm_read);
+
 unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait)
 {
-       return 0;
+       struct drm_file *file_priv = filp->private_data;
+       unsigned int mask = 0;
+
+       poll_wait(filp, &file_priv->event_wait, wait);
+
+       if (!list_empty(&file_priv->event_list))
+               mask |= POLLIN | POLLRDNORM;
+
+       return mask;
 }
 EXPORT_SYMBOL(drm_poll);