[RFC PATCH] drm: Atomic modeset ioctl

2012-10-10 Thread ville.syrj...@linux.intel.com
From: Ville Syrj?l? 

The atomic modeset ioctl cna be used to push any number of new values
for object properties. The driver can then check the full device
configuration as single unit, and try to apply the changes atomically.

The ioctl simply takes a list of object IDs and property IDs and their
values. For setting values to blob properties, the property value
indicates the length of the data, and the actual data is passed via
another blob pointer.

The caller can demand non-blocking operation from the ioctl, and if the
driver can't satisfy that requirement an error will be returned.

The caller can also request to receive asynchronous completion events
after the operation has reached the hardware. An event is sent for each
object specified by the caller, whether or not the actual state of
that object changed. Each event also carries a framebuffer ID, which
indicates to user space that the specified object is no longer
accessing that framebuffer.

TODO: detailed error reporting?

Signed-off-by: Ville Syrj?l? 
---
 drivers/gpu/drm/drm_crtc.c |  146 
 drivers/gpu/drm/drm_drv.c  |1 +
 include/drm/drm.h  |   12 
 include/drm/drmP.h |8 +++
 include/drm/drm_crtc.h |   13 
 include/drm/drm_mode.h |   16 +
 6 files changed, 196 insertions(+), 0 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index b313958..38c6604 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -4162,3 +4162,149 @@ int drm_calc_vscale(struct drm_region *src, struct 
drm_region *dst,
return vscale;
 }
 EXPORT_SYMBOL(drm_calc_vscale);
+
+int drm_mode_atomic_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+   struct drm_mode_atomic *arg = data;
+   uint32_t __user *objs_ptr = (uint32_t __user *)(unsigned 
long)(arg->objs_ptr);
+   uint32_t __user *count_props_ptr = (uint32_t __user *)(unsigned 
long)(arg->count_props_ptr);
+   uint32_t __user *props_ptr = (uint32_t __user *)(unsigned 
long)(arg->props_ptr);
+   uint64_t __user *prop_values_ptr = (uint64_t __user *)(unsigned 
long)(arg->prop_values_ptr);
+   uint64_t __user *blob_values_ptr = (uint64_t __user *)(unsigned 
long)(arg->blob_values_ptr);
+   unsigned int copied_objs = 0;
+   unsigned int copied_props = 0;
+   unsigned int copied_blobs = 0;
+   void *state;
+   int ret = 0;
+   unsigned int i, j;
+
+   if (!dev->driver->atomic_funcs ||
+   !dev->driver->atomic_funcs->begin ||
+   !dev->driver->atomic_funcs->set ||
+   !dev->driver->atomic_funcs->check ||
+   !dev->driver->atomic_funcs->commit ||
+   !dev->driver->atomic_funcs->end)
+   return -ENOSYS;
+
+   if (arg->flags & ~(DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_EVENT | 
DRM_MODE_ATOMIC_NONBLOCK))
+   return -EINVAL;
+
+   /* can't test and expect an event at the same time. */
+   if (arg->flags & DRM_MODE_ATOMIC_TEST_ONLY && arg->flags & 
DRM_MODE_ATOMIC_EVENT)
+   return -EINVAL;
+
+   mutex_lock(>mode_config.mutex);
+
+   state = dev->driver->atomic_funcs->begin(dev, file_priv, arg->flags, 
arg->user_data);
+   if (IS_ERR(state)) {
+   ret = PTR_ERR(state);
+   goto unlock;
+   }
+
+   for (i = 0; i < arg->count_objs; i++) {
+   uint32_t obj_id, count_props;
+   struct drm_mode_object *obj;
+
+   if (get_user(obj_id, objs_ptr + copied_objs)) {
+   ret = -EFAULT;
+   goto out;
+   }
+
+   obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_ANY);
+   if (!obj || !obj->properties) {
+   ret = -ENOENT;
+   goto out;
+   }
+
+   if (get_user(count_props, count_props_ptr + copied_objs)) {
+   ret = -EFAULT;
+   goto out;
+   }
+
+   copied_objs++;
+
+   for (j = 0; j < count_props; j++) {
+   uint32_t prop_id;
+   uint64_t prop_value;
+   struct drm_mode_object *prop_obj;
+   struct drm_property *prop;
+   void *blob_data = NULL;
+
+   if (get_user(prop_id, props_ptr + copied_props)) {
+   ret = -EFAULT;
+   goto out;
+   }
+
+   if (!object_has_prop(obj, prop_id)) {
+   ret = -EINVAL;
+   goto out;
+   }
+
+   prop_obj = drm_mode_object_find(dev, prop_id, 
DRM_MODE_OBJECT_PROPERTY);
+   if (!prop_obj) {
+   

[RFC PATCH] drm: Atomic modeset ioctl

2012-10-10 Thread ville . syrjala
From: Ville Syrjälä ville.syrj...@linux.intel.com

The atomic modeset ioctl cna be used to push any number of new values
for object properties. The driver can then check the full device
configuration as single unit, and try to apply the changes atomically.

The ioctl simply takes a list of object IDs and property IDs and their
values. For setting values to blob properties, the property value
indicates the length of the data, and the actual data is passed via
another blob pointer.

The caller can demand non-blocking operation from the ioctl, and if the
driver can't satisfy that requirement an error will be returned.

The caller can also request to receive asynchronous completion events
after the operation has reached the hardware. An event is sent for each
object specified by the caller, whether or not the actual state of
that object changed. Each event also carries a framebuffer ID, which
indicates to user space that the specified object is no longer
accessing that framebuffer.

TODO: detailed error reporting?

Signed-off-by: Ville Syrjälä ville.syrj...@linux.intel.com
---
 drivers/gpu/drm/drm_crtc.c |  146 
 drivers/gpu/drm/drm_drv.c  |1 +
 include/drm/drm.h  |   12 
 include/drm/drmP.h |8 +++
 include/drm/drm_crtc.h |   13 
 include/drm/drm_mode.h |   16 +
 6 files changed, 196 insertions(+), 0 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index b313958..38c6604 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -4162,3 +4162,149 @@ int drm_calc_vscale(struct drm_region *src, struct 
drm_region *dst,
return vscale;
 }
 EXPORT_SYMBOL(drm_calc_vscale);
+
+int drm_mode_atomic_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+   struct drm_mode_atomic *arg = data;
+   uint32_t __user *objs_ptr = (uint32_t __user *)(unsigned 
long)(arg-objs_ptr);
+   uint32_t __user *count_props_ptr = (uint32_t __user *)(unsigned 
long)(arg-count_props_ptr);
+   uint32_t __user *props_ptr = (uint32_t __user *)(unsigned 
long)(arg-props_ptr);
+   uint64_t __user *prop_values_ptr = (uint64_t __user *)(unsigned 
long)(arg-prop_values_ptr);
+   uint64_t __user *blob_values_ptr = (uint64_t __user *)(unsigned 
long)(arg-blob_values_ptr);
+   unsigned int copied_objs = 0;
+   unsigned int copied_props = 0;
+   unsigned int copied_blobs = 0;
+   void *state;
+   int ret = 0;
+   unsigned int i, j;
+
+   if (!dev-driver-atomic_funcs ||
+   !dev-driver-atomic_funcs-begin ||
+   !dev-driver-atomic_funcs-set ||
+   !dev-driver-atomic_funcs-check ||
+   !dev-driver-atomic_funcs-commit ||
+   !dev-driver-atomic_funcs-end)
+   return -ENOSYS;
+
+   if (arg-flags  ~(DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_EVENT | 
DRM_MODE_ATOMIC_NONBLOCK))
+   return -EINVAL;
+
+   /* can't test and expect an event at the same time. */
+   if (arg-flags  DRM_MODE_ATOMIC_TEST_ONLY  arg-flags  
DRM_MODE_ATOMIC_EVENT)
+   return -EINVAL;
+
+   mutex_lock(dev-mode_config.mutex);
+
+   state = dev-driver-atomic_funcs-begin(dev, file_priv, arg-flags, 
arg-user_data);
+   if (IS_ERR(state)) {
+   ret = PTR_ERR(state);
+   goto unlock;
+   }
+
+   for (i = 0; i  arg-count_objs; i++) {
+   uint32_t obj_id, count_props;
+   struct drm_mode_object *obj;
+
+   if (get_user(obj_id, objs_ptr + copied_objs)) {
+   ret = -EFAULT;
+   goto out;
+   }
+
+   obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_ANY);
+   if (!obj || !obj-properties) {
+   ret = -ENOENT;
+   goto out;
+   }
+
+   if (get_user(count_props, count_props_ptr + copied_objs)) {
+   ret = -EFAULT;
+   goto out;
+   }
+
+   copied_objs++;
+
+   for (j = 0; j  count_props; j++) {
+   uint32_t prop_id;
+   uint64_t prop_value;
+   struct drm_mode_object *prop_obj;
+   struct drm_property *prop;
+   void *blob_data = NULL;
+
+   if (get_user(prop_id, props_ptr + copied_props)) {
+   ret = -EFAULT;
+   goto out;
+   }
+
+   if (!object_has_prop(obj, prop_id)) {
+   ret = -EINVAL;
+   goto out;
+   }
+
+   prop_obj = drm_mode_object_find(dev, prop_id, 
DRM_MODE_OBJECT_PROPERTY);
+   if (!prop_obj) {
+   ret =