From: Rob Clark <r...@ti.com>

---
 drivers/gpu/drm/drm_crtc.c |  147 +++++++++++++++++++++++++++++++++++---------
 drivers/gpu/drm/drm_drv.c  |    1 +
 include/drm/drm.h          |    2 +
 include/drm/drm_crtc.h     |    2 +
 include/drm/drm_mode.h     |   38 ++++++++++++
 5 files changed, 161 insertions(+), 29 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 0ddd43e..d285b11 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -3792,6 +3792,51 @@ out:
        return ret;
 }
 
+static struct drm_pending_vblank_event *create_vblank_event(
+               struct drm_device *dev, struct drm_file *file_priv, uint64_t 
user_data)
+{
+       struct drm_pending_vblank_event *e = NULL;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+       if (file_priv->event_space < sizeof e->event) {
+               spin_unlock_irqrestore(&dev->event_lock, flags);
+               goto out;
+       }
+       file_priv->event_space -= sizeof e->event;
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+
+       e = kzalloc(sizeof *e, GFP_KERNEL);
+       if (e == NULL) {
+               spin_lock_irqsave(&dev->event_lock, flags);
+               file_priv->event_space += sizeof e->event;
+               spin_unlock_irqrestore(&dev->event_lock, flags);
+               goto out;
+       }
+
+       e->event.base.type = DRM_EVENT_FLIP_COMPLETE;
+       e->event.base.length = sizeof e->event;
+       e->event.user_data = user_data;
+       e->base.event = &e->event.base;
+       e->base.file_priv = file_priv;
+       e->base.destroy =
+               (void (*) (struct drm_pending_event *)) kfree;
+
+out:
+       return e;
+}
+
+static void destroy_vblank_event(struct drm_device *dev,
+               struct drm_file *file_priv, struct drm_pending_vblank_event *e)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+       file_priv->event_space += sizeof e->event;
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+       kfree(e);
+}
+
 int drm_mode_page_flip_ioctl(struct drm_device *dev,
                             void *data, struct drm_file *file_priv)
 {
@@ -3800,7 +3845,6 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
        struct drm_mode_object *obj;
        struct drm_crtc *crtc;
        struct drm_pending_vblank_event *e = NULL;
-       unsigned long flags;
        void *state;
        int ret = -EINVAL;
 
@@ -3831,30 +3875,11 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
        }
 
        if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
-               ret = -ENOMEM;
-               spin_lock_irqsave(&dev->event_lock, flags);
-               if (file_priv->event_space < sizeof e->event) {
-                       spin_unlock_irqrestore(&dev->event_lock, flags);
-                       goto out;
-               }
-               file_priv->event_space -= sizeof e->event;
-               spin_unlock_irqrestore(&dev->event_lock, flags);
-
-               e = kzalloc(sizeof *e, GFP_KERNEL);
-               if (e == NULL) {
-                       spin_lock_irqsave(&dev->event_lock, flags);
-                       file_priv->event_space += sizeof e->event;
-                       spin_unlock_irqrestore(&dev->event_lock, flags);
+               e = create_vblank_event(dev, file_priv, page_flip->user_data);
+               if (!e) {
+                       ret = -ENOMEM;
                        goto out;
                }
-
-               e->event.base.type = DRM_EVENT_FLIP_COMPLETE;
-               e->event.base.length = sizeof e->event;
-               e->event.user_data = page_flip->user_data;
-               e->base.event = &e->event.base;
-               e->base.file_priv = file_priv;
-               e->base.destroy =
-                       (void (*) (struct drm_pending_event *)) kfree;
        }
 
        ret = drm_mode_set_obj_prop(dev, obj, state,
@@ -3863,15 +3888,79 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
                goto out;
 
        ret = dev->driver->atomic_commit(dev, state, e);
-       if (ret) {
-               if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
-                       spin_lock_irqsave(&dev->event_lock, flags);
-                       file_priv->event_space += sizeof e->event;
-                       spin_unlock_irqrestore(&dev->event_lock, flags);
-                       kfree(e);
+       if (ret && e)
+               destroy_vblank_event(dev, file_priv, e);
+
+out:
+       dev->driver->atomic_end(dev, state);
+out_unlock:
+       mutex_unlock(&dev->mode_config.mutex);
+       return ret;
+}
+
+int drm_mode_nuclear_page_flip_ioctl(struct drm_device *dev,
+                            void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_crtc_nuclear_page_flip *page_flip = data;
+       struct drm_mode_obj_set_property __user *props =
+                       (struct drm_mode_obj_set_property __user *)
+                       (unsigned long)page_flip->props_ptr;
+       struct drm_pending_vblank_event *e = NULL;
+       void *state;
+       int i, ret;
+
+       if (page_flip->flags & ~DRM_MODE_NUCLEAR_PAGE_FLIP_FLAGS ||
+           page_flip->reserved != 0)
+               return -EINVAL;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       mutex_lock(&dev->mode_config.mutex);
+       state = dev->driver->atomic_begin(dev);
+       if (IS_ERR(state)) {
+               ret = PTR_ERR(state);
+               goto out_unlock;
+       }
+
+       if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
+               e = create_vblank_event(dev, file_priv, page_flip->user_data);
+               if (!e) {
+                       ret = -ENOMEM;
+                       goto out;
                }
        }
 
+       for (i = 0; i < page_flip->count_props; i++) {
+               struct drm_mode_obj_set_property prop;
+               if (copy_from_user(&prop, &props[i], sizeof(prop))) {
+                       ret = -EFAULT;
+                       goto out;
+               }
+
+               /* TODO should we enforce that none of the
+                * properties target objects in chain with
+                * other crtcs?  Or just let the driver deal
+                * with it?
+                */
+
+               ret = drm_mode_set_obj_prop_id(dev, state,
+                               prop.obj_id, prop.obj_type,
+                               prop.prop_id, prop.value);
+               if (ret)
+                       goto out;
+       }
+
+       ret = dev->driver->atomic_check(dev, state);
+       if (ret)
+               goto out;
+
+       if (!(page_flip->flags & DRM_MODE_TEST_ONLY))
+               ret = dev->driver->atomic_commit(dev, state, e);
+
+       if (ret && e)
+               destroy_vblank_event(dev, file_priv, e);
+
 out:
        dev->driver->atomic_end(dev, state);
 out_unlock:
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 9238de4..d700042 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -166,6 +166,7 @@ static struct drm_ioctl_desc drm_ioctls[] = {
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, 
DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, 
drm_mode_obj_get_properties_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_SETPROPERTY, 
drm_mode_obj_set_property_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_NUCLEAR_PAGE_FLIP, 
drm_mode_nuclear_page_flip_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
 };
 
 #define DRM_CORE_IOCTL_COUNT   ARRAY_SIZE( drm_ioctls )
diff --git a/include/drm/drm.h b/include/drm/drm.h
index e51035a..dec69fd 100644
--- a/include/drm/drm.h
+++ b/include/drm/drm.h
@@ -732,6 +732,8 @@ struct drm_prime_handle {
 #define DRM_IOCTL_MODE_ADDFB2          DRM_IOWR(0xB8, struct drm_mode_fb_cmd2)
 #define DRM_IOCTL_MODE_OBJ_GETPROPERTIES       DRM_IOWR(0xB9, struct 
drm_mode_obj_get_properties)
 #define DRM_IOCTL_MODE_OBJ_SETPROPERTY DRM_IOWR(0xBA, struct 
drm_mode_obj_set_property)
+/* placeholder for DRM_IOCTL_ATOMIC_MODE_SET */
+#define DRM_IOCTL_MODE_NUCLEAR_PAGE_FLIP       DRM_IOWR(0xBC, struct 
drm_mode_crtc_nuclear_page_flip)
 
 /**
  * Device specific ioctls should only be in their respective headers
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 9417aaa..9316fbf 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -1080,6 +1080,8 @@ extern bool drm_detect_hdmi_monitor(struct edid *edid);
 extern bool drm_detect_monitor_audio(struct edid *edid);
 extern int drm_mode_page_flip_ioctl(struct drm_device *dev,
                                    void *data, struct drm_file *file_priv);
+extern int drm_mode_nuclear_page_flip_ioctl(struct drm_device *dev,
+                            void *data, struct drm_file *file_priv);
 extern struct drm_display_mode *drm_cvt_mode(struct drm_device *dev,
                                int hdisplay, int vdisplay, int vrefresh,
                                bool reduced, bool interlaced, bool margins);
diff --git a/include/drm/drm_mode.h b/include/drm/drm_mode.h
index 8cec2cf..32d6f65 100644
--- a/include/drm/drm_mode.h
+++ b/include/drm/drm_mode.h
@@ -408,7 +408,10 @@ struct drm_mode_crtc_lut {
 };
 
 #define DRM_MODE_PAGE_FLIP_EVENT 0x01
+#define DRM_MODE_TEST_ONLY       0x02
 #define DRM_MODE_PAGE_FLIP_FLAGS DRM_MODE_PAGE_FLIP_EVENT
+#define DRM_MODE_NUCLEAR_PAGE_FLIP_FLAGS \
+       (DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_TEST_ONLY)
 
 /*
  * Request a page flip on the specified crtc.
@@ -440,6 +443,41 @@ struct drm_mode_crtc_page_flip {
        __u64 user_data;
 };
 
+/*
+ * Request a page flip on the crtc specified by 'crtc_id' plus zero or
+ * more planes attached to this crtc.
+ *
+ * This ioctl will ask KMS to schedule a page flip for the specified
+ * crtc.  Once any pending rendering targeting the specified fb(s) (as
+ * of ioctl time) has completed, the crtc and zero or more planes will
+ * be reprogrammed to display the new fb(s) after the next vertical
+ * refresh.  The ioctl returns immediately, but subsequent rendering
+ * to the current fb will block in the execbuffer ioctl until the page
+ * flip happens.  If a page flip is already pending as the ioctl is
+ * called, EBUSY will be returned.
+ *
+ * The ioctl supports the following flags:
+ *  + DRM_MODE_PAGE_FLIP_EVENT, which will request that drm sends back
+ *    a vblank event (see drm.h: struct drm_event_vblank) when the page
+ *    flip is done.  The user_data field passed in with this ioctl will
+ *    be returned as the user_data field in the vblank event struct.
+ *  + DRM_MODE_TEST_ONLY, don't actually apply the changes (or generate
+ *    a vblank event) but just test the configuration to see if it is
+ *    possible.
+ *
+ * The reserved field must be zero until we figure out something clever
+ * to use it for.
+ */
+
+struct drm_mode_crtc_nuclear_page_flip {
+       uint32_t crtc_id;
+       uint32_t flags;
+       uint64_t user_data;
+       uint32_t reserved;
+       uint32_t count_props;
+       uint64_t props_ptr;  /* ptr to array of drm_mode_obj_set_property */
+};
+
 /* create a dumb scanout buffer */
 struct drm_mode_create_dumb {
        uint32_t height;
-- 
1.7.9.5

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

Reply via email to