[RFC 10/11] drm: atomic pageflip
Rob Clark
rob.clark at linaro.org
Wed Sep 12 21:00:10 PDT 2012
From: Rob Clark <rob at ti.com>
---
drivers/gpu/drm/drm_crtc.c | 157 ++++++++++++++++++++++++++++++++++++--------
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, 171 insertions(+), 29 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 6d22c8c..7457d9a 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -3825,6 +3825,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)
{
@@ -3833,7 +3878,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;
@@ -3868,30 +3912,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(obj, state,
@@ -3900,15 +3925,89 @@ 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_atomic_page_flip_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_crtc_atomic_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;
+ struct drm_mode_object *obj;
+ void *state;
+ int i, ret;
+
+ if (page_flip->flags & ~DRM_MODE_ATOMIC_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);
+
+ obj = drm_mode_object_find(dev, page_flip->crtc_id,
+ DRM_MODE_OBJECT_CRTC);
+ if (!obj) {
+ DRM_DEBUG_KMS("Unknown CRTC ID %d\n", page_flip->crtc_id);
+ ret = -ENOENT;
+ goto out_unlock;
+ }
+
+ state = dev->driver->atomic_begin(dev, obj_to_crtc(obj));
+ 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 73e5633..933e7ae 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_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_ATOMIC_PAGE_FLIP, drm_mode_atomic_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..0908741 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_ATOMIC_PAGE_FLIP DRM_IOWR(0xBC, struct drm_mode_crtc_atomic_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 92e6bf8..795ecee 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -1117,6 +1117,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_atomic_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 9cc0336..fdee5ed 100644
--- a/include/drm/drm_mode.h
+++ b/include/drm/drm_mode.h
@@ -410,7 +410,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_ATOMIC_PAGE_FLIP_FLAGS \
+ (DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_TEST_ONLY)
/*
* Request a page flip on the specified crtc.
@@ -442,6 +445,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_atomic_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
More information about the dri-devel
mailing list