]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/gpu/drm/drm_atomic.c
Merge branches 'fixes' and 'ioremap' into for-linus
[karo-tx-linux.git] / drivers / gpu / drm / drm_atomic.c
index c6277a4a1f2f235e43fcfd569944d46dc7133119..f6f2fb58eb37f583f6568964f9ad8aab8be75968 100644 (file)
 #include <drm/drm_atomic.h>
 #include <drm/drm_plane_helper.h>
 
-static void kfree_state(struct drm_atomic_state *state)
+/**
+ * drm_atomic_state_default_release -
+ * release memory initialized by drm_atomic_state_init
+ * @state: atomic state
+ *
+ * Free all the memory allocated by drm_atomic_state_init.
+ * This is useful for drivers that subclass the atomic state.
+ */
+void drm_atomic_state_default_release(struct drm_atomic_state *state)
 {
        kfree(state->connectors);
        kfree(state->connector_states);
@@ -38,24 +46,20 @@ static void kfree_state(struct drm_atomic_state *state)
        kfree(state->crtc_states);
        kfree(state->planes);
        kfree(state->plane_states);
-       kfree(state);
 }
+EXPORT_SYMBOL(drm_atomic_state_default_release);
 
 /**
- * drm_atomic_state_alloc - allocate atomic state
+ * drm_atomic_state_init - init new atomic state
  * @dev: DRM device
+ * @state: atomic state
  *
- * This allocates an empty atomic state to track updates.
+ * Default implementation for filling in a new atomic state.
+ * This is useful for drivers that subclass the atomic state.
  */
-struct drm_atomic_state *
-drm_atomic_state_alloc(struct drm_device *dev)
+int
+drm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state)
 {
-       struct drm_atomic_state *state;
-
-       state = kzalloc(sizeof(*state), GFP_KERNEL);
-       if (!state)
-               return NULL;
-
        /* TODO legacy paths should maybe do a better job about
         * setting this appropriately?
         */
@@ -92,31 +96,50 @@ drm_atomic_state_alloc(struct drm_device *dev)
 
        state->dev = dev;
 
-       DRM_DEBUG_ATOMIC("Allocate atomic state %p\n", state);
+       DRM_DEBUG_ATOMIC("Allocated atomic state %p\n", state);
 
-       return state;
+       return 0;
 fail:
-       kfree_state(state);
+       drm_atomic_state_default_release(state);
+       return -ENOMEM;
+}
+EXPORT_SYMBOL(drm_atomic_state_init);
 
-       return NULL;
+/**
+ * drm_atomic_state_alloc - allocate atomic state
+ * @dev: DRM device
+ *
+ * This allocates an empty atomic state to track updates.
+ */
+struct drm_atomic_state *
+drm_atomic_state_alloc(struct drm_device *dev)
+{
+       struct drm_mode_config *config = &dev->mode_config;
+       struct drm_atomic_state *state;
+
+       if (!config->funcs->atomic_state_alloc) {
+               state = kzalloc(sizeof(*state), GFP_KERNEL);
+               if (!state)
+                       return NULL;
+               if (drm_atomic_state_init(dev, state) < 0) {
+                       kfree(state);
+                       return NULL;
+               }
+               return state;
+       }
+
+       return config->funcs->atomic_state_alloc(dev);
 }
 EXPORT_SYMBOL(drm_atomic_state_alloc);
 
 /**
- * drm_atomic_state_clear - clear state object
+ * drm_atomic_state_default_clear - clear base atomic state
  * @state: atomic state
  *
- * When the w/w mutex algorithm detects a deadlock we need to back off and drop
- * all locks. So someone else could sneak in and change the current modeset
- * configuration. Which means that all the state assembled in @state is no
- * longer an atomic update to the current state, but to some arbitrary earlier
- * state. Which could break assumptions the driver's ->atomic_check likely
- * relies on.
- *
- * Hence we must clear all cached state and completely start over, using this
- * function.
+ * Default implementation for clearing atomic state.
+ * This is useful for drivers that subclass the atomic state.
  */
-void drm_atomic_state_clear(struct drm_atomic_state *state)
+void drm_atomic_state_default_clear(struct drm_atomic_state *state)
 {
        struct drm_device *dev = state->dev;
        struct drm_mode_config *config = &dev->mode_config;
@@ -162,6 +185,32 @@ void drm_atomic_state_clear(struct drm_atomic_state *state)
                state->plane_states[i] = NULL;
        }
 }
+EXPORT_SYMBOL(drm_atomic_state_default_clear);
+
+/**
+ * drm_atomic_state_clear - clear state object
+ * @state: atomic state
+ *
+ * When the w/w mutex algorithm detects a deadlock we need to back off and drop
+ * all locks. So someone else could sneak in and change the current modeset
+ * configuration. Which means that all the state assembled in @state is no
+ * longer an atomic update to the current state, but to some arbitrary earlier
+ * state. Which could break assumptions the driver's ->atomic_check likely
+ * relies on.
+ *
+ * Hence we must clear all cached state and completely start over, using this
+ * function.
+ */
+void drm_atomic_state_clear(struct drm_atomic_state *state)
+{
+       struct drm_device *dev = state->dev;
+       struct drm_mode_config *config = &dev->mode_config;
+
+       if (config->funcs->atomic_state_clear)
+               config->funcs->atomic_state_clear(state);
+       else
+               drm_atomic_state_default_clear(state);
+}
 EXPORT_SYMBOL(drm_atomic_state_clear);
 
 /**
@@ -173,14 +222,25 @@ EXPORT_SYMBOL(drm_atomic_state_clear);
  */
 void drm_atomic_state_free(struct drm_atomic_state *state)
 {
+       struct drm_device *dev;
+       struct drm_mode_config *config;
+
        if (!state)
                return;
 
+       dev = state->dev;
+       config = &dev->mode_config;
+
        drm_atomic_state_clear(state);
 
        DRM_DEBUG_ATOMIC("Freeing atomic state %p\n", state);
 
-       kfree_state(state);
+       if (config->funcs->atomic_state_free) {
+               config->funcs->atomic_state_free(state);
+       } else {
+               drm_atomic_state_default_release(state);
+               kfree(state);
+       }
 }
 EXPORT_SYMBOL(drm_atomic_state_free);
 
@@ -229,6 +289,100 @@ drm_atomic_get_crtc_state(struct drm_atomic_state *state,
 }
 EXPORT_SYMBOL(drm_atomic_get_crtc_state);
 
+/**
+ * drm_atomic_set_mode_for_crtc - set mode for CRTC
+ * @state: the CRTC whose incoming state to update
+ * @mode: kernel-internal mode to use for the CRTC, or NULL to disable
+ *
+ * Set a mode (originating from the kernel) on the desired CRTC state. Does
+ * not change any other state properties, including enable, active, or
+ * mode_changed.
+ *
+ * RETURNS:
+ * Zero on success, error code on failure. Cannot return -EDEADLK.
+ */
+int drm_atomic_set_mode_for_crtc(struct drm_crtc_state *state,
+                                struct drm_display_mode *mode)
+{
+       struct drm_mode_modeinfo umode;
+
+       /* Early return for no change. */
+       if (mode && memcmp(&state->mode, mode, sizeof(*mode)) == 0)
+               return 0;
+
+       if (state->mode_blob)
+               drm_property_unreference_blob(state->mode_blob);
+       state->mode_blob = NULL;
+
+       if (mode) {
+               drm_mode_convert_to_umode(&umode, mode);
+               state->mode_blob =
+                       drm_property_create_blob(state->crtc->dev,
+                                                sizeof(umode),
+                                                &umode);
+               if (IS_ERR(state->mode_blob))
+                       return PTR_ERR(state->mode_blob);
+
+               drm_mode_copy(&state->mode, mode);
+               state->enable = true;
+               DRM_DEBUG_ATOMIC("Set [MODE:%s] for CRTC state %p\n",
+                                mode->name, state);
+       } else {
+               memset(&state->mode, 0, sizeof(state->mode));
+               state->enable = false;
+               DRM_DEBUG_ATOMIC("Set [NOMODE] for CRTC state %p\n",
+                                state);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_atomic_set_mode_for_crtc);
+
+/**
+ * drm_atomic_set_mode_prop_for_crtc - set mode for CRTC
+ * @state: the CRTC whose incoming state to update
+ * @blob: pointer to blob property to use for mode
+ *
+ * Set a mode (originating from a blob property) on the desired CRTC state.
+ * This function will take a reference on the blob property for the CRTC state,
+ * and release the reference held on the state's existing mode property, if any
+ * was set.
+ *
+ * RETURNS:
+ * Zero on success, error code on failure. Cannot return -EDEADLK.
+ */
+int drm_atomic_set_mode_prop_for_crtc(struct drm_crtc_state *state,
+                                      struct drm_property_blob *blob)
+{
+       if (blob == state->mode_blob)
+               return 0;
+
+       if (state->mode_blob)
+               drm_property_unreference_blob(state->mode_blob);
+       state->mode_blob = NULL;
+
+       if (blob) {
+               if (blob->length != sizeof(struct drm_mode_modeinfo) ||
+                   drm_mode_convert_umode(&state->mode,
+                                          (const struct drm_mode_modeinfo *)
+                                           blob->data))
+                       return -EINVAL;
+
+               state->mode_blob = drm_property_reference_blob(blob);
+               state->enable = true;
+               DRM_DEBUG_ATOMIC("Set [MODE:%s] for CRTC state %p\n",
+                                state->mode.name, state);
+       } else {
+               memset(&state->mode, 0, sizeof(state->mode));
+               state->enable = false;
+               DRM_DEBUG_ATOMIC("Set [NOMODE] for CRTC state %p\n",
+                                state);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_atomic_set_mode_prop_for_crtc);
+
 /**
  * drm_atomic_crtc_set_property - set property on CRTC
  * @crtc: the drm CRTC to set a property on
@@ -251,10 +405,18 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
 {
        struct drm_device *dev = crtc->dev;
        struct drm_mode_config *config = &dev->mode_config;
+       int ret;
 
-       /* FIXME: Mode prop is missing, which also controls ->enable. */
        if (property == config->prop_active)
                state->active = val;
+       else if (property == config->prop_mode_id) {
+               struct drm_property_blob *mode =
+                       drm_property_lookup_blob(dev, val);
+               ret = drm_atomic_set_mode_prop_for_crtc(state, mode);
+               if (mode)
+                       drm_property_unreference_blob(mode);
+               return ret;
+       }
        else if (crtc->funcs->atomic_set_property)
                return crtc->funcs->atomic_set_property(crtc, state, property, val);
        else
@@ -279,6 +441,8 @@ int drm_atomic_crtc_get_property(struct drm_crtc *crtc,
 
        if (property == config->prop_active)
                *val = state->active;
+       else if (property == config->prop_mode_id)
+               *val = (state->mode_blob) ? state->mode_blob->base.id : 0;
        else if (crtc->funcs->atomic_get_property)
                return crtc->funcs->atomic_get_property(crtc, state, property, val);
        else
@@ -314,6 +478,23 @@ static int drm_atomic_crtc_check(struct drm_crtc *crtc,
                return -EINVAL;
        }
 
+       /* The state->enable vs. state->mode_blob checks can be WARN_ON,
+        * as this is a kernel-internal detail that userspace should never
+        * be able to trigger. */
+       if (drm_core_check_feature(crtc->dev, DRIVER_ATOMIC) &&
+           WARN_ON(state->enable && !state->mode_blob)) {
+               DRM_DEBUG_ATOMIC("[CRTC:%d] enabled without mode blob\n",
+                                crtc->base.id);
+               return -EINVAL;
+       }
+
+       if (drm_core_check_feature(crtc->dev, DRIVER_ATOMIC) &&
+           WARN_ON(!state->enable && state->mode_blob)) {
+               DRM_DEBUG_ATOMIC("[CRTC:%d] disabled with mode blob\n",
+                                crtc->base.id);
+               return -EINVAL;
+       }
+
        return 0;
 }
 
@@ -895,6 +1076,45 @@ drm_atomic_add_affected_connectors(struct drm_atomic_state *state,
 }
 EXPORT_SYMBOL(drm_atomic_add_affected_connectors);
 
+/**
+ * drm_atomic_add_affected_planes - add planes for crtc
+ * @state: atomic state
+ * @crtc: DRM crtc
+ *
+ * This function walks the current configuration and adds all planes
+ * currently used by @crtc to the atomic configuration @state. This is useful
+ * when an atomic commit also needs to check all currently enabled plane on
+ * @crtc, e.g. when changing the mode. It's also useful when re-enabling a CRTC
+ * to avoid special code to force-enable all planes.
+ *
+ * Since acquiring a plane state will always also acquire the w/w mutex of the
+ * current CRTC for that plane (if there is any) adding all the plane states for
+ * a CRTC will not reduce parallism of atomic updates.
+ *
+ * Returns:
+ * 0 on success or can fail with -EDEADLK or -ENOMEM. When the error is EDEADLK
+ * then the w/w mutex code has detected a deadlock and the entire atomic
+ * sequence must be restarted. All other errors are fatal.
+ */
+int
+drm_atomic_add_affected_planes(struct drm_atomic_state *state,
+                              struct drm_crtc *crtc)
+{
+       struct drm_plane *plane;
+
+       WARN_ON(!drm_atomic_get_existing_crtc_state(state, crtc));
+
+       drm_for_each_plane_mask(plane, state->dev, crtc->state->plane_mask) {
+               struct drm_plane_state *plane_state =
+                       drm_atomic_get_plane_state(state, plane);
+
+               if (IS_ERR(plane_state))
+                       return PTR_ERR(plane_state);
+       }
+       return 0;
+}
+EXPORT_SYMBOL(drm_atomic_add_affected_planes);
+
 /**
  * drm_atomic_connectors_for_crtc - count number of connected outputs
  * @state: atomic state
@@ -996,8 +1216,7 @@ int drm_atomic_check_only(struct drm_atomic_state *state)
 
        if (!state->allow_modeset) {
                for_each_crtc_in_state(state, crtc, crtc_state, i) {
-                       if (crtc_state->mode_changed ||
-                           crtc_state->active_changed) {
+                       if (drm_atomic_crtc_needs_modeset(crtc_state)) {
                                DRM_DEBUG_ATOMIC("[CRTC:%d] requires full modeset\n",
                                                 crtc->base.id);
                                return -EINVAL;