Merge tag 'topic/atomic-core-2015-01-05' of git://anongit.freedesktop.org/drm-intel...
authorDave Airlie <airlied@redhat.com>
Thu, 8 Jan 2015 23:22:40 +0000 (09:22 +1000)
committerDave Airlie <airlied@redhat.com>
Thu, 8 Jan 2015 23:22:40 +0000 (09:22 +1000)
Next batch of atomic work. Most important is the propertification from Rob
and the nth iteration of the actual atomic ioctl originally from Ville.
Big differences compared to earlier revisions:
- Core properties are now fully handled by the core, drivers can only
  handle driver-specific properties.
- Atomic props&ioctl are opt-in per file_priv, userspace needs to
  explicitly ask for it (like universal plane support).
- For now all hidden behind the atomic module option until this has
  settled a bit.
- Atomic modesets are currently not possible since the exact abi for how
  to handle the mode property is still under discussion.

Besides this some cleanup patches from me and the addition of per-object
state to global state backpointers to simplify drivers.

* tag 'topic/atomic-core-2015-01-05' of git://anongit.freedesktop.org/drm-intel:
  drm: Ensure universal_planes is set for atomic
  drm/atomic: Hide drm.ko internal interfaces
  drm: Atomic modeset ioctl
  drm/atomic: atomic connector properties
  drm/atomic: atomic plane properties
  drm: small property creation cleanup
  drm/atomic: atomic_check functions
  drm: add atomic properties
  drm: refactor getproperties/getconnector
  drm: tweak getconnector locking
  drm: add atomic_get_property
  drm: add atomic_set_property wrappers
  drm: get rid of direct property value access
  drm: store property instead of id in obj attachment
  drm: allow property validation for refcnted props
  drm/atomic: Introduce state->obj backpointers
  drm/atomic-helper: Again check modeset *before* plane states
  drm/atomic-helper: Export both plane and modeset check helpers

19 files changed:
Documentation/DocBook/drm.tmpl
drivers/gpu/drm/drm_atomic.c
drivers/gpu/drm/drm_atomic_helper.c
drivers/gpu/drm/drm_crtc.c
drivers/gpu/drm/drm_crtc_helper.c
drivers/gpu/drm/drm_crtc_internal.h
drivers/gpu/drm/drm_drv.c
drivers/gpu/drm/drm_ioctl.c
drivers/gpu/drm/drm_plane_helper.c
drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
drivers/gpu/drm/msm/msm_atomic.c
drivers/gpu/drm/msm/msm_drv.c
drivers/gpu/drm/msm/msm_drv.h
include/drm/drmP.h
include/drm/drm_atomic.h
include/drm/drm_atomic_helper.h
include/drm/drm_crtc.h
include/uapi/drm/drm.h
include/uapi/drm/drm_mode.h

index 8a0b2a84f4bf76404c6f4ae2f81f52c6b392afbd..640c2a30563f83a9be4767e6370bd3a0e044123f 100644 (file)
               Driver supports dedicated render nodes.
             </para></listitem>
           </varlistentry>
+          <varlistentry>
+            <term>DRIVER_ATOMIC</term>
+            <listitem><para>
+              Driver supports atomic properties.  In this case the driver
+              must implement appropriate obj->atomic_get_property() vfuncs
+              for any modeset objects with driver specific properties.
+            </para></listitem>
+          </varlistentry>
         </variablelist>
       </sect3>
       <sect3>
@@ -2565,8 +2573,8 @@ void intel_crt_init(struct drm_device *dev)
        <td valign="top" >Description/Restrictions</td>
        </tr>
        <tr>
-       <td rowspan="25" valign="top" >DRM</td>
-       <td rowspan="4" valign="top" >Generic</td>
+       <td rowspan="36" valign="top" >DRM</td>
+       <td rowspan="5" valign="top" >Connector</td>
        <td valign="top" >“EDID”</td>
        <td valign="top" >BLOB | IMMUTABLE</td>
        <td valign="top" >0</td>
@@ -2595,7 +2603,14 @@ void intel_crt_init(struct drm_device *dev)
        <td valign="top" >Contains tiling information for a connector.</td>
        </tr>
        <tr>
-       <td rowspan="1" valign="top" >Plane</td>
+       <td valign="top" >“CRTC_ID”</td>
+       <td valign="top" >OBJECT</td>
+       <td valign="top" >DRM_MODE_OBJECT_CRTC</td>
+       <td valign="top" >Connector</td>
+       <td valign="top" >CRTC that connector is attached to (atomic)</td>
+       </tr>
+       <tr>
+       <td rowspan="11" valign="top" >Plane</td>
        <td valign="top" >“type”</td>
        <td valign="top" >ENUM | IMMUTABLE</td>
        <td valign="top" >{ "Overlay", "Primary", "Cursor" }</td>
@@ -2603,6 +2618,76 @@ void intel_crt_init(struct drm_device *dev)
        <td valign="top" >Plane type</td>
        </tr>
        <tr>
+       <td valign="top" >“SRC_X”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=UINT_MAX</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >Scanout source x coordinate in 16.16 fixed point (atomic)</td>
+       </tr>
+       <tr>
+       <td valign="top" >“SRC_Y”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=UINT_MAX</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >Scanout source y coordinate in 16.16 fixed point (atomic)</td>
+       </tr>
+       <tr>
+       <td valign="top" >“SRC_W”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=UINT_MAX</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >Scanout source width in 16.16 fixed point (atomic)</td>
+       </tr>
+       <tr>
+       <td valign="top" >“SRC_H”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=UINT_MAX</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >Scanout source height in 16.16 fixed point (atomic)</td>
+       </tr>
+       <tr>
+       <td valign="top" >“CRTC_X”</td>
+       <td valign="top" >SIGNED_RANGE</td>
+       <td valign="top" >Min=INT_MIN, Max=INT_MAX</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >Scanout CRTC (destination) x coordinate (atomic)</td>
+       </tr>
+       <tr>
+       <td valign="top" >“CRTC_Y”</td>
+       <td valign="top" >SIGNED_RANGE</td>
+       <td valign="top" >Min=INT_MIN, Max=INT_MAX</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >Scanout CRTC (destination) y coordinate (atomic)</td>
+       </tr>
+       <tr>
+       <td valign="top" >“CRTC_W”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=UINT_MAX</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >Scanout CRTC (destination) width (atomic)</td>
+       </tr>
+       <tr>
+       <td valign="top" >“CRTC_H”</td>
+       <td valign="top" >RANGE</td>
+       <td valign="top" >Min=0, Max=UINT_MAX</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >Scanout CRTC (destination) height (atomic)</td>
+       </tr>
+       <tr>
+       <td valign="top" >“FB_ID”</td>
+       <td valign="top" >OBJECT</td>
+       <td valign="top" >DRM_MODE_OBJECT_FB</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >Scanout framebuffer (atomic)</td>
+       </tr>
+       <tr>
+       <td valign="top" >“CRTC_ID”</td>
+       <td valign="top" >OBJECT</td>
+       <td valign="top" >DRM_MODE_OBJECT_CRTC</td>
+       <td valign="top" >Plane</td>
+       <td valign="top" >CRTC that plane is attached to (atomic)</td>
+       </tr>
+       <tr>
        <td rowspan="2" valign="top" >DVI-I</td>
        <td valign="top" >“subconnector”</td>
        <td valign="top" >ENUM</td>
index ff5f034cc40526ba4504d725d06228c662232417..1e38dfc8e462fde168de1d8316ff237a8e4f8568 100644 (file)
@@ -56,6 +56,11 @@ drm_atomic_state_alloc(struct drm_device *dev)
        if (!state)
                return NULL;
 
+       /* TODO legacy paths should maybe do a better job about
+        * setting this appropriately?
+        */
+       state->allow_modeset = true;
+
        state->num_connector = ACCESS_ONCE(dev->mode_config.num_connector);
 
        state->crtcs = kcalloc(dev->mode_config.num_crtc,
@@ -216,6 +221,70 @@ drm_atomic_get_crtc_state(struct drm_atomic_state *state,
 }
 EXPORT_SYMBOL(drm_atomic_get_crtc_state);
 
+/**
+ * drm_atomic_crtc_set_property - set property on CRTC
+ * @crtc: the drm CRTC to set a property on
+ * @state: the state object to update with the new property value
+ * @property: the property to set
+ * @val: the new property value
+ *
+ * Use this instead of calling crtc->atomic_set_property directly.
+ * This function handles generic/core properties and calls out to
+ * driver's ->atomic_set_property() for driver properties.  To ensure
+ * consistent behavior you must call this function rather than the
+ * driver hook directly.
+ *
+ * RETURNS:
+ * Zero on success, error code on failure
+ */
+int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
+               struct drm_crtc_state *state, struct drm_property *property,
+               uint64_t val)
+{
+       if (crtc->funcs->atomic_set_property)
+               return crtc->funcs->atomic_set_property(crtc, state, property, val);
+       return -EINVAL;
+}
+EXPORT_SYMBOL(drm_atomic_crtc_set_property);
+
+/*
+ * This function handles generic/core properties and calls out to
+ * driver's ->atomic_get_property() for driver properties.  To ensure
+ * consistent behavior you must call this function rather than the
+ * driver hook directly.
+ */
+int drm_atomic_crtc_get_property(struct drm_crtc *crtc,
+               const struct drm_crtc_state *state,
+               struct drm_property *property, uint64_t *val)
+{
+       if (crtc->funcs->atomic_get_property)
+               return crtc->funcs->atomic_get_property(crtc, state, property, val);
+       return -EINVAL;
+}
+
+/**
+ * drm_atomic_crtc_check - check crtc state
+ * @crtc: crtc to check
+ * @state: crtc state to check
+ *
+ * Provides core sanity checks for crtc state.
+ *
+ * RETURNS:
+ * Zero on success, error code on failure
+ */
+static int drm_atomic_crtc_check(struct drm_crtc *crtc,
+               struct drm_crtc_state *state)
+{
+       /* NOTE: we explicitly don't enforce constraints such as primary
+        * layer covering entire screen, since that is something we want
+        * to allow (on hw that supports it).  For hw that does not, it
+        * should be checked in driver's crtc->atomic_check() vfunc.
+        *
+        * TODO: Add generic modeset state checks once we support those.
+        */
+       return 0;
+}
+
 /**
  * drm_atomic_get_plane_state - get plane state
  * @state: global atomic state object
@@ -271,6 +340,183 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state,
 }
 EXPORT_SYMBOL(drm_atomic_get_plane_state);
 
+/**
+ * drm_atomic_plane_set_property - set property on plane
+ * @plane: the drm plane to set a property on
+ * @state: the state object to update with the new property value
+ * @property: the property to set
+ * @val: the new property value
+ *
+ * Use this instead of calling plane->atomic_set_property directly.
+ * This function handles generic/core properties and calls out to
+ * driver's ->atomic_set_property() for driver properties.  To ensure
+ * consistent behavior you must call this function rather than the
+ * driver hook directly.
+ *
+ * RETURNS:
+ * Zero on success, error code on failure
+ */
+int drm_atomic_plane_set_property(struct drm_plane *plane,
+               struct drm_plane_state *state, struct drm_property *property,
+               uint64_t val)
+{
+       struct drm_device *dev = plane->dev;
+       struct drm_mode_config *config = &dev->mode_config;
+
+       if (property == config->prop_fb_id) {
+               struct drm_framebuffer *fb = drm_framebuffer_lookup(dev, val);
+               drm_atomic_set_fb_for_plane(state, fb);
+               if (fb)
+                       drm_framebuffer_unreference(fb);
+       } else if (property == config->prop_crtc_id) {
+               struct drm_crtc *crtc = drm_crtc_find(dev, val);
+               return drm_atomic_set_crtc_for_plane(state, crtc);
+       } else if (property == config->prop_crtc_x) {
+               state->crtc_x = U642I64(val);
+       } else if (property == config->prop_crtc_y) {
+               state->crtc_y = U642I64(val);
+       } else if (property == config->prop_crtc_w) {
+               state->crtc_w = val;
+       } else if (property == config->prop_crtc_h) {
+               state->crtc_h = val;
+       } else if (property == config->prop_src_x) {
+               state->src_x = val;
+       } else if (property == config->prop_src_y) {
+               state->src_y = val;
+       } else if (property == config->prop_src_w) {
+               state->src_w = val;
+       } else if (property == config->prop_src_h) {
+               state->src_h = val;
+       } else if (plane->funcs->atomic_set_property) {
+               return plane->funcs->atomic_set_property(plane, state,
+                               property, val);
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_atomic_plane_set_property);
+
+/*
+ * This function handles generic/core properties and calls out to
+ * driver's ->atomic_get_property() for driver properties.  To ensure
+ * consistent behavior you must call this function rather than the
+ * driver hook directly.
+ */
+static int
+drm_atomic_plane_get_property(struct drm_plane *plane,
+               const struct drm_plane_state *state,
+               struct drm_property *property, uint64_t *val)
+{
+       struct drm_device *dev = plane->dev;
+       struct drm_mode_config *config = &dev->mode_config;
+
+       if (property == config->prop_fb_id) {
+               *val = (state->fb) ? state->fb->base.id : 0;
+       } else if (property == config->prop_crtc_id) {
+               *val = (state->crtc) ? state->crtc->base.id : 0;
+       } else if (property == config->prop_crtc_x) {
+               *val = I642U64(state->crtc_x);
+       } else if (property == config->prop_crtc_y) {
+               *val = I642U64(state->crtc_y);
+       } else if (property == config->prop_crtc_w) {
+               *val = state->crtc_w;
+       } else if (property == config->prop_crtc_h) {
+               *val = state->crtc_h;
+       } else if (property == config->prop_src_x) {
+               *val = state->src_x;
+       } else if (property == config->prop_src_y) {
+               *val = state->src_y;
+       } else if (property == config->prop_src_w) {
+               *val = state->src_w;
+       } else if (property == config->prop_src_h) {
+               *val = state->src_h;
+       } else if (plane->funcs->atomic_get_property) {
+               return plane->funcs->atomic_get_property(plane, state, property, val);
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/**
+ * drm_atomic_plane_check - check plane state
+ * @plane: plane to check
+ * @state: plane state to check
+ *
+ * Provides core sanity checks for plane state.
+ *
+ * RETURNS:
+ * Zero on success, error code on failure
+ */
+static int drm_atomic_plane_check(struct drm_plane *plane,
+               struct drm_plane_state *state)
+{
+       unsigned int fb_width, fb_height;
+       unsigned int i;
+
+       /* either *both* CRTC and FB must be set, or neither */
+       if (WARN_ON(state->crtc && !state->fb)) {
+               DRM_DEBUG_KMS("CRTC set but no FB\n");
+               return -EINVAL;
+       } else if (WARN_ON(state->fb && !state->crtc)) {
+               DRM_DEBUG_KMS("FB set but no CRTC\n");
+               return -EINVAL;
+       }
+
+       /* if disabled, we don't care about the rest of the state: */
+       if (!state->crtc)
+               return 0;
+
+       /* Check whether this plane is usable on this CRTC */
+       if (!(plane->possible_crtcs & drm_crtc_mask(state->crtc))) {
+               DRM_DEBUG_KMS("Invalid crtc for plane\n");
+               return -EINVAL;
+       }
+
+       /* Check whether this plane supports the fb pixel format. */
+       for (i = 0; i < plane->format_count; i++)
+               if (state->fb->pixel_format == plane->format_types[i])
+                       break;
+       if (i == plane->format_count) {
+               DRM_DEBUG_KMS("Invalid pixel format %s\n",
+                             drm_get_format_name(state->fb->pixel_format));
+               return -EINVAL;
+       }
+
+       /* Give drivers some help against integer overflows */
+       if (state->crtc_w > INT_MAX ||
+           state->crtc_x > INT_MAX - (int32_t) state->crtc_w ||
+           state->crtc_h > INT_MAX ||
+           state->crtc_y > INT_MAX - (int32_t) state->crtc_h) {
+               DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
+                             state->crtc_w, state->crtc_h,
+                             state->crtc_x, state->crtc_y);
+               return -ERANGE;
+       }
+
+       fb_width = state->fb->width << 16;
+       fb_height = state->fb->height << 16;
+
+       /* Make sure source coordinates are inside the fb. */
+       if (state->src_w > fb_width ||
+           state->src_x > fb_width - state->src_w ||
+           state->src_h > fb_height ||
+           state->src_y > fb_height - state->src_h) {
+               DRM_DEBUG_KMS("Invalid source coordinates "
+                             "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
+                             state->src_w >> 16, ((state->src_w & 0xffff) * 15625) >> 10,
+                             state->src_h >> 16, ((state->src_h & 0xffff) * 15625) >> 10,
+                             state->src_x >> 16, ((state->src_x & 0xffff) * 15625) >> 10,
+                             state->src_y >> 16, ((state->src_y & 0xffff) * 15625) >> 10);
+               return -ENOSPC;
+       }
+
+       return 0;
+}
+
 /**
  * drm_atomic_get_connector_state - get connector state
  * @state: global atomic state object
@@ -342,10 +588,114 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state,
 }
 EXPORT_SYMBOL(drm_atomic_get_connector_state);
 
+/**
+ * drm_atomic_connector_set_property - set property on connector.
+ * @connector: the drm connector to set a property on
+ * @state: the state object to update with the new property value
+ * @property: the property to set
+ * @val: the new property value
+ *
+ * Use this instead of calling connector->atomic_set_property directly.
+ * This function handles generic/core properties and calls out to
+ * driver's ->atomic_set_property() for driver properties.  To ensure
+ * consistent behavior you must call this function rather than the
+ * driver hook directly.
+ *
+ * RETURNS:
+ * Zero on success, error code on failure
+ */
+int drm_atomic_connector_set_property(struct drm_connector *connector,
+               struct drm_connector_state *state, struct drm_property *property,
+               uint64_t val)
+{
+       struct drm_device *dev = connector->dev;
+       struct drm_mode_config *config = &dev->mode_config;
+
+       if (property == config->prop_crtc_id) {
+               struct drm_crtc *crtc = drm_crtc_find(dev, val);
+               return drm_atomic_set_crtc_for_connector(state, crtc);
+       } else if (property == config->dpms_property) {
+               /* setting DPMS property requires special handling, which
+                * is done in legacy setprop path for us.  Disallow (for
+                * now?) atomic writes to DPMS property:
+                */
+               return -EINVAL;
+       } else if (connector->funcs->atomic_set_property) {
+               return connector->funcs->atomic_set_property(connector,
+                               state, property, val);
+       } else {
+               return -EINVAL;
+       }
+}
+EXPORT_SYMBOL(drm_atomic_connector_set_property);
+
+/*
+ * This function handles generic/core properties and calls out to
+ * driver's ->atomic_get_property() for driver properties.  To ensure
+ * consistent behavior you must call this function rather than the
+ * driver hook directly.
+ */
+static int
+drm_atomic_connector_get_property(struct drm_connector *connector,
+               const struct drm_connector_state *state,
+               struct drm_property *property, uint64_t *val)
+{
+       struct drm_device *dev = connector->dev;
+       struct drm_mode_config *config = &dev->mode_config;
+
+       if (property == config->prop_crtc_id) {
+               *val = (state->crtc) ? state->crtc->base.id : 0;
+       } else if (property == config->dpms_property) {
+               *val = connector->dpms;
+       } else if (connector->funcs->atomic_get_property) {
+               return connector->funcs->atomic_get_property(connector,
+                               state, property, val);
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int drm_atomic_get_property(struct drm_mode_object *obj,
+               struct drm_property *property, uint64_t *val)
+{
+       struct drm_device *dev = property->dev;
+       int ret;
+
+       switch (obj->type) {
+       case DRM_MODE_OBJECT_CONNECTOR: {
+               struct drm_connector *connector = obj_to_connector(obj);
+               WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
+               ret = drm_atomic_connector_get_property(connector,
+                               connector->state, property, val);
+               break;
+       }
+       case DRM_MODE_OBJECT_CRTC: {
+               struct drm_crtc *crtc = obj_to_crtc(obj);
+               WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
+               ret = drm_atomic_crtc_get_property(crtc,
+                               crtc->state, property, val);
+               break;
+       }
+       case DRM_MODE_OBJECT_PLANE: {
+               struct drm_plane *plane = obj_to_plane(obj);
+               WARN_ON(!drm_modeset_is_locked(&plane->mutex));
+               ret = drm_atomic_plane_get_property(plane,
+                               plane->state, property, val);
+               break;
+       }
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
 /**
  * drm_atomic_set_crtc_for_plane - set crtc for plane
- * @state: the incoming atomic state
- * @plane: the plane whose incoming state to update
+ * @plane_state: the plane whose incoming state to update
  * @crtc: crtc to use for the plane
  *
  * Changing the assigned crtc for a plane requires us to grab the lock and state
@@ -358,16 +708,12 @@ EXPORT_SYMBOL(drm_atomic_get_connector_state);
  * sequence must be restarted. All other errors are fatal.
  */
 int
-drm_atomic_set_crtc_for_plane(struct drm_atomic_state *state,
-                             struct drm_plane *plane, struct drm_crtc *crtc)
+drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state,
+                             struct drm_crtc *crtc)
 {
-       struct drm_plane_state *plane_state =
-                       drm_atomic_get_plane_state(state, plane);
+       struct drm_plane *plane = plane_state->plane;
        struct drm_crtc_state *crtc_state;
 
-       if (WARN_ON(IS_ERR(plane_state)))
-               return PTR_ERR(plane_state);
-
        if (plane_state->crtc) {
                crtc_state = drm_atomic_get_crtc_state(plane_state->state,
                                                       plane_state->crtc);
@@ -583,14 +929,62 @@ EXPORT_SYMBOL(drm_atomic_legacy_backoff);
  */
 int drm_atomic_check_only(struct drm_atomic_state *state)
 {
-       struct drm_mode_config *config = &state->dev->mode_config;
+       struct drm_device *dev = state->dev;
+       struct drm_mode_config *config = &dev->mode_config;
+       int nplanes = config->num_total_plane;
+       int ncrtcs = config->num_crtc;
+       int i, ret = 0;
 
        DRM_DEBUG_KMS("checking %p\n", state);
 
+       for (i = 0; i < nplanes; i++) {
+               struct drm_plane *plane = state->planes[i];
+
+               if (!plane)
+                       continue;
+
+               ret = drm_atomic_plane_check(plane, state->plane_states[i]);
+               if (ret) {
+                       DRM_DEBUG_KMS("[PLANE:%d] atomic core check failed\n",
+                                     plane->base.id);
+                       return ret;
+               }
+       }
+
+       for (i = 0; i < ncrtcs; i++) {
+               struct drm_crtc *crtc = state->crtcs[i];
+
+               if (!crtc)
+                       continue;
+
+               ret = drm_atomic_crtc_check(crtc, state->crtc_states[i]);
+               if (ret) {
+                       DRM_DEBUG_KMS("[CRTC:%d] atomic core check failed\n",
+                                     crtc->base.id);
+                       return ret;
+               }
+       }
+
        if (config->funcs->atomic_check)
-               return config->funcs->atomic_check(state->dev, state);
-       else
-               return 0;
+               ret = config->funcs->atomic_check(state->dev, state);
+
+       if (!state->allow_modeset) {
+               for (i = 0; i < ncrtcs; i++) {
+                       struct drm_crtc *crtc = state->crtcs[i];
+                       struct drm_crtc_state *crtc_state = state->crtc_states[i];
+
+                       if (!crtc)
+                               continue;
+
+                       if (crtc_state->mode_changed) {
+                               DRM_DEBUG_KMS("[CRTC:%d] requires full modeset\n",
+                                             crtc->base.id);
+                               return -EINVAL;
+                       }
+               }
+       }
+
+       return ret;
 }
 EXPORT_SYMBOL(drm_atomic_check_only);
 
@@ -655,3 +1049,313 @@ int drm_atomic_async_commit(struct drm_atomic_state *state)
        return config->funcs->atomic_commit(state->dev, state, true);
 }
 EXPORT_SYMBOL(drm_atomic_async_commit);
+
+/*
+ * The big monstor ioctl
+ */
+
+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);
+}
+
+static int atomic_set_prop(struct drm_atomic_state *state,
+               struct drm_mode_object *obj, struct drm_property *prop,
+               uint64_t prop_value)
+{
+       struct drm_mode_object *ref;
+       int ret;
+
+       if (!drm_property_change_valid_get(prop, prop_value, &ref))
+               return -EINVAL;
+
+       switch (obj->type) {
+       case DRM_MODE_OBJECT_CONNECTOR: {
+               struct drm_connector *connector = obj_to_connector(obj);
+               struct drm_connector_state *connector_state;
+
+               connector_state = drm_atomic_get_connector_state(state, connector);
+               if (IS_ERR(connector_state)) {
+                       ret = PTR_ERR(connector_state);
+                       break;
+               }
+
+               ret = drm_atomic_connector_set_property(connector,
+                               connector_state, prop, prop_value);
+               break;
+       }
+       case DRM_MODE_OBJECT_CRTC: {
+               struct drm_crtc *crtc = obj_to_crtc(obj);
+               struct drm_crtc_state *crtc_state;
+
+               crtc_state = drm_atomic_get_crtc_state(state, crtc);
+               if (IS_ERR(crtc_state)) {
+                       ret = PTR_ERR(crtc_state);
+                       break;
+               }
+
+               ret = drm_atomic_crtc_set_property(crtc,
+                               crtc_state, prop, prop_value);
+               break;
+       }
+       case DRM_MODE_OBJECT_PLANE: {
+               struct drm_plane *plane = obj_to_plane(obj);
+               struct drm_plane_state *plane_state;
+
+               plane_state = drm_atomic_get_plane_state(state, plane);
+               if (IS_ERR(plane_state)) {
+                       ret = PTR_ERR(plane_state);
+                       break;
+               }
+
+               ret = drm_atomic_plane_set_property(plane,
+                               plane_state, prop, prop_value);
+               break;
+       }
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       drm_property_change_valid_put(prop, ref);
+       return ret;
+}
+
+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);
+       unsigned int copied_objs, copied_props;
+       struct drm_atomic_state *state;
+       struct drm_modeset_acquire_ctx ctx;
+       struct drm_plane *plane;
+       unsigned plane_mask = 0;
+       int ret = 0;
+       unsigned int i, j;
+
+       /* disallow for drivers not supporting atomic: */
+       if (!drm_core_check_feature(dev, DRIVER_ATOMIC))
+               return -EINVAL;
+
+       /* disallow for userspace that has not enabled atomic cap (even
+        * though this may be a bit overkill, since legacy userspace
+        * wouldn't know how to call this ioctl)
+        */
+       if (!file_priv->atomic)
+               return -EINVAL;
+
+       if (arg->flags & ~DRM_MODE_ATOMIC_FLAGS)
+               return -EINVAL;
+
+       if (arg->reserved)
+               return -EINVAL;
+
+       if ((arg->flags & DRM_MODE_PAGE_FLIP_ASYNC) &&
+                       !dev->mode_config.async_page_flip)
+               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_PAGE_FLIP_EVENT))
+               return -EINVAL;
+
+       drm_modeset_acquire_init(&ctx, 0);
+
+       state = drm_atomic_state_alloc(dev);
+       if (!state)
+               return -ENOMEM;
+
+       state->acquire_ctx = &ctx;
+       state->allow_modeset = !!(arg->flags & DRM_MODE_ATOMIC_ALLOW_MODESET);
+
+retry:
+       copied_objs = 0;
+       copied_props = 0;
+
+       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 fail;
+               }
+
+               obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_ANY);
+               if (!obj || !obj->properties) {
+                       ret = -ENOENT;
+                       goto fail;
+               }
+
+               if (obj->type == DRM_MODE_OBJECT_PLANE) {
+                       plane = obj_to_plane(obj);
+                       plane_mask |= (1 << drm_plane_index(plane));
+                       plane->old_fb = plane->fb;
+               }
+
+               if (get_user(count_props, count_props_ptr + copied_objs)) {
+                       ret = -EFAULT;
+                       goto fail;
+               }
+
+               copied_objs++;
+
+               for (j = 0; j < count_props; j++) {
+                       uint32_t prop_id;
+                       uint64_t prop_value;
+                       struct drm_property *prop;
+
+                       if (get_user(prop_id, props_ptr + copied_props)) {
+                               ret = -EFAULT;
+                               goto fail;
+                       }
+
+                       prop = drm_property_find(dev, prop_id);
+                       if (!prop) {
+                               ret = -ENOENT;
+                               goto fail;
+                       }
+
+                       if (get_user(prop_value, prop_values_ptr + copied_props)) {
+                               ret = -EFAULT;
+                               goto fail;
+                       }
+
+                       ret = atomic_set_prop(state, obj, prop, prop_value);
+                       if (ret)
+                               goto fail;
+
+                       copied_props++;
+               }
+       }
+
+       if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT) {
+               int ncrtcs = dev->mode_config.num_crtc;
+
+               for (i = 0; i < ncrtcs; i++) {
+                       struct drm_crtc_state *crtc_state = state->crtc_states[i];
+                       struct drm_pending_vblank_event *e;
+
+                       if (!crtc_state)
+                               continue;
+
+                       e = create_vblank_event(dev, file_priv, arg->user_data);
+                       if (!e) {
+                               ret = -ENOMEM;
+                               goto fail;
+                       }
+
+                       crtc_state->event = e;
+               }
+       }
+
+       if (arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) {
+               ret = drm_atomic_check_only(state);
+               /* _check_only() does not free state, unlike _commit() */
+               drm_atomic_state_free(state);
+       } else if (arg->flags & DRM_MODE_ATOMIC_NONBLOCK) {
+               ret = drm_atomic_async_commit(state);
+       } else {
+               ret = drm_atomic_commit(state);
+       }
+
+       /* if succeeded, fixup legacy plane crtc/fb ptrs before dropping
+        * locks (ie. while it is still safe to deref plane->state).  We
+        * need to do this here because the driver entry points cannot
+        * distinguish between legacy and atomic ioctls.
+        */
+       drm_for_each_plane_mask(plane, dev, plane_mask) {
+               if (ret == 0) {
+                       struct drm_framebuffer *new_fb = plane->state->fb;
+                       if (new_fb)
+                               drm_framebuffer_reference(new_fb);
+                       plane->fb = new_fb;
+                       plane->crtc = plane->state->crtc;
+               } else {
+                       plane->old_fb = NULL;
+               }
+               if (plane->old_fb) {
+                       drm_framebuffer_unreference(plane->old_fb);
+                       plane->old_fb = NULL;
+               }
+       }
+
+       drm_modeset_drop_locks(&ctx);
+       drm_modeset_acquire_fini(&ctx);
+
+       return ret;
+
+fail:
+       if (ret == -EDEADLK)
+               goto backoff;
+
+       if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT) {
+               int ncrtcs = dev->mode_config.num_crtc;
+
+               for (i = 0; i < ncrtcs; i++) {
+                       struct drm_crtc_state *crtc_state = state->crtc_states[i];
+
+                       if (!crtc_state)
+                               continue;
+
+                       destroy_vblank_event(dev, file_priv, crtc_state->event);
+                       crtc_state->event = NULL;
+               }
+       }
+
+       drm_atomic_state_free(state);
+
+       drm_modeset_drop_locks(&ctx);
+       drm_modeset_acquire_fini(&ctx);
+
+       return ret;
+
+backoff:
+       drm_atomic_state_clear(state);
+       drm_modeset_backoff(&ctx);
+
+       goto retry;
+}
index bbdbe4721573a92667fe3989bbc1e0be8123bc2b..541ba833ed36edc3e99b6f64dfc3e505d279719f 100644 (file)
@@ -330,7 +330,29 @@ mode_fixup(struct drm_atomic_state *state)
        return 0;
 }
 
-static int
+/**
+ * drm_atomic_helper_check - validate state object for modeset changes
+ * @dev: DRM device
+ * @state: the driver state object
+ *
+ * Check the state object to see if the requested state is physically possible.
+ * This does all the crtc and connector related computations for an atomic
+ * update. It computes and updates crtc_state->mode_changed, adds any additional
+ * connectors needed for full modesets and calls down into ->mode_fixup
+ * functions of the driver backend.
+ *
+ * IMPORTANT:
+ *
+ * Drivers which update ->mode_changed (e.g. in their ->atomic_check hooks if a
+ * plane update can't be done without a full modeset) _must_ call this function
+ * afterwards after that change. It is permitted to call this function multiple
+ * times for the same update, e.g. when the ->atomic_check functions depend upon
+ * the adjusted dotclock for fifo space allocation and watermark computation.
+ *
+ * RETURNS
+ * Zero for success or -errno
+ */
+int
 drm_atomic_helper_check_modeset(struct drm_device *dev,
                                struct drm_atomic_state *state)
 {
@@ -406,23 +428,23 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
 
        return mode_fixup(state);
 }
+EXPORT_SYMBOL(drm_atomic_helper_check_modeset);
 
 /**
- * drm_atomic_helper_check - validate state object
+ * drm_atomic_helper_check - validate state object for modeset changes
  * @dev: DRM device
  * @state: the driver state object
  *
  * Check the state object to see if the requested state is physically possible.
- * Only crtcs and planes have check callbacks, so for any additional (global)
- * checking that a driver needs it can simply wrap that around this function.
- * Drivers without such needs can directly use this as their ->atomic_check()
- * callback.
+ * This does all the plane update related checks using by calling into the
+ * ->atomic_check hooks provided by the driver.
  *
  * RETURNS
  * Zero for success or -errno
  */
-int drm_atomic_helper_check(struct drm_device *dev,
-                           struct drm_atomic_state *state)
+int
+drm_atomic_helper_check_planes(struct drm_device *dev,
+                              struct drm_atomic_state *state)
 {
        int nplanes = dev->mode_config.num_total_plane;
        int ncrtcs = dev->mode_config.num_crtc;
@@ -445,7 +467,7 @@ int drm_atomic_helper_check(struct drm_device *dev,
 
                ret = funcs->atomic_check(plane, plane_state);
                if (ret) {
-                       DRM_DEBUG_KMS("[PLANE:%d] atomic check failed\n",
+                       DRM_DEBUG_KMS("[PLANE:%d] atomic driver check failed\n",
                                      plane->base.id);
                        return ret;
                }
@@ -465,16 +487,49 @@ int drm_atomic_helper_check(struct drm_device *dev,
 
                ret = funcs->atomic_check(crtc, state->crtc_states[i]);
                if (ret) {
-                       DRM_DEBUG_KMS("[CRTC:%d] atomic check failed\n",
+                       DRM_DEBUG_KMS("[CRTC:%d] atomic driver check failed\n",
                                      crtc->base.id);
                        return ret;
                }
        }
 
+       return ret;
+}
+EXPORT_SYMBOL(drm_atomic_helper_check_planes);
+
+/**
+ * drm_atomic_helper_check - validate state object
+ * @dev: DRM device
+ * @state: the driver state object
+ *
+ * Check the state object to see if the requested state is physically possible.
+ * Only crtcs and planes have check callbacks, so for any additional (global)
+ * checking that a driver needs it can simply wrap that around this function.
+ * Drivers without such needs can directly use this as their ->atomic_check()
+ * callback.
+ *
+ * This just wraps the two parts of the state checking for planes and modeset
+ * state in the default order: First it calls drm_atomic_helper_check_modeset()
+ * and then drm_atomic_helper_check_planes(). The assumption is that the
+ * ->atomic_check functions depend upon an updated adjusted_mode.clock to
+ * e.g. properly compute watermarks.
+ *
+ * RETURNS
+ * Zero for success or -errno
+ */
+int drm_atomic_helper_check(struct drm_device *dev,
+                           struct drm_atomic_state *state)
+{
+       int ret;
+
        ret = drm_atomic_helper_check_modeset(dev, state);
        if (ret)
                return ret;
 
+       ret = drm_atomic_helper_check_planes(dev, state);
+       if (ret)
+               return ret;
+
        return ret;
 }
 EXPORT_SYMBOL(drm_atomic_helper_check);
@@ -1222,7 +1277,7 @@ retry:
                goto fail;
        }
 
-       ret = drm_atomic_set_crtc_for_plane(state, plane, crtc);
+       ret = drm_atomic_set_crtc_for_plane(plane_state, crtc);
        if (ret != 0)
                goto fail;
        drm_atomic_set_fb_for_plane(plane_state, fb);
@@ -1301,7 +1356,7 @@ retry:
                goto fail;
        }
 
-       ret = drm_atomic_set_crtc_for_plane(state, plane, NULL);
+       ret = drm_atomic_set_crtc_for_plane(plane_state, NULL);
        if (ret != 0)
                goto fail;
        drm_atomic_set_fb_for_plane(plane_state, NULL);
@@ -1464,7 +1519,7 @@ retry:
 
                crtc_state->enable = false;
 
-               ret = drm_atomic_set_crtc_for_plane(state, crtc->primary, NULL);
+               ret = drm_atomic_set_crtc_for_plane(primary_state, NULL);
                if (ret != 0)
                        goto fail;
 
@@ -1479,7 +1534,7 @@ retry:
        crtc_state->enable = true;
        drm_mode_copy(&crtc_state->mode, set->mode);
 
-       ret = drm_atomic_set_crtc_for_plane(state, crtc->primary, crtc);
+       ret = drm_atomic_set_crtc_for_plane(primary_state, crtc);
        if (ret != 0)
                goto fail;
        drm_atomic_set_fb_for_plane(primary_state, set->fb);
@@ -1558,8 +1613,8 @@ retry:
                goto fail;
        }
 
-       ret = crtc->funcs->atomic_set_property(crtc, crtc_state,
-                                              property, val);
+       ret = drm_atomic_crtc_set_property(crtc, crtc_state,
+                       property, val);
        if (ret)
                goto fail;
 
@@ -1617,8 +1672,8 @@ retry:
                goto fail;
        }
 
-       ret = plane->funcs->atomic_set_property(plane, plane_state,
-                                              property, val);
+       ret = drm_atomic_plane_set_property(plane, plane_state,
+                       property, val);
        if (ret)
                goto fail;
 
@@ -1676,8 +1731,8 @@ retry:
                goto fail;
        }
 
-       ret = connector->funcs->atomic_set_property(connector, connector_state,
-                                              property, val);
+       ret = drm_atomic_connector_set_property(connector, connector_state,
+                       property, val);
        if (ret)
                goto fail;
 
@@ -1751,7 +1806,7 @@ retry:
                goto fail;
        }
 
-       ret = drm_atomic_set_crtc_for_plane(state, plane, crtc);
+       ret = drm_atomic_set_crtc_for_plane(plane_state, crtc);
        if (ret != 0)
                goto fail;
        drm_atomic_set_fb_for_plane(plane_state, fb);
@@ -1814,6 +1869,9 @@ void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc)
 {
        kfree(crtc->state);
        crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL);
+
+       if (crtc->state)
+               crtc->state->crtc = crtc;
 }
 EXPORT_SYMBOL(drm_atomic_helper_crtc_reset);
 
@@ -1873,6 +1931,9 @@ void drm_atomic_helper_plane_reset(struct drm_plane *plane)
 
        kfree(plane->state);
        plane->state = kzalloc(sizeof(*plane->state), GFP_KERNEL);
+
+       if (plane->state)
+               plane->state->plane = plane;
 }
 EXPORT_SYMBOL(drm_atomic_helper_plane_reset);
 
@@ -1930,6 +1991,9 @@ void drm_atomic_helper_connector_reset(struct drm_connector *connector)
 {
        kfree(connector->state);
        connector->state = kzalloc(sizeof(*connector->state), GFP_KERNEL);
+
+       if (connector->state)
+               connector->state->connector = connector;
 }
 EXPORT_SYMBOL(drm_atomic_helper_connector_reset);
 
index 2892d746a1e9552672a4eab5d37eb453aefc945e..7e4acad3f6f9f132f40c3ea76d54e5048105461e 100644 (file)
@@ -38,6 +38,7 @@
 #include <drm/drm_edid.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_modeset_lock.h>
+#include <drm/drm_atomic.h>
 
 #include "drm_crtc_internal.h"
 #include "drm_internal.h"
@@ -830,6 +831,7 @@ int drm_connector_init(struct drm_device *dev,
                       const struct drm_connector_funcs *funcs,
                       int connector_type)
 {
+       struct drm_mode_config *config = &dev->mode_config;
        int ret;
        struct ida *connector_ida =
                &drm_connector_enum_list[connector_type].ida;
@@ -868,16 +870,20 @@ int drm_connector_init(struct drm_device *dev,
 
        /* We should add connectors at the end to avoid upsetting the connector
         * index too much. */
-       list_add_tail(&connector->head, &dev->mode_config.connector_list);
-       dev->mode_config.num_connector++;
+       list_add_tail(&connector->head, &config->connector_list);
+       config->num_connector++;
 
        if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL)
                drm_object_attach_property(&connector->base,
-                                             dev->mode_config.edid_property,
+                                             config->edid_property,
                                              0);
 
        drm_object_attach_property(&connector->base,
-                                     dev->mode_config.dpms_property, 0);
+                                     config->dpms_property, 0);
+
+       if (drm_core_check_feature(dev, DRIVER_ATOMIC)) {
+               drm_object_attach_property(&connector->base, config->prop_crtc_id, 0);
+       }
 
        connector->debugfs_entry = NULL;
 
@@ -1168,6 +1174,7 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
                             const uint32_t *formats, uint32_t format_count,
                             enum drm_plane_type type)
 {
+       struct drm_mode_config *config = &dev->mode_config;
        int ret;
 
        ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
@@ -1192,15 +1199,28 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
        plane->possible_crtcs = possible_crtcs;
        plane->type = type;
 
-       list_add_tail(&plane->head, &dev->mode_config.plane_list);
-       dev->mode_config.num_total_plane++;
+       list_add_tail(&plane->head, &config->plane_list);
+       config->num_total_plane++;
        if (plane->type == DRM_PLANE_TYPE_OVERLAY)
-               dev->mode_config.num_overlay_plane++;
+               config->num_overlay_plane++;
 
        drm_object_attach_property(&plane->base,
-                                  dev->mode_config.plane_type_property,
+                                  config->plane_type_property,
                                   plane->type);
 
+       if (drm_core_check_feature(dev, DRIVER_ATOMIC)) {
+               drm_object_attach_property(&plane->base, config->prop_fb_id, 0);
+               drm_object_attach_property(&plane->base, config->prop_crtc_id, 0);
+               drm_object_attach_property(&plane->base, config->prop_crtc_x, 0);
+               drm_object_attach_property(&plane->base, config->prop_crtc_y, 0);
+               drm_object_attach_property(&plane->base, config->prop_crtc_w, 0);
+               drm_object_attach_property(&plane->base, config->prop_crtc_h, 0);
+               drm_object_attach_property(&plane->base, config->prop_src_x, 0);
+               drm_object_attach_property(&plane->base, config->prop_src_y, 0);
+               drm_object_attach_property(&plane->base, config->prop_src_w, 0);
+               drm_object_attach_property(&plane->base, config->prop_src_h, 0);
+       }
+
        return 0;
 }
 EXPORT_SYMBOL(drm_universal_plane_init);
@@ -1322,50 +1342,109 @@ void drm_plane_force_disable(struct drm_plane *plane)
 }
 EXPORT_SYMBOL(drm_plane_force_disable);
 
-static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
+static int drm_mode_create_standard_properties(struct drm_device *dev)
 {
-       struct drm_property *edid;
-       struct drm_property *dpms;
-       struct drm_property *dev_path;
+       struct drm_property *prop;
 
        /*
         * Standard properties (apply to all connectors)
         */
-       edid = drm_property_create(dev, DRM_MODE_PROP_BLOB |
+       prop = drm_property_create(dev, DRM_MODE_PROP_BLOB |
                                   DRM_MODE_PROP_IMMUTABLE,
                                   "EDID", 0);
-       dev->mode_config.edid_property = edid;
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.edid_property = prop;
 
-       dpms = drm_property_create_enum(dev, 0,
+       prop = drm_property_create_enum(dev, 0,
                                   "DPMS", drm_dpms_enum_list,
                                   ARRAY_SIZE(drm_dpms_enum_list));
-       dev->mode_config.dpms_property = dpms;
-
-       dev_path = drm_property_create(dev,
-                                      DRM_MODE_PROP_BLOB |
-                                      DRM_MODE_PROP_IMMUTABLE,
-                                      "PATH", 0);
-       dev->mode_config.path_property = dev_path;
-
-       dev->mode_config.tile_property = drm_property_create(dev,
-                                                            DRM_MODE_PROP_BLOB |
-                                                            DRM_MODE_PROP_IMMUTABLE,
-                                                            "TILE", 0);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.dpms_property = prop;
 
-       return 0;
-}
+       prop = drm_property_create(dev,
+                                  DRM_MODE_PROP_BLOB |
+                                  DRM_MODE_PROP_IMMUTABLE,
+                                  "PATH", 0);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.path_property = prop;
 
-static int drm_mode_create_standard_plane_properties(struct drm_device *dev)
-{
-       struct drm_property *type;
+       prop = drm_property_create(dev,
+                                  DRM_MODE_PROP_BLOB |
+                                  DRM_MODE_PROP_IMMUTABLE,
+                                  "TILE", 0);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.tile_property = prop;
 
-       /*
-        * Standard properties (apply to all planes)
-        */
-       type = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
+       prop = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
                                        "type", drm_plane_type_enum_list,
                                        ARRAY_SIZE(drm_plane_type_enum_list));
-       dev->mode_config.plane_type_property = type;
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.plane_type_property = prop;
+
+       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+                       "SRC_X", 0, UINT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_src_x = prop;
+
+       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+                       "SRC_Y", 0, UINT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_src_y = prop;
+
+       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+                       "SRC_W", 0, UINT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_src_w = prop;
+
+       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+                       "SRC_H", 0, UINT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_src_h = prop;
+
+       prop = drm_property_create_signed_range(dev, DRM_MODE_PROP_ATOMIC,
+                       "CRTC_X", INT_MIN, INT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_crtc_x = prop;
+
+       prop = drm_property_create_signed_range(dev, DRM_MODE_PROP_ATOMIC,
+                       "CRTC_Y", INT_MIN, INT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_crtc_y = prop;
+
+       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+                       "CRTC_W", 0, INT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_crtc_w = prop;
+
+       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+                       "CRTC_H", 0, INT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_crtc_h = prop;
+
+       prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC,
+                       "FB_ID", DRM_MODE_OBJECT_FB);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_fb_id = prop;
+
+       prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC,
+                       "CRTC_ID", DRM_MODE_OBJECT_CRTC);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_crtc_id = prop;
 
        return 0;
 }
@@ -1991,6 +2070,44 @@ static struct drm_encoder *drm_connector_get_encoder(struct drm_connector *conne
        return connector->encoder;
 }
 
+/* helper for getconnector and getproperties ioctls */
+static int get_properties(struct drm_mode_object *obj, bool atomic,
+               uint32_t __user *prop_ptr, uint64_t __user *prop_values,
+               uint32_t *arg_count_props)
+{
+       int props_count;
+       int i, ret, copied;
+
+       props_count = obj->properties->count;
+       if (!atomic)
+               props_count -= obj->properties->atomic_count;
+
+       if ((*arg_count_props >= props_count) && props_count) {
+               for (i = 0, copied = 0; copied < props_count; i++) {
+                       struct drm_property *prop = obj->properties->properties[i];
+                       uint64_t val;
+
+                       if ((prop->flags & DRM_MODE_PROP_ATOMIC) && !atomic)
+                               continue;
+
+                       ret = drm_object_property_get_value(obj, prop, &val);
+                       if (ret)
+                               return ret;
+
+                       if (put_user(prop->base.id, prop_ptr + copied))
+                               return -EFAULT;
+
+                       if (put_user(val, prop_values + copied))
+                               return -EFAULT;
+
+                       copied++;
+               }
+       }
+       *arg_count_props = props_count;
+
+       return 0;
+}
+
 /**
  * drm_mode_getconnector - get connector configuration
  * @dev: drm device for the ioctl
@@ -2012,15 +2129,12 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
        struct drm_encoder *encoder;
        struct drm_display_mode *mode;
        int mode_count = 0;
-       int props_count = 0;
        int encoders_count = 0;
        int ret = 0;
        int copied = 0;
        int i;
        struct drm_mode_modeinfo u_mode;
        struct drm_mode_modeinfo __user *mode_ptr;
-       uint32_t __user *prop_ptr;
-       uint64_t __user *prop_values;
        uint32_t __user *encoder_ptr;
 
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
@@ -2031,6 +2145,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
        DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id);
 
        mutex_lock(&dev->mode_config.mutex);
+       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
 
        connector = drm_connector_find(dev, out_resp->connector_id);
        if (!connector) {
@@ -2038,8 +2153,6 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
                goto out;
        }
 
-       props_count = connector->properties.count;
-
        for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++)
                if (connector->encoder_ids[i] != 0)
                        encoders_count++;
@@ -2062,14 +2175,11 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
        out_resp->mm_height = connector->display_info.height_mm;
        out_resp->subpixel = connector->display_info.subpixel_order;
        out_resp->connection = connector->status;
-       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
-
        encoder = drm_connector_get_encoder(connector);
        if (encoder)
                out_resp->encoder_id = encoder->base.id;
        else
                out_resp->encoder_id = 0;
-       drm_modeset_unlock(&dev->mode_config.connection_mutex);
 
        /*
         * This ioctl is called twice, once to determine how much space is
@@ -2093,26 +2203,12 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
        }
        out_resp->count_modes = mode_count;
 
-       if ((out_resp->count_props >= props_count) && props_count) {
-               copied = 0;
-               prop_ptr = (uint32_t __user *)(unsigned long)(out_resp->props_ptr);
-               prop_values = (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr);
-               for (i = 0; i < connector->properties.count; i++) {
-                       if (put_user(connector->properties.ids[i],
-                                    prop_ptr + copied)) {
-                               ret = -EFAULT;
-                               goto out;
-                       }
-
-                       if (put_user(connector->properties.values[i],
-                                    prop_values + copied)) {
-                               ret = -EFAULT;
-                               goto out;
-                       }
-                       copied++;
-               }
-       }
-       out_resp->count_props = props_count;
+       ret = get_properties(&connector->base, file_priv->atomic,
+                       (uint32_t __user *)(unsigned long)(out_resp->props_ptr),
+                       (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr),
+                       &out_resp->count_props);
+       if (ret)
+               goto out;
 
        if ((out_resp->count_encoders >= encoders_count) && encoders_count) {
                copied = 0;
@@ -2131,6 +2227,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
        out_resp->count_encoders = encoders_count;
 
 out:
+       drm_modeset_unlock(&dev->mode_config.connection_mutex);
        mutex_unlock(&dev->mode_config.mutex);
 
        return ret;
@@ -3823,9 +3920,11 @@ void drm_object_attach_property(struct drm_mode_object *obj,
                return;
        }
 
-       obj->properties->ids[count] = property->base.id;
+       obj->properties->properties[count] = property;
        obj->properties->values[count] = init_val;
        obj->properties->count++;
+       if (property->flags & DRM_MODE_PROP_ATOMIC)
+               obj->properties->atomic_count++;
 }
 EXPORT_SYMBOL(drm_object_attach_property);
 
@@ -3848,7 +3947,7 @@ int drm_object_property_set_value(struct drm_mode_object *obj,
        int i;
 
        for (i = 0; i < obj->properties->count; i++) {
-               if (obj->properties->ids[i] == property->base.id) {
+               if (obj->properties->properties[i] == property) {
                        obj->properties->values[i] = val;
                        return 0;
                }
@@ -3877,8 +3976,16 @@ int drm_object_property_get_value(struct drm_mode_object *obj,
 {
        int i;
 
+       /* read-only properties bypass atomic mechanism and still store
+        * their value in obj->properties->values[].. mostly to avoid
+        * having to deal w/ EDID and similar props in atomic paths:
+        */
+       if (drm_core_check_feature(property->dev, DRIVER_ATOMIC) &&
+                       !(property->flags & DRM_MODE_PROP_IMMUTABLE))
+               return drm_atomic_get_property(obj, property, val);
+
        for (i = 0; i < obj->properties->count; i++) {
-               if (obj->properties->ids[i] == property->base.id) {
+               if (obj->properties->properties[i] == property) {
                        *val = obj->properties->values[i];
                        return 0;
                }
@@ -4194,14 +4301,24 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector,
 }
 EXPORT_SYMBOL(drm_mode_connector_update_edid_property);
 
-static bool drm_property_change_is_valid(struct drm_property *property,
-                                        uint64_t value)
+/* Some properties could refer to dynamic refcnt'd objects, or things that
+ * need special locking to handle lifetime issues (ie. to ensure the prop
+ * value doesn't become invalid part way through the property update due to
+ * race).  The value returned by reference via 'obj' should be passed back
+ * to drm_property_change_valid_put() after the property is set (and the
+ * object to which the property is attached has a chance to take it's own
+ * reference).
+ */
+bool drm_property_change_valid_get(struct drm_property *property,
+                                        uint64_t value, struct drm_mode_object **ref)
 {
        int i;
 
        if (property->flags & DRM_MODE_PROP_IMMUTABLE)
                return false;
 
+       *ref = NULL;
+
        if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) {
                if (value < property->values[0] || value > property->values[1])
                        return false;
@@ -4223,20 +4340,29 @@ static bool drm_property_change_is_valid(struct drm_property *property,
                /* Only the driver knows */
                return true;
        } else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) {
-               struct drm_mode_object *obj;
-
                /* a zero value for an object property translates to null: */
                if (value == 0)
                        return true;
-               /*
-                * NOTE: use _object_find() directly to bypass restriction on
-                * looking up refcnt'd objects (ie. fb's).  For a refcnt'd
-                * object this could race against object finalization, so it
-                * simply tells us that the object *was* valid.  Which is good
-                * enough.
-                */
-               obj = _object_find(property->dev, value, property->values[0]);
-               return obj != NULL;
+
+               /* handle refcnt'd objects specially: */
+               if (property->values[0] == DRM_MODE_OBJECT_FB) {
+                       struct drm_framebuffer *fb;
+                       fb = drm_framebuffer_lookup(property->dev, value);
+                       if (fb) {
+                               *ref = &fb->base;
+                               return true;
+                       } else {
+                               return false;
+                       }
+               } else {
+                       return _object_find(property->dev, value, property->values[0]) != NULL;
+               }
+       } else {
+               int i;
+               for (i = 0; i < property->num_values; i++)
+                       if (property->values[i] == value)
+                               return true;
+               return false;
        }
 
        for (i = 0; i < property->num_values; i++)
@@ -4245,6 +4371,18 @@ static bool drm_property_change_is_valid(struct drm_property *property,
        return false;
 }
 
+void drm_property_change_valid_put(struct drm_property *property,
+               struct drm_mode_object *ref)
+{
+       if (!ref)
+               return;
+
+       if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) {
+               if (property->values[0] == DRM_MODE_OBJECT_FB)
+                       drm_framebuffer_unreference(obj_to_fb(ref));
+       }
+}
+
 /**
  * drm_mode_connector_property_set_ioctl - set the current value of a connector property
  * @dev: DRM device
@@ -4360,11 +4498,6 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,
        struct drm_mode_obj_get_properties *arg = data;
        struct drm_mode_object *obj;
        int ret = 0;
-       int i;
-       int copied = 0;
-       int props_count = 0;
-       uint32_t __user *props_ptr;
-       uint64_t __user *prop_values_ptr;
 
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -EINVAL;
@@ -4381,30 +4514,11 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,
                goto out;
        }
 
-       props_count = obj->properties->count;
+       ret = get_properties(obj, file_priv->atomic,
+                       (uint32_t __user *)(unsigned long)(arg->props_ptr),
+                       (uint64_t __user *)(unsigned long)(arg->prop_values_ptr),
+                       &arg->count_props);
 
-       /* This ioctl is called twice, once to determine how much space is
-        * needed, and the 2nd time to fill it. */
-       if ((arg->count_props >= props_count) && props_count) {
-               copied = 0;
-               props_ptr = (uint32_t __user *)(unsigned long)(arg->props_ptr);
-               prop_values_ptr = (uint64_t __user *)(unsigned long)
-                                 (arg->prop_values_ptr);
-               for (i = 0; i < props_count; i++) {
-                       if (put_user(obj->properties->ids[i],
-                                    props_ptr + copied)) {
-                               ret = -EFAULT;
-                               goto out;
-                       }
-                       if (put_user(obj->properties->values[i],
-                                    prop_values_ptr + copied)) {
-                               ret = -EFAULT;
-                               goto out;
-                       }
-                       copied++;
-               }
-       }
-       arg->count_props = props_count;
 out:
        drm_modeset_unlock_all(dev);
        return ret;
@@ -4433,8 +4547,8 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
        struct drm_mode_object *arg_obj;
        struct drm_mode_object *prop_obj;
        struct drm_property *property;
-       int ret = -EINVAL;
-       int i;
+       int i, ret = -EINVAL;
+       struct drm_mode_object *ref;
 
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -EINVAL;
@@ -4450,7 +4564,7 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
                goto out;
 
        for (i = 0; i < arg_obj->properties->count; i++)
-               if (arg_obj->properties->ids[i] == arg->prop_id)
+               if (arg_obj->properties->properties[i]->base.id == arg->prop_id)
                        break;
 
        if (i == arg_obj->properties->count)
@@ -4464,7 +4578,7 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
        }
        property = obj_to_property(prop_obj);
 
-       if (!drm_property_change_is_valid(property, arg->value))
+       if (!drm_property_change_valid_get(property, arg->value, &ref))
                goto out;
 
        switch (arg_obj->type) {
@@ -4481,6 +4595,8 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
                break;
        }
 
+       drm_property_change_valid_put(property, ref);
+
 out:
        drm_modeset_unlock_all(dev);
        return ret;
@@ -5225,8 +5341,7 @@ void drm_mode_config_init(struct drm_device *dev)
        idr_init(&dev->mode_config.tile_idr);
 
        drm_modeset_lock_all(dev);
-       drm_mode_create_standard_connector_properties(dev);
-       drm_mode_create_standard_plane_properties(dev);
+       drm_mode_create_standard_properties(dev);
        drm_modeset_unlock_all(dev);
 
        /* Just to be sure */
index d552708409ded9cb3d8a81891d4824da4711f162..b1979e7bdc8862548d3f483eac109bf7959df27e 100644 (file)
@@ -946,6 +946,7 @@ int drm_helper_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mod
                crtc_state = kzalloc(sizeof(*crtc_state), GFP_KERNEL);
        if (!crtc_state)
                return -ENOMEM;
+       crtc_state->crtc = crtc;
 
        crtc_state->enable = true;
        crtc_state->planes_changed = true;
@@ -1005,6 +1006,7 @@ int drm_helper_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
                plane_state = kzalloc(sizeof(*plane_state), GFP_KERNEL);
        if (!plane_state)
                return -ENOMEM;
+       plane_state->plane = plane;
 
        plane_state->crtc = crtc;
        drm_atomic_set_fb_for_plane(plane_state, crtc->primary->fb);
index a2945ee6d6755bc101bdd3ef184ea891b7bbeea8..247dc8b625645b308631201486df4bfd1556eef8 100644 (file)
@@ -36,3 +36,9 @@ int drm_mode_object_get(struct drm_device *dev,
 void drm_mode_object_put(struct drm_device *dev,
                         struct drm_mode_object *object);
 
+/* drm_atomic.c */
+int drm_atomic_get_property(struct drm_mode_object *obj,
+                          struct drm_property *property, uint64_t *val);
+int drm_mode_atomic_ioctl(struct drm_device *dev,
+                         void *data, struct drm_file *file_priv);
+
index 4f41377b0b80963f9ce84b3492412d025924652b..d51213464672a9c1b7d3362f355b6ddde29c2403 100644 (file)
 unsigned int drm_debug = 0;    /* 1 to enable debug output */
 EXPORT_SYMBOL(drm_debug);
 
+bool drm_atomic = 0;
+
 MODULE_AUTHOR(CORE_AUTHOR);
 MODULE_DESCRIPTION(CORE_DESC);
 MODULE_LICENSE("GPL and additional rights");
 MODULE_PARM_DESC(debug, "Enable debug output");
+MODULE_PARM_DESC(atomic, "Enable experimental atomic KMS API");
 MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs] (0: never disable, <0: disable immediately)");
 MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]");
 MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps");
 
 module_param_named(debug, drm_debug, int, 0600);
+module_param_named_unsafe(atomic, drm_atomic, bool, 0600);
 
 static DEFINE_SPINLOCK(drm_minor_lock);
 static struct idr drm_minors_idr;
index 00587a1e3c83c0dc23c05a55a7d37ea10f891847..3785d66721f2f6fdba2d134ba8eaa0c9ab8fb416 100644 (file)
@@ -32,6 +32,7 @@
 #include <drm/drm_core.h>
 #include "drm_legacy.h"
 #include "drm_internal.h"
+#include "drm_crtc_internal.h"
 
 #include <linux/pci.h>
 #include <linux/export.h>
@@ -345,6 +346,17 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
                        return -EINVAL;
                file_priv->universal_planes = req->value;
                break;
+       case DRM_CLIENT_CAP_ATOMIC:
+               /* for now, hide behind experimental drm.atomic moduleparam */
+               if (!drm_atomic)
+                       return -EINVAL;
+               if (!drm_core_check_feature(dev, DRIVER_ATOMIC))
+                       return -EINVAL;
+               if (req->value > 1)
+                       return -EINVAL;
+               file_priv->atomic = req->value;
+               file_priv->universal_planes = req->value;
+               break;
        default:
                return -EINVAL;
        }
@@ -620,6 +632,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
        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_CURSOR2, drm_mode_cursor2_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATOMIC, drm_mode_atomic_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
 };
 
 #define DRM_CORE_IOCTL_COUNT   ARRAY_SIZE( drm_ioctls )
index 391dfb7d1086e808c01d21d4f67db56285613d43..f24c4cfe674b9e0d75a36f0ab017afef7bcddb5f 100644 (file)
@@ -523,6 +523,7 @@ int drm_plane_helper_update(struct drm_plane *plane, struct drm_crtc *crtc,
                plane_state = kzalloc(sizeof(*plane_state), GFP_KERNEL);
        if (!plane_state)
                return -ENOMEM;
+       plane_state->plane = plane;
 
        plane_state->crtc = crtc;
        drm_atomic_set_fb_for_plane(plane_state, fb);
@@ -569,6 +570,7 @@ int drm_plane_helper_disable(struct drm_plane *plane)
                plane_state = kzalloc(sizeof(*plane_state), GFP_KERNEL);
        if (!plane_state)
                return -ENOMEM;
+       plane_state->plane = plane;
 
        plane_state->crtc = NULL;
        drm_atomic_set_fb_for_plane(plane_state, NULL);
index 26e5fdea6594c7d23854bc566ffe618453c455dd..fc76f630e5b18fe653013c5b7aa95ef114210ffe 100644 (file)
@@ -113,6 +113,7 @@ static void mdp5_plane_reset(struct drm_plane *plane)
        } else {
                mdp5_state->zpos = 1 + drm_plane_index(plane);
        }
+       mdp5_state->base.plane = plane;
 
        plane->state = &mdp5_state->base;
 }
index 191968256c5822ae0a7964db31377e5ba7b0e0ce..2c396540e279efdbfdc0cd9c0038728d04c7e65b 100644 (file)
@@ -127,6 +127,26 @@ static void add_fb(struct msm_commit *c, struct drm_framebuffer *fb)
 }
 
 
+int msm_atomic_check(struct drm_device *dev,
+                    struct drm_atomic_state *state)
+{
+       int ret;
+
+       /*
+        * msm ->atomic_check can update ->mode_changed for pixel format
+        * changes, hence must be run before we check the modeset changes.
+        */
+       ret = drm_atomic_helper_check_planes(dev, state);
+       if (ret)
+               return ret;
+
+       ret = drm_atomic_helper_check_modeset(dev, state);
+       if (ret)
+               return ret;
+
+       return ret;
+}
+
 /**
  * drm_atomic_helper_commit - commit validated state object
  * @dev: DRM device
index 9a61546a0b05276cd313b0877306b02930fa2fa5..f1ebedde6346d6c966bceaf0b351a549d8d87d7f 100644 (file)
@@ -29,7 +29,7 @@ static void msm_fb_output_poll_changed(struct drm_device *dev)
 static const struct drm_mode_config_funcs mode_config_funcs = {
        .fb_create = msm_framebuffer_create,
        .output_poll_changed = msm_fb_output_poll_changed,
-       .atomic_check = drm_atomic_helper_check,
+       .atomic_check = msm_atomic_check,
        .atomic_commit = msm_atomic_commit,
 };
 
index b69ef2d5a26c0a0afa9896ed02a88a087f9cebfe..22e5391a7ce8f321d06d3bd0ec7dd1902d8b2228 100644 (file)
@@ -148,6 +148,8 @@ void __msm_fence_worker(struct work_struct *work);
                (_cb)->func = _func;                         \
        } while (0)
 
+int msm_atomic_check(struct drm_device *dev,
+                    struct drm_atomic_state *state);
 int msm_atomic_commit(struct drm_device *dev,
                struct drm_atomic_state *state, bool async);
 
index a5f6a1f563c4f5b658fb43634af72ef1395766ec..e928625a9da0be41b7b03508a19f3df2cfb8acb5 100644 (file)
@@ -143,6 +143,7 @@ void drm_err(const char *format, ...);
 #define DRIVER_MODESET     0x2000
 #define DRIVER_PRIME       0x4000
 #define DRIVER_RENDER      0x8000
+#define DRIVER_ATOMIC      0x10000
 
 /***********************************************************************/
 /** \name Macros to make printk easier */
@@ -283,6 +284,8 @@ struct drm_file {
         * in the plane list
         */
        unsigned universal_planes:1;
+       /* true if client understands atomic properties */
+       unsigned atomic:1;
 
        struct pid *pid;
        kuid_t uid;
@@ -954,6 +957,7 @@ extern void drm_master_put(struct drm_master **master);
 extern void drm_put_dev(struct drm_device *dev);
 extern void drm_unplug_dev(struct drm_device *dev);
 extern unsigned int drm_debug;
+extern bool drm_atomic;
 
                                /* Debugfs support */
 #if defined(CONFIG_DEBUG_FS)
index ad2229574dd9456e9ba28a4c8e16f2492963b3b3..51168a8b723a2d45f4ff658eb27d2b54bd5f1d88 100644 (file)
@@ -38,16 +38,25 @@ void drm_atomic_state_free(struct drm_atomic_state *state);
 struct drm_crtc_state * __must_check
 drm_atomic_get_crtc_state(struct drm_atomic_state *state,
                          struct drm_crtc *crtc);
+int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
+               struct drm_crtc_state *state, struct drm_property *property,
+               uint64_t val);
 struct drm_plane_state * __must_check
 drm_atomic_get_plane_state(struct drm_atomic_state *state,
                           struct drm_plane *plane);
+int drm_atomic_plane_set_property(struct drm_plane *plane,
+               struct drm_plane_state *state, struct drm_property *property,
+               uint64_t val);
 struct drm_connector_state * __must_check
 drm_atomic_get_connector_state(struct drm_atomic_state *state,
                               struct drm_connector *connector);
+int drm_atomic_connector_set_property(struct drm_connector *connector,
+               struct drm_connector_state *state, struct drm_property *property,
+               uint64_t val);
 
 int __must_check
-drm_atomic_set_crtc_for_plane(struct drm_atomic_state *state,
-                             struct drm_plane *plane, struct drm_crtc *crtc);
+drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state,
+                             struct drm_crtc *crtc);
 void drm_atomic_set_fb_for_plane(struct drm_plane_state *plane_state,
                                 struct drm_framebuffer *fb);
 int __must_check
index f956b413311e15eed5653b348708468d26197ed9..2095917ff8c77d8ca691f34bb3bbb21c726f0f07 100644 (file)
 
 #include <drm/drm_crtc.h>
 
+int drm_atomic_helper_check_modeset(struct drm_device *dev,
+                               struct drm_atomic_state *state);
+int drm_atomic_helper_check_planes(struct drm_device *dev,
+                              struct drm_atomic_state *state);
 int drm_atomic_helper_check(struct drm_device *dev,
                            struct drm_atomic_state *state);
 int drm_atomic_helper_commit(struct drm_device *dev,
index 291239f2fafca3b75091e6333f40f5dc5d302a38..6588bffb65180458e968f9f3230a2b4b3174f838 100644 (file)
@@ -63,8 +63,16 @@ struct drm_mode_object {
 
 #define DRM_OBJECT_MAX_PROPERTY 24
 struct drm_object_properties {
-       int count;
-       uint32_t ids[DRM_OBJECT_MAX_PROPERTY];
+       int count, atomic_count;
+       /* NOTE: if we ever start dynamically destroying properties (ie.
+        * not at drm_mode_config_cleanup() time), then we'd have to do
+        * a better job of detaching property from mode objects to avoid
+        * dangling property pointers:
+        */
+       struct drm_property *properties[DRM_OBJECT_MAX_PROPERTY];
+       /* do not read/write values directly, but use drm_object_property_get_value()
+        * and drm_object_property_set_value():
+        */
        uint64_t values[DRM_OBJECT_MAX_PROPERTY];
 };
 
@@ -237,7 +245,9 @@ struct drm_atomic_state;
 
 /**
  * struct drm_crtc_state - mutable CRTC state
+ * @crtc: backpointer to the CRTC
  * @enable: whether the CRTC should be enabled, gates all other state
+ * @active: whether the CRTC is actively displaying (used for DPMS)
  * @mode_changed: for use by helpers and drivers when computing state updates
  * @plane_mask: bitmask of (1 << drm_plane_index(plane)) of attached planes
  * @last_vblank_count: for helpers and drivers to capture the vblank of the
@@ -248,9 +258,18 @@ struct drm_atomic_state;
  * @event: optional pointer to a DRM event to signal upon completion of the
  *     state update
  * @state: backpointer to global drm_atomic_state
+ *
+ * Note that the distinction between @enable and @active is rather subtile:
+ * Flipping @active while @enable is set without changing anything else may
+ * never return in a failure from the ->atomic_check callback. Userspace assumes
+ * that a DPMS On will always succeed. In other words: @enable controls resource
+ * assignment, @active controls the actual hardware state.
  */
 struct drm_crtc_state {
+       struct drm_crtc *crtc;
+
        bool enable;
+       bool active;
 
        /* computed state bits used by helpers and drivers */
        bool planes_changed : 1;
@@ -292,6 +311,9 @@ struct drm_crtc_state {
  * @atomic_duplicate_state: duplicate the atomic state for this CRTC
  * @atomic_destroy_state: destroy an atomic state for this CRTC
  * @atomic_set_property: set a property on an atomic state for this CRTC
+ *    (do not call directly, use drm_atomic_crtc_set_property())
+ * @atomic_get_property: get a property on an atomic state for this CRTC
+ *    (do not call directly, use drm_atomic_crtc_get_property())
  *
  * The drm_crtc_funcs structure is the central CRTC management structure
  * in the DRM.  Each CRTC controls one or more connectors (note that the name
@@ -351,6 +373,10 @@ struct drm_crtc_funcs {
                                   struct drm_crtc_state *state,
                                   struct drm_property *property,
                                   uint64_t val);
+       int (*atomic_get_property)(struct drm_crtc *crtc,
+                                  const struct drm_crtc_state *state,
+                                  struct drm_property *property,
+                                  uint64_t *val);
 };
 
 /**
@@ -449,11 +475,14 @@ struct drm_crtc {
 
 /**
  * struct drm_connector_state - mutable connector state
+ * @connector: backpointer to the connector
  * @crtc: CRTC to connect connector to, NULL if disabled
  * @best_encoder: can be used by helpers and drivers to select the encoder
  * @state: backpointer to global drm_atomic_state
  */
 struct drm_connector_state {
+       struct drm_connector *connector;
+
        struct drm_crtc *crtc;  /* do not write directly, use drm_atomic_set_crtc_for_connector() */
 
        struct drm_encoder *best_encoder;
@@ -475,6 +504,9 @@ struct drm_connector_state {
  * @atomic_duplicate_state: duplicate the atomic state for this connector
  * @atomic_destroy_state: destroy an atomic state for this connector
  * @atomic_set_property: set a property on an atomic state for this connector
+ *    (do not call directly, use drm_atomic_connector_set_property())
+ * @atomic_get_property: get a property on an atomic state for this connector
+ *    (do not call directly, use drm_atomic_connector_get_property())
  *
  * Each CRTC may have one or more connectors attached to it.  The functions
  * below allow the core DRM code to control connectors, enumerate available modes,
@@ -508,6 +540,10 @@ struct drm_connector_funcs {
                                   struct drm_connector_state *state,
                                   struct drm_property *property,
                                   uint64_t val);
+       int (*atomic_get_property)(struct drm_connector *connector,
+                                  const struct drm_connector_state *state,
+                                  struct drm_property *property,
+                                  uint64_t *val);
 };
 
 /**
@@ -693,6 +729,7 @@ struct drm_connector {
 
 /**
  * struct drm_plane_state - mutable plane state
+ * @plane: backpointer to the plane
  * @crtc: currently bound CRTC, NULL if disabled
  * @fb: currently bound framebuffer
  * @fence: optional fence to wait for before scanning out @fb
@@ -709,6 +746,8 @@ struct drm_connector {
  * @state: backpointer to global drm_atomic_state
  */
 struct drm_plane_state {
+       struct drm_plane *plane;
+
        struct drm_crtc *crtc;   /* do not write directly, use drm_atomic_set_crtc_for_plane() */
        struct drm_framebuffer *fb;  /* do not write directly, use drm_atomic_set_fb_for_plane() */
        struct fence *fence;
@@ -735,6 +774,9 @@ struct drm_plane_state {
  * @atomic_duplicate_state: duplicate the atomic state for this plane
  * @atomic_destroy_state: destroy an atomic state for this plane
  * @atomic_set_property: set a property on an atomic state for this plane
+ *    (do not call directly, use drm_atomic_plane_set_property())
+ * @atomic_get_property: get a property on an atomic state for this plane
+ *    (do not call directly, use drm_atomic_plane_get_property())
  */
 struct drm_plane_funcs {
        int (*update_plane)(struct drm_plane *plane,
@@ -758,6 +800,10 @@ struct drm_plane_funcs {
                                   struct drm_plane_state *state,
                                   struct drm_property *property,
                                   uint64_t val);
+       int (*atomic_get_property)(struct drm_plane *plane,
+                                  const struct drm_plane_state *state,
+                                  struct drm_property *property,
+                                  uint64_t *val);
 };
 
 enum drm_plane_type {
@@ -856,7 +902,7 @@ struct drm_bridge {
 /**
  * struct struct drm_atomic_state - the global state object for atomic updates
  * @dev: parent DRM device
- * @flags: state flags like async update
+ * @allow_modeset: allow full modeset
  * @planes: pointer to array of plane pointers
  * @plane_states: pointer to array of plane states pointers
  * @crtcs: pointer to array of CRTC pointers
@@ -868,7 +914,7 @@ struct drm_bridge {
  */
 struct drm_atomic_state {
        struct drm_device *dev;
-       uint32_t flags;
+       bool allow_modeset : 1;
        struct drm_plane **planes;
        struct drm_plane_state **plane_states;
        struct drm_crtc **crtcs;
@@ -1053,6 +1099,16 @@ struct drm_mode_config {
        struct drm_property *tile_property;
        struct drm_property *plane_type_property;
        struct drm_property *rotation_property;
+       struct drm_property *prop_src_x;
+       struct drm_property *prop_src_y;
+       struct drm_property *prop_src_w;
+       struct drm_property *prop_src_h;
+       struct drm_property *prop_crtc_x;
+       struct drm_property *prop_crtc_y;
+       struct drm_property *prop_crtc_w;
+       struct drm_property *prop_crtc_h;
+       struct drm_property *prop_fb_id;
+       struct drm_property *prop_crtc_id;
 
        /* DVI-I properties */
        struct drm_property *dvi_i_subconnector_property;
@@ -1290,6 +1346,10 @@ extern int drm_mode_create_scaling_mode_property(struct drm_device *dev);
 extern int drm_mode_create_aspect_ratio_property(struct drm_device *dev);
 extern int drm_mode_create_dirty_info_property(struct drm_device *dev);
 extern int drm_mode_create_suggested_offset_properties(struct drm_device *dev);
+extern bool drm_property_change_valid_get(struct drm_property *property,
+                                        uint64_t value, struct drm_mode_object **ref);
+extern void drm_property_change_valid_put(struct drm_property *property,
+               struct drm_mode_object *ref);
 
 extern int drm_mode_connector_attach_encoder(struct drm_connector *connector,
                                             struct drm_encoder *encoder);
@@ -1381,6 +1441,8 @@ extern int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
 extern int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
                                       struct drm_property *property,
                                       uint64_t value);
+extern int drm_mode_atomic_ioctl(struct drm_device *dev,
+                                void *data, struct drm_file *file_priv);
 
 extern void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth,
                                 int *bpp);
index b0b85561364161e7ce5c8196a6a8b03940db4b9d..01b2d6d0e35529d534ca5d6bd8c3efcbcbd2d838 100644 (file)
@@ -654,6 +654,13 @@ struct drm_get_cap {
  */
 #define DRM_CLIENT_CAP_UNIVERSAL_PLANES  2
 
+/**
+ * DRM_CLIENT_CAP_ATOMIC
+ *
+ * If set to 1, the DRM core will expose atomic properties to userspace
+ */
+#define DRM_CLIENT_CAP_ATOMIC  3
+
 /** DRM_IOCTL_SET_CLIENT_CAP ioctl argument type */
 struct drm_set_client_cap {
        __u64 capability;
@@ -777,6 +784,7 @@ struct drm_prime_handle {
 #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)
 #define DRM_IOCTL_MODE_CURSOR2         DRM_IOWR(0xBB, struct drm_mode_cursor2)
+#define DRM_IOCTL_MODE_ATOMIC          DRM_IOWR(0xBC, struct drm_mode_atomic)
 
 /**
  * Device specific ioctls should only be in their respective headers
index aae71cb32123ee9d91439970099a1f8a4670d294..ca788e01dab20795ea848b0ab9c70ef3d9487743 100644 (file)
@@ -272,6 +272,13 @@ struct drm_mode_get_connector {
 #define DRM_MODE_PROP_OBJECT           DRM_MODE_PROP_TYPE(1)
 #define DRM_MODE_PROP_SIGNED_RANGE     DRM_MODE_PROP_TYPE(2)
 
+/* the PROP_ATOMIC flag is used to hide properties from userspace that
+ * is not aware of atomic properties.  This is mostly to work around
+ * older userspace (DDX drivers) that read/write each prop they find,
+ * witout being aware that this could be triggering a lengthy modeset.
+ */
+#define DRM_MODE_PROP_ATOMIC        0x80000000
+
 struct drm_mode_property_enum {
        __u64 value;
        char name[DRM_PROP_NAME_LEN];
@@ -519,4 +526,27 @@ struct drm_mode_destroy_dumb {
        uint32_t handle;
 };
 
+/* page-flip flags are valid, plus: */
+#define DRM_MODE_ATOMIC_TEST_ONLY 0x0100
+#define DRM_MODE_ATOMIC_NONBLOCK  0x0200
+#define DRM_MODE_ATOMIC_ALLOW_MODESET 0x0400
+
+#define DRM_MODE_ATOMIC_FLAGS (\
+               DRM_MODE_PAGE_FLIP_EVENT |\
+               DRM_MODE_PAGE_FLIP_ASYNC |\
+               DRM_MODE_ATOMIC_TEST_ONLY |\
+               DRM_MODE_ATOMIC_NONBLOCK |\
+               DRM_MODE_ATOMIC_ALLOW_MODESET)
+
+struct drm_mode_atomic {
+       __u32 flags;
+       __u32 count_objs;
+       __u64 objs_ptr;
+       __u64 count_props_ptr;
+       __u64 props_ptr;
+       __u64 prop_values_ptr;
+       __u64 reserved;
+       __u64 user_data;
+};
+
 #endif