drm/atomic: add commit_planes_on_crtc helper
[deliverable/linux.git] / drivers / gpu / drm / drm_atomic_helper.c
index 7e3a52b97c7d4154b82870f2c2d2ebb04298a5e0..424a98bfa6869a283090dc641948452ea485c3d9 100644 (file)
@@ -116,9 +116,9 @@ steal_encoder(struct drm_atomic_state *state,
         */
        WARN_ON(!drm_modeset_is_locked(&config->connection_mutex));
 
-       DRM_DEBUG_KMS("[ENCODER:%d:%s] in use on [CRTC:%d], stealing it\n",
-                     encoder->base.id, encoder->name,
-                     encoder_crtc->base.id);
+       DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] in use on [CRTC:%d], stealing it\n",
+                        encoder->base.id, encoder->name,
+                        encoder_crtc->base.id);
 
        crtc_state = drm_atomic_get_crtc_state(state, encoder_crtc);
        if (IS_ERR(crtc_state))
@@ -130,9 +130,9 @@ steal_encoder(struct drm_atomic_state *state,
                if (connector->state->best_encoder != encoder)
                        continue;
 
-               DRM_DEBUG_KMS("Stealing encoder from [CONNECTOR:%d:%s]\n",
-                             connector->base.id,
-                             connector->name);
+               DRM_DEBUG_ATOMIC("Stealing encoder from [CONNECTOR:%d:%s]\n",
+                                connector->base.id,
+                                connector->name);
 
                connector_state = drm_atomic_get_connector_state(state,
                                                                 connector);
@@ -151,7 +151,7 @@ steal_encoder(struct drm_atomic_state *state,
 static int
 update_connector_routing(struct drm_atomic_state *state, int conn_idx)
 {
-       struct drm_connector_helper_funcs *funcs;
+       const struct drm_connector_helper_funcs *funcs;
        struct drm_encoder *new_encoder;
        struct drm_crtc *encoder_crtc;
        struct drm_connector *connector;
@@ -165,9 +165,9 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx)
        if (!connector)
                return 0;
 
-       DRM_DEBUG_KMS("Updating routing for [CONNECTOR:%d:%s]\n",
-                       connector->base.id,
-                       connector->name);
+       DRM_DEBUG_ATOMIC("Updating routing for [CONNECTOR:%d:%s]\n",
+                        connector->base.id,
+                        connector->name);
 
        if (connector->state->crtc != connector_state->crtc) {
                if (connector->state->crtc) {
@@ -186,7 +186,7 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx)
        }
 
        if (!connector_state->crtc) {
-               DRM_DEBUG_KMS("Disabling [CONNECTOR:%d:%s]\n",
+               DRM_DEBUG_ATOMIC("Disabling [CONNECTOR:%d:%s]\n",
                                connector->base.id,
                                connector->name);
 
@@ -199,19 +199,19 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx)
        new_encoder = funcs->best_encoder(connector);
 
        if (!new_encoder) {
-               DRM_DEBUG_KMS("No suitable encoder found for [CONNECTOR:%d:%s]\n",
-                             connector->base.id,
-                             connector->name);
+               DRM_DEBUG_ATOMIC("No suitable encoder found for [CONNECTOR:%d:%s]\n",
+                                connector->base.id,
+                                connector->name);
                return -EINVAL;
        }
 
        if (new_encoder == connector_state->best_encoder) {
-               DRM_DEBUG_KMS("[CONNECTOR:%d:%s] keeps [ENCODER:%d:%s], now on [CRTC:%d]\n",
-                             connector->base.id,
-                             connector->name,
-                             new_encoder->base.id,
-                             new_encoder->name,
-                             connector_state->crtc->base.id);
+               DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] keeps [ENCODER:%d:%s], now on [CRTC:%d]\n",
+                                connector->base.id,
+                                connector->name,
+                                new_encoder->base.id,
+                                new_encoder->name,
+                                connector_state->crtc->base.id);
 
                return 0;
        }
@@ -222,9 +222,9 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx)
        if (encoder_crtc) {
                ret = steal_encoder(state, new_encoder, encoder_crtc);
                if (ret) {
-                       DRM_DEBUG_KMS("Encoder stealing failed for [CONNECTOR:%d:%s]\n",
-                                     connector->base.id,
-                                     connector->name);
+                       DRM_DEBUG_ATOMIC("Encoder stealing failed for [CONNECTOR:%d:%s]\n",
+                                        connector->base.id,
+                                        connector->name);
                        return ret;
                }
        }
@@ -235,12 +235,12 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx)
        crtc_state = state->crtc_states[idx];
        crtc_state->mode_changed = true;
 
-       DRM_DEBUG_KMS("[CONNECTOR:%d:%s] using [ENCODER:%d:%s] on [CRTC:%d]\n",
-                     connector->base.id,
-                     connector->name,
-                     new_encoder->base.id,
-                     new_encoder->name,
-                     connector_state->crtc->base.id);
+       DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] using [ENCODER:%d:%s] on [CRTC:%d]\n",
+                        connector->base.id,
+                        connector->name,
+                        new_encoder->base.id,
+                        new_encoder->name,
+                        connector_state->crtc->base.id);
 
        return 0;
 }
@@ -248,30 +248,24 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx)
 static int
 mode_fixup(struct drm_atomic_state *state)
 {
-       int ncrtcs = state->dev->mode_config.num_crtc;
+       struct drm_crtc *crtc;
        struct drm_crtc_state *crtc_state;
+       struct drm_connector *connector;
        struct drm_connector_state *conn_state;
        int i;
        bool ret;
 
-       for (i = 0; i < ncrtcs; i++) {
-               crtc_state = state->crtc_states[i];
-
-               if (!crtc_state || !crtc_state->mode_changed)
+       for_each_crtc_in_state(state, crtc, crtc_state, i) {
+               if (!crtc_state->mode_changed)
                        continue;
 
                drm_mode_copy(&crtc_state->adjusted_mode, &crtc_state->mode);
        }
 
-       for (i = 0; i < state->num_connector; i++) {
-               struct drm_encoder_helper_funcs *funcs;
+       for_each_connector_in_state(state, connector, conn_state, i) {
+               const struct drm_encoder_helper_funcs *funcs;
                struct drm_encoder *encoder;
 
-               conn_state = state->connector_states[i];
-
-               if (!conn_state)
-                       continue;
-
                WARN_ON(!!conn_state->best_encoder != !!conn_state->crtc);
 
                if (!conn_state->crtc || !conn_state->best_encoder)
@@ -286,13 +280,15 @@ mode_fixup(struct drm_atomic_state *state)
                 */
                encoder = conn_state->best_encoder;
                funcs = encoder->helper_private;
+               if (!funcs)
+                       continue;
 
                if (encoder->bridge && encoder->bridge->funcs->mode_fixup) {
                        ret = encoder->bridge->funcs->mode_fixup(
                                        encoder->bridge, &crtc_state->mode,
                                        &crtc_state->adjusted_mode);
                        if (!ret) {
-                               DRM_DEBUG_KMS("Bridge fixup failed\n");
+                               DRM_DEBUG_ATOMIC("Bridge fixup failed\n");
                                return -EINVAL;
                        }
                }
@@ -301,37 +297,36 @@ mode_fixup(struct drm_atomic_state *state)
                        ret = funcs->atomic_check(encoder, crtc_state,
                                                  conn_state);
                        if (ret) {
-                               DRM_DEBUG_KMS("[ENCODER:%d:%s] check failed\n",
-                                             encoder->base.id, encoder->name);
+                               DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] check failed\n",
+                                                encoder->base.id, encoder->name);
                                return ret;
                        }
                } else {
                        ret = funcs->mode_fixup(encoder, &crtc_state->mode,
                                                &crtc_state->adjusted_mode);
                        if (!ret) {
-                               DRM_DEBUG_KMS("[ENCODER:%d:%s] fixup failed\n",
-                                             encoder->base.id, encoder->name);
+                               DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] fixup failed\n",
+                                                encoder->base.id, encoder->name);
                                return -EINVAL;
                        }
                }
        }
 
-       for (i = 0; i < ncrtcs; i++) {
-               struct drm_crtc_helper_funcs *funcs;
-               struct drm_crtc *crtc;
+       for_each_crtc_in_state(state, crtc, crtc_state, i) {
+               const struct drm_crtc_helper_funcs *funcs;
 
-               crtc_state = state->crtc_states[i];
-               crtc = state->crtcs[i];
-
-               if (!crtc_state || !crtc_state->mode_changed)
+               if (!crtc_state->mode_changed)
                        continue;
 
                funcs = crtc->helper_private;
+               if (!funcs->mode_fixup)
+                       continue;
+
                ret = funcs->mode_fixup(crtc, &crtc_state->mode,
                                        &crtc_state->adjusted_mode);
                if (!ret) {
-                       DRM_DEBUG_KMS("[CRTC:%d] fixup failed\n",
-                                     crtc->base.id);
+                       DRM_DEBUG_ATOMIC("[CRTC:%d] fixup failed\n",
+                                        crtc->base.id);
                        return -EINVAL;
                }
        }
@@ -346,7 +341,7 @@ needs_modeset(struct drm_crtc_state *state)
 }
 
 /**
- * drm_atomic_helper_check - validate state object for modeset changes
+ * drm_atomic_helper_check_modeset - validate state object for modeset changes
  * @dev: DRM device
  * @state: the driver state object
  *
@@ -371,32 +366,27 @@ int
 drm_atomic_helper_check_modeset(struct drm_device *dev,
                                struct drm_atomic_state *state)
 {
-       int ncrtcs = dev->mode_config.num_crtc;
        struct drm_crtc *crtc;
        struct drm_crtc_state *crtc_state;
+       struct drm_connector *connector;
+       struct drm_connector_state *connector_state;
        int i, ret;
 
-       for (i = 0; i < ncrtcs; i++) {
-               crtc = state->crtcs[i];
-               crtc_state = state->crtc_states[i];
-
-               if (!crtc)
-                       continue;
-
+       for_each_crtc_in_state(state, crtc, crtc_state, i) {
                if (!drm_mode_equal(&crtc->state->mode, &crtc_state->mode)) {
-                       DRM_DEBUG_KMS("[CRTC:%d] mode changed\n",
-                                     crtc->base.id);
+                       DRM_DEBUG_ATOMIC("[CRTC:%d] mode changed\n",
+                                        crtc->base.id);
                        crtc_state->mode_changed = true;
                }
 
                if (crtc->state->enable != crtc_state->enable) {
-                       DRM_DEBUG_KMS("[CRTC:%d] enable changed\n",
-                                     crtc->base.id);
+                       DRM_DEBUG_ATOMIC("[CRTC:%d] enable changed\n",
+                                        crtc->base.id);
                        crtc_state->mode_changed = true;
                }
        }
 
-       for (i = 0; i < state->num_connector; i++) {
+       for_each_connector_in_state(state, connector, connector_state, i) {
                /*
                 * This only sets crtc->mode_changed for routing changes,
                 * drivers must set crtc->mode_changed themselves when connector
@@ -413,32 +403,26 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
         * configuration. This must be done before calling mode_fixup in case a
         * crtc only changed its mode but has the same set of connectors.
         */
-       for (i = 0; i < ncrtcs; i++) {
+       for_each_crtc_in_state(state, crtc, crtc_state, i) {
                int num_connectors;
 
-               crtc = state->crtcs[i];
-               crtc_state = state->crtc_states[i];
-
-               if (!crtc)
-                       continue;
-
                /*
                 * We must set ->active_changed after walking connectors for
                 * otherwise an update that only changes active would result in
                 * a full modeset because update_connector_routing force that.
                 */
                if (crtc->state->active != crtc_state->active) {
-                       DRM_DEBUG_KMS("[CRTC:%d] active changed\n",
-                                     crtc->base.id);
+                       DRM_DEBUG_ATOMIC("[CRTC:%d] active changed\n",
+                                        crtc->base.id);
                        crtc_state->active_changed = true;
                }
 
                if (!needs_modeset(crtc_state))
                        continue;
 
-               DRM_DEBUG_KMS("[CRTC:%d] needs all connectors, enable: %c, active: %c\n",
-                             crtc->base.id,
-                             crtc_state->enable ? 'y' : 'n',
+               DRM_DEBUG_ATOMIC("[CRTC:%d] needs all connectors, enable: %c, active: %c\n",
+                                crtc->base.id,
+                                crtc_state->enable ? 'y' : 'n',
                              crtc_state->active ? 'y' : 'n');
 
                ret = drm_atomic_add_affected_connectors(state, crtc);
@@ -449,8 +433,8 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
                                                                crtc);
 
                if (crtc_state->enable != !!num_connectors) {
-                       DRM_DEBUG_KMS("[CRTC:%d] enabled/connectors mismatch\n",
-                                     crtc->base.id);
+                       DRM_DEBUG_ATOMIC("[CRTC:%d] enabled/connectors mismatch\n",
+                                        crtc->base.id);
 
                        return -EINVAL;
                }
@@ -461,7 +445,7 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
 EXPORT_SYMBOL(drm_atomic_helper_check_modeset);
 
 /**
- * drm_atomic_helper_check - validate state object for modeset changes
+ * drm_atomic_helper_check_planes - validate state object for planes changes
  * @dev: DRM device
  * @state: the driver state object
  *
@@ -476,17 +460,14 @@ 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;
+       struct drm_crtc *crtc;
+       struct drm_crtc_state *crtc_state;
+       struct drm_plane *plane;
+       struct drm_plane_state *plane_state;
        int i, ret = 0;
 
-       for (i = 0; i < nplanes; i++) {
-               struct drm_plane_helper_funcs *funcs;
-               struct drm_plane *plane = state->planes[i];
-               struct drm_plane_state *plane_state = state->plane_states[i];
-
-               if (!plane)
-                       continue;
+       for_each_plane_in_state(state, plane, plane_state, i) {
+               const struct drm_plane_helper_funcs *funcs;
 
                funcs = plane->helper_private;
 
@@ -497,18 +478,14 @@ drm_atomic_helper_check_planes(struct drm_device *dev,
 
                ret = funcs->atomic_check(plane, plane_state);
                if (ret) {
-                       DRM_DEBUG_KMS("[PLANE:%d] atomic driver check failed\n",
-                                     plane->base.id);
+                       DRM_DEBUG_ATOMIC("[PLANE:%d] atomic driver check failed\n",
+                                        plane->base.id);
                        return ret;
                }
        }
 
-       for (i = 0; i < ncrtcs; i++) {
-               struct drm_crtc_helper_funcs *funcs;
-               struct drm_crtc *crtc = state->crtcs[i];
-
-               if (!crtc)
-                       continue;
+       for_each_crtc_in_state(state, crtc, crtc_state, i) {
+               const struct drm_crtc_helper_funcs *funcs;
 
                funcs = crtc->helper_private;
 
@@ -517,8 +494,8 @@ drm_atomic_helper_check_planes(struct drm_device *dev,
 
                ret = funcs->atomic_check(crtc, state->crtc_states[i]);
                if (ret) {
-                       DRM_DEBUG_KMS("[CRTC:%d] atomic driver check failed\n",
-                                     crtc->base.id);
+                       DRM_DEBUG_ATOMIC("[CRTC:%d] atomic driver check failed\n",
+                                        crtc->base.id);
                        return ret;
                }
        }
@@ -567,27 +544,26 @@ EXPORT_SYMBOL(drm_atomic_helper_check);
 static void
 disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
 {
-       int ncrtcs = old_state->dev->mode_config.num_crtc;
+       struct drm_connector *connector;
+       struct drm_connector_state *old_conn_state;
+       struct drm_crtc *crtc;
+       struct drm_crtc_state *old_crtc_state;
        int i;
 
-       for (i = 0; i < old_state->num_connector; i++) {
-               struct drm_connector_state *old_conn_state;
-               struct drm_connector *connector;
-               struct drm_encoder_helper_funcs *funcs;
+       for_each_connector_in_state(old_state, connector, old_conn_state, i) {
+               const struct drm_encoder_helper_funcs *funcs;
                struct drm_encoder *encoder;
                struct drm_crtc_state *old_crtc_state;
 
-               old_conn_state = old_state->connector_states[i];
-               connector = old_state->connectors[i];
-
                /* Shut down everything that's in the changeset and currently
                 * still on. So need to check the old, saved state. */
-               if (!old_conn_state || !old_conn_state->crtc)
+               if (!old_conn_state->crtc)
                        continue;
 
                old_crtc_state = old_state->crtc_states[drm_crtc_index(old_conn_state->crtc)];
 
-               if (!old_crtc_state->active)
+               if (!old_crtc_state->active ||
+                   !needs_modeset(old_conn_state->crtc->state))
                        continue;
 
                encoder = old_conn_state->best_encoder;
@@ -600,12 +576,12 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
 
                funcs = encoder->helper_private;
 
-               DRM_DEBUG_KMS("disabling [ENCODER:%d:%s]\n",
-                             encoder->base.id, encoder->name);
+               DRM_DEBUG_ATOMIC("disabling [ENCODER:%d:%s]\n",
+                                encoder->base.id, encoder->name);
 
                /*
                 * Each encoder has at most one connector (since we always steal
-                * it away), so we won't call call disable hooks twice.
+                * it away), so we won't call disable hooks twice.
                 */
                if (encoder->bridge)
                        encoder->bridge->funcs->disable(encoder->bridge);
@@ -622,16 +598,11 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
                        encoder->bridge->funcs->post_disable(encoder->bridge);
        }
 
-       for (i = 0; i < ncrtcs; i++) {
-               struct drm_crtc_helper_funcs *funcs;
-               struct drm_crtc *crtc;
-               struct drm_crtc_state *old_crtc_state;
-
-               crtc = old_state->crtcs[i];
-               old_crtc_state = old_state->crtc_states[i];
+       for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
+               const struct drm_crtc_helper_funcs *funcs;
 
                /* Shut down everything that needs a full modeset. */
-               if (!crtc || !needs_modeset(crtc->state))
+               if (!needs_modeset(crtc->state))
                        continue;
 
                if (!old_crtc_state->active)
@@ -639,8 +610,8 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
 
                funcs = crtc->helper_private;
 
-               DRM_DEBUG_KMS("disabling [CRTC:%d]\n",
-                             crtc->base.id);
+               DRM_DEBUG_ATOMIC("disabling [CRTC:%d]\n",
+                                crtc->base.id);
 
 
                /* Right function depends upon target state. */
@@ -653,19 +624,32 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
        }
 }
 
-static void
-set_routing_links(struct drm_device *dev, struct drm_atomic_state *old_state)
+/**
+ * drm_atomic_helper_update_legacy_modeset_state - update legacy modeset state
+ * @dev: DRM device
+ * @old_state: atomic state object with old state structures
+ *
+ * This function updates all the various legacy modeset state pointers in
+ * connectors, encoders and crtcs. It also updates the timestamping constants
+ * used for precise vblank timestamps by calling
+ * drm_calc_timestamping_constants().
+ *
+ * Drivers can use this for building their own atomic commit if they don't have
+ * a pure helper-based modeset implementation.
+ */
+void
+drm_atomic_helper_update_legacy_modeset_state(struct drm_device *dev,
+                                             struct drm_atomic_state *old_state)
 {
-       int ncrtcs = old_state->dev->mode_config.num_crtc;
+       struct drm_connector *connector;
+       struct drm_connector_state *old_conn_state;
+       struct drm_crtc *crtc;
+       struct drm_crtc_state *old_crtc_state;
        int i;
 
        /* clear out existing links */
-       for (i = 0; i < old_state->num_connector; i++) {
-               struct drm_connector *connector;
-
-               connector = old_state->connectors[i];
-
-               if (!connector || !connector->encoder)
+       for_each_connector_in_state(old_state, connector, old_conn_state, i) {
+               if (!connector->encoder)
                        continue;
 
                WARN_ON(!connector->encoder->crtc);
@@ -675,12 +659,8 @@ set_routing_links(struct drm_device *dev, struct drm_atomic_state *old_state)
        }
 
        /* set new links */
-       for (i = 0; i < old_state->num_connector; i++) {
-               struct drm_connector *connector;
-
-               connector = old_state->connectors[i];
-
-               if (!connector || !connector->state->crtc)
+       for_each_connector_in_state(old_state, connector, old_conn_state, i) {
+               if (!connector->state->crtc)
                        continue;
 
                if (WARN_ON(!connector->state->best_encoder))
@@ -691,56 +671,51 @@ set_routing_links(struct drm_device *dev, struct drm_atomic_state *old_state)
        }
 
        /* set legacy state in the crtc structure */
-       for (i = 0; i < ncrtcs; i++) {
-               struct drm_crtc *crtc;
-
-               crtc = old_state->crtcs[i];
-
-               if (!crtc)
-                       continue;
-
+       for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
                crtc->mode = crtc->state->mode;
                crtc->enabled = crtc->state->enable;
                crtc->x = crtc->primary->state->src_x >> 16;
                crtc->y = crtc->primary->state->src_y >> 16;
+
+               if (crtc->state->enable)
+                       drm_calc_timestamping_constants(crtc,
+                                                       &crtc->state->adjusted_mode);
        }
 }
+EXPORT_SYMBOL(drm_atomic_helper_update_legacy_modeset_state);
 
 static void
 crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state)
 {
-       int ncrtcs = old_state->dev->mode_config.num_crtc;
+       struct drm_crtc *crtc;
+       struct drm_crtc_state *old_crtc_state;
+       struct drm_connector *connector;
+       struct drm_connector_state *old_conn_state;
        int i;
 
-       for (i = 0; i < ncrtcs; i++) {
-               struct drm_crtc_helper_funcs *funcs;
-               struct drm_crtc *crtc;
+       for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
+               const struct drm_crtc_helper_funcs *funcs;
 
-               crtc = old_state->crtcs[i];
-
-               if (!crtc || !crtc->state->mode_changed)
+               if (!crtc->state->mode_changed)
                        continue;
 
                funcs = crtc->helper_private;
 
-               if (crtc->state->enable) {
-                       DRM_DEBUG_KMS("modeset on [CRTC:%d]\n",
-                                     crtc->base.id);
+               if (crtc->state->enable && funcs->mode_set_nofb) {
+                       DRM_DEBUG_ATOMIC("modeset on [CRTC:%d]\n",
+                                        crtc->base.id);
 
                        funcs->mode_set_nofb(crtc);
                }
        }
 
-       for (i = 0; i < old_state->num_connector; i++) {
-               struct drm_connector *connector;
+       for_each_connector_in_state(old_state, connector, old_conn_state, i) {
+               const struct drm_encoder_helper_funcs *funcs;
                struct drm_crtc_state *new_crtc_state;
-               struct drm_encoder_helper_funcs *funcs;
                struct drm_encoder *encoder;
                struct drm_display_mode *mode, *adjusted_mode;
 
-               connector = old_state->connectors[i];
-
-               if (!connector || !connector->state->best_encoder)
+               if (!connector->state->best_encoder)
                        continue;
 
                encoder = connector->state->best_encoder;
@@ -752,14 +727,15 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state)
                if (!new_crtc_state->mode_changed)
                        continue;
 
-               DRM_DEBUG_KMS("modeset on [ENCODER:%d:%s]\n",
-                             encoder->base.id, encoder->name);
+               DRM_DEBUG_ATOMIC("modeset on [ENCODER:%d:%s]\n",
+                                encoder->base.id, encoder->name);
 
                /*
                 * Each encoder has at most one connector (since we always steal
-                * it away), so we won't call call mode_set hooks twice.
+                * it away), so we won't call mode_set hooks twice.
                 */
-               funcs->mode_set(encoder, mode, adjusted_mode);
+               if (funcs->mode_set)
+                       funcs->mode_set(encoder, mode, adjusted_mode);
 
                if (encoder->bridge && encoder->bridge->funcs->mode_set)
                        encoder->bridge->funcs->mode_set(encoder->bridge,
@@ -768,46 +744,58 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state)
 }
 
 /**
- * drm_atomic_helper_commit_pre_planes - modeset commit before plane updates
+ * drm_atomic_helper_commit_modeset_disables - modeset commit to disable outputs
  * @dev: DRM device
- * @state: atomic state
+ * @old_state: atomic state object with old state structures
  *
- * This function commits the modeset changes that need to be committed before
- * updating planes. It shuts down all the outputs that need to be shut down and
+ * This function shuts down all the outputs that need to be shut down and
  * prepares them (if required) with the new mode.
+ *
+ * For compatability with legacy crtc helpers this should be called before
+ * drm_atomic_helper_commit_planes(), which is what the default commit function
+ * does. But drivers with different needs can group the modeset commits together
+ * and do the plane commits at the end. This is useful for drivers doing runtime
+ * PM since planes updates then only happen when the CRTC is actually enabled.
  */
-void drm_atomic_helper_commit_pre_planes(struct drm_device *dev,
-                                        struct drm_atomic_state *state)
+void drm_atomic_helper_commit_modeset_disables(struct drm_device *dev,
+                                              struct drm_atomic_state *old_state)
 {
-       disable_outputs(dev, state);
-       set_routing_links(dev, state);
-       crtc_set_mode(dev, state);
+       disable_outputs(dev, old_state);
+
+       drm_atomic_helper_update_legacy_modeset_state(dev, old_state);
+
+       crtc_set_mode(dev, old_state);
 }
-EXPORT_SYMBOL(drm_atomic_helper_commit_pre_planes);
+EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_disables);
 
 /**
- * drm_atomic_helper_commit_post_planes - modeset commit after plane updates
+ * drm_atomic_helper_commit_modeset_enables - modeset commit to enable outputs
  * @dev: DRM device
  * @old_state: atomic state object with old state structures
  *
- * This function commits the modeset changes that need to be committed after
- * updating planes: It enables all the outputs with the new configuration which
- * had to be turned off for the update.
+ * This function enables all the outputs with the new configuration which had to
+ * be turned off for the update.
+ *
+ * For compatability with legacy crtc helpers this should be called after
+ * drm_atomic_helper_commit_planes(), which is what the default commit function
+ * does. But drivers with different needs can group the modeset commits together
+ * and do the plane commits at the end. This is useful for drivers doing runtime
+ * PM since planes updates then only happen when the CRTC is actually enabled.
  */
-void drm_atomic_helper_commit_post_planes(struct drm_device *dev,
-                                         struct drm_atomic_state *old_state)
+void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
+                                             struct drm_atomic_state *old_state)
 {
-       int ncrtcs = old_state->dev->mode_config.num_crtc;
+       struct drm_crtc *crtc;
+       struct drm_crtc_state *old_crtc_state;
+       struct drm_connector *connector;
+       struct drm_connector_state *old_conn_state;
        int i;
 
-       for (i = 0; i < ncrtcs; i++) {
-               struct drm_crtc_helper_funcs *funcs;
-               struct drm_crtc *crtc;
-
-               crtc = old_state->crtcs[i];
+       for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
+               const struct drm_crtc_helper_funcs *funcs;
 
                /* Need to filter out CRTCs where only planes change. */
-               if (!crtc || !needs_modeset(crtc->state))
+               if (!needs_modeset(crtc->state))
                        continue;
 
                if (!crtc->state->active)
@@ -816,8 +804,8 @@ void drm_atomic_helper_commit_post_planes(struct drm_device *dev,
                funcs = crtc->helper_private;
 
                if (crtc->state->enable) {
-                       DRM_DEBUG_KMS("enabling [CRTC:%d]\n",
-                                     crtc->base.id);
+                       DRM_DEBUG_ATOMIC("enabling [CRTC:%d]\n",
+                                        crtc->base.id);
 
                        if (funcs->enable)
                                funcs->enable(crtc);
@@ -826,28 +814,26 @@ void drm_atomic_helper_commit_post_planes(struct drm_device *dev,
                }
        }
 
-       for (i = 0; i < old_state->num_connector; i++) {
-               struct drm_connector *connector;
-               struct drm_encoder_helper_funcs *funcs;
+       for_each_connector_in_state(old_state, connector, old_conn_state, i) {
+               const struct drm_encoder_helper_funcs *funcs;
                struct drm_encoder *encoder;
 
-               connector = old_state->connectors[i];
-
-               if (!connector || !connector->state->best_encoder)
+               if (!connector->state->best_encoder)
                        continue;
 
-               if (!connector->state->crtc->state->active)
+               if (!connector->state->crtc->state->active ||
+                   !needs_modeset(connector->state->crtc->state))
                        continue;
 
                encoder = connector->state->best_encoder;
                funcs = encoder->helper_private;
 
-               DRM_DEBUG_KMS("enabling [ENCODER:%d:%s]\n",
-                             encoder->base.id, encoder->name);
+               DRM_DEBUG_ATOMIC("enabling [ENCODER:%d:%s]\n",
+                                encoder->base.id, encoder->name);
 
                /*
                 * Each encoder has at most one connector (since we always steal
-                * it away), so we won't call call enable hooks twice.
+                * it away), so we won't call enable hooks twice.
                 */
                if (encoder->bridge)
                        encoder->bridge->funcs->pre_enable(encoder->bridge);
@@ -861,18 +847,17 @@ void drm_atomic_helper_commit_post_planes(struct drm_device *dev,
                        encoder->bridge->funcs->enable(encoder->bridge);
        }
 }
-EXPORT_SYMBOL(drm_atomic_helper_commit_post_planes);
+EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_enables);
 
 static void wait_for_fences(struct drm_device *dev,
                            struct drm_atomic_state *state)
 {
-       int nplanes = dev->mode_config.num_total_plane;
+       struct drm_plane *plane;
+       struct drm_plane_state *plane_state;
        int i;
 
-       for (i = 0; i < nplanes; i++) {
-               struct drm_plane *plane = state->planes[i];
-
-               if (!plane || !plane->state->fence)
+       for_each_plane_in_state(state, plane, plane_state, i) {
+               if (!plane->state->fence)
                        continue;
 
                WARN_ON(!plane->state->fb);
@@ -889,16 +874,9 @@ static bool framebuffer_changed(struct drm_device *dev,
 {
        struct drm_plane *plane;
        struct drm_plane_state *old_plane_state;
-       int nplanes = old_state->dev->mode_config.num_total_plane;
        int i;
 
-       for (i = 0; i < nplanes; i++) {
-               plane = old_state->planes[i];
-               old_plane_state = old_state->plane_states[i];
-
-               if (!plane)
-                       continue;
-
+       for_each_plane_in_state(old_state, plane, old_plane_state, i) {
                if (plane->state->crtc != crtc &&
                    old_plane_state->crtc != crtc)
                        continue;
@@ -927,16 +905,9 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev,
 {
        struct drm_crtc *crtc;
        struct drm_crtc_state *old_crtc_state;
-       int ncrtcs = old_state->dev->mode_config.num_crtc;
        int i, ret;
 
-       for (i = 0; i < ncrtcs; i++) {
-               crtc = old_state->crtcs[i];
-               old_crtc_state = old_state->crtc_states[i];
-
-               if (!crtc)
-                       continue;
-
+       for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
                /* No one cares about the old state, so abuse it for tracking
                 * and store whether we hold a vblank reference (and should do a
                 * vblank wait) in the ->enable boolean. */
@@ -961,11 +932,8 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev,
                old_crtc_state->last_vblank_count = drm_vblank_count(dev, i);
        }
 
-       for (i = 0; i < ncrtcs; i++) {
-               crtc = old_state->crtcs[i];
-               old_crtc_state = old_state->crtc_states[i];
-
-               if (!crtc || !old_crtc_state->enable)
+       for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
+               if (!old_crtc_state->enable)
                        continue;
 
                ret = wait_event_timeout(dev->vblank[i].queue,
@@ -1014,7 +982,7 @@ int drm_atomic_helper_commit(struct drm_device *dev,
 
        /*
         * Everything below can be run asynchronously without the need to grab
-        * any modeset locks at all under one conditions: It must be guaranteed
+        * any modeset locks at all under one condition: It must be guaranteed
         * that the asynchronous work has either been cancelled (if the driver
         * supports it, which at least requires that the framebuffers get
         * cleaned up with drm_atomic_helper_cleanup_planes()) or completed
@@ -1030,11 +998,11 @@ int drm_atomic_helper_commit(struct drm_device *dev,
 
        wait_for_fences(dev, state);
 
-       drm_atomic_helper_commit_pre_planes(dev, state);
+       drm_atomic_helper_commit_modeset_disables(dev, state);
 
        drm_atomic_helper_commit_planes(dev, state);
 
-       drm_atomic_helper_commit_post_planes(dev, state);
+       drm_atomic_helper_commit_modeset_enables(dev, state);
 
        drm_atomic_helper_wait_for_vblanks(dev, state);
 
@@ -1085,9 +1053,9 @@ EXPORT_SYMBOL(drm_atomic_helper_commit);
  */
 
 /**
- * drm_atomic_helper_prepare_planes - prepare plane resources after commit
+ * drm_atomic_helper_prepare_planes - prepare plane resources before commit
  * @dev: DRM device
- * @state: atomic state object with old state structures
+ * @state: atomic state object with new state structures
  *
  * This function prepares plane state, specifically framebuffers, for the new
  * configuration. If any failure is encountered this function will call
@@ -1103,8 +1071,9 @@ int drm_atomic_helper_prepare_planes(struct drm_device *dev,
        int ret, i;
 
        for (i = 0; i < nplanes; i++) {
-               struct drm_plane_helper_funcs *funcs;
+               const struct drm_plane_helper_funcs *funcs;
                struct drm_plane *plane = state->planes[i];
+               struct drm_plane_state *plane_state = state->plane_states[i];
                struct drm_framebuffer *fb;
 
                if (!plane)
@@ -1112,10 +1081,10 @@ int drm_atomic_helper_prepare_planes(struct drm_device *dev,
 
                funcs = plane->helper_private;
 
-               fb = state->plane_states[i]->fb;
+               fb = plane_state->fb;
 
                if (fb && funcs->prepare_fb) {
-                       ret = funcs->prepare_fb(plane, fb);
+                       ret = funcs->prepare_fb(plane, fb, plane_state);
                        if (ret)
                                goto fail;
                }
@@ -1125,8 +1094,9 @@ int drm_atomic_helper_prepare_planes(struct drm_device *dev,
 
 fail:
        for (i--; i >= 0; i--) {
-               struct drm_plane_helper_funcs *funcs;
+               const struct drm_plane_helper_funcs *funcs;
                struct drm_plane *plane = state->planes[i];
+               struct drm_plane_state *plane_state = state->plane_states[i];
                struct drm_framebuffer *fb;
 
                if (!plane)
@@ -1137,7 +1107,7 @@ fail:
                fb = state->plane_states[i]->fb;
 
                if (fb && funcs->cleanup_fb)
-                       funcs->cleanup_fb(plane, fb);
+                       funcs->cleanup_fb(plane, fb, plane_state);
 
        }
 
@@ -1157,20 +1127,22 @@ EXPORT_SYMBOL(drm_atomic_helper_prepare_planes);
  *
  * It still requires the global state object @old_state to know which planes and
  * crtcs need to be updated though.
+ *
+ * Note that this function does all plane updates across all CRTCs in one step.
+ * If the hardware can't support this approach look at
+ * drm_atomic_helper_commit_planes_on_crtc() instead.
  */
 void drm_atomic_helper_commit_planes(struct drm_device *dev,
                                     struct drm_atomic_state *old_state)
 {
-       int nplanes = dev->mode_config.num_total_plane;
-       int ncrtcs = dev->mode_config.num_crtc;
+       struct drm_crtc *crtc;
+       struct drm_crtc_state *old_crtc_state;
+       struct drm_plane *plane;
+       struct drm_plane_state *old_plane_state;
        int i;
 
-       for (i = 0; i < ncrtcs; i++) {
-               struct drm_crtc_helper_funcs *funcs;
-               struct drm_crtc *crtc = old_state->crtcs[i];
-
-               if (!crtc)
-                       continue;
+       for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
+               const struct drm_crtc_helper_funcs *funcs;
 
                funcs = crtc->helper_private;
 
@@ -1180,37 +1152,27 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
                funcs->atomic_begin(crtc);
        }
 
-       for (i = 0; i < nplanes; i++) {
-               struct drm_plane_helper_funcs *funcs;
-               struct drm_plane *plane = old_state->planes[i];
-               struct drm_plane_state *old_plane_state;
-
-               if (!plane)
-                       continue;
+       for_each_plane_in_state(old_state, plane, old_plane_state, i) {
+               const struct drm_plane_helper_funcs *funcs;
 
                funcs = plane->helper_private;
 
                if (!funcs)
                        continue;
 
-               old_plane_state = old_state->plane_states[i];
-
                /*
                 * Special-case disabling the plane if drivers support it.
                 */
                if (drm_atomic_plane_disabling(plane, old_plane_state) &&
                    funcs->atomic_disable)
                        funcs->atomic_disable(plane, old_plane_state);
-               else
+               else if (plane->state->crtc ||
+                        drm_atomic_plane_disabling(plane, old_plane_state))
                        funcs->atomic_update(plane, old_plane_state);
        }
 
-       for (i = 0; i < ncrtcs; i++) {
-               struct drm_crtc_helper_funcs *funcs;
-               struct drm_crtc *crtc = old_state->crtcs[i];
-
-               if (!crtc)
-                       continue;
+       for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
+               const struct drm_crtc_helper_funcs *funcs;
 
                funcs = crtc->helper_private;
 
@@ -1222,6 +1184,64 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
 }
 EXPORT_SYMBOL(drm_atomic_helper_commit_planes);
 
+/**
+ * drm_atomic_helper_commit_planes_on_crtc - commit plane state for a crtc
+ * @old_crtc_state: atomic state object with the old crtc state
+ *
+ * This function commits the new plane state using the plane and atomic helper
+ * functions for planes on the specific crtc. It assumes that the atomic state
+ * has already been pushed into the relevant object state pointers, since this
+ * step can no longer fail.
+ *
+ * This function is useful when plane updates should be done crtc-by-crtc
+ * instead of one global step like drm_atomic_helper_commit_planes() does.
+ *
+ * This function can only be savely used when planes are not allowed to move
+ * between different CRTCs because this function doesn't handle inter-CRTC
+ * depencies. Callers need to ensure that either no such depencies exist,
+ * resolve them through ordering of commit calls or through some other means.
+ */
+void
+drm_atomic_helper_commit_planes_on_crtc(struct drm_crtc_state *old_crtc_state)
+{
+       const struct drm_crtc_helper_funcs *crtc_funcs;
+       struct drm_crtc *crtc = old_crtc_state->crtc;
+       struct drm_atomic_state *old_state = old_crtc_state->state;
+       struct drm_plane *plane;
+       unsigned plane_mask;
+
+       plane_mask = old_crtc_state->plane_mask;
+       plane_mask |= crtc->state->plane_mask;
+
+       crtc_funcs = crtc->helper_private;
+       if (crtc_funcs && crtc_funcs->atomic_begin)
+               crtc_funcs->atomic_begin(crtc);
+
+       drm_for_each_plane_mask(plane, crtc->dev, plane_mask) {
+               struct drm_plane_state *old_plane_state =
+                       drm_atomic_get_existing_plane_state(old_state, plane);
+               const struct drm_plane_helper_funcs *plane_funcs;
+
+               plane_funcs = plane->helper_private;
+
+               if (!old_plane_state || !plane_funcs)
+                       continue;
+
+               WARN_ON(plane->state->crtc && plane->state->crtc != crtc);
+
+               if (drm_atomic_plane_disabling(plane, old_plane_state) &&
+                   plane_funcs->atomic_disable)
+                       plane_funcs->atomic_disable(plane, old_plane_state);
+               else if (plane->state->crtc ||
+                        drm_atomic_plane_disabling(plane, old_plane_state))
+                       plane_funcs->atomic_update(plane, old_plane_state);
+       }
+
+       if (crtc_funcs && crtc_funcs->atomic_flush)
+               crtc_funcs->atomic_flush(crtc);
+}
+EXPORT_SYMBOL(drm_atomic_helper_commit_planes_on_crtc);
+
 /**
  * drm_atomic_helper_cleanup_planes - cleanup plane resources after commit
  * @dev: DRM device
@@ -1237,23 +1257,20 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_planes);
 void drm_atomic_helper_cleanup_planes(struct drm_device *dev,
                                      struct drm_atomic_state *old_state)
 {
-       int nplanes = dev->mode_config.num_total_plane;
+       struct drm_plane *plane;
+       struct drm_plane_state *plane_state;
        int i;
 
-       for (i = 0; i < nplanes; i++) {
-               struct drm_plane_helper_funcs *funcs;
-               struct drm_plane *plane = old_state->planes[i];
+       for_each_plane_in_state(old_state, plane, plane_state, i) {
+               const struct drm_plane_helper_funcs *funcs;
                struct drm_framebuffer *old_fb;
 
-               if (!plane)
-                       continue;
-
                funcs = plane->helper_private;
 
-               old_fb = old_state->plane_states[i]->fb;
+               old_fb = plane_state->fb;
 
                if (old_fb && funcs->cleanup_fb)
-                       funcs->cleanup_fb(plane, old_fb);
+                       funcs->cleanup_fb(plane, old_fb, plane_state);
        }
 }
 EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes);
@@ -1379,13 +1396,13 @@ retry:
        plane_state->src_h = src_h;
        plane_state->src_w = src_w;
 
+       if (plane == crtc->cursor)
+               state->legacy_cursor_update = true;
+
        ret = drm_atomic_commit(state);
        if (ret != 0)
                goto fail;
 
-       if (plane == crtc->cursor)
-               state->legacy_cursor_update = true;
-
        /* Driver takes ownership of state on successful commit. */
        return 0;
 fail:
@@ -1496,8 +1513,10 @@ static int update_output_state(struct drm_atomic_state *state,
                               struct drm_mode_set *set)
 {
        struct drm_device *dev = set->crtc->dev;
+       struct drm_crtc *crtc;
+       struct drm_crtc_state *crtc_state;
+       struct drm_connector *connector;
        struct drm_connector_state *conn_state;
-       int ncrtcs = state->dev->mode_config.num_crtc;
        int ret, i, j;
 
        ret = drm_modeset_lock(&dev->mode_config.connection_mutex,
@@ -1513,27 +1532,14 @@ static int update_output_state(struct drm_atomic_state *state,
                        return PTR_ERR(conn_state);
        }
 
-       for (i = 0; i < ncrtcs; i++) {
-               struct drm_crtc *crtc = state->crtcs[i];
-
-               if (!crtc)
-                       continue;
-
+       for_each_crtc_in_state(state, crtc, crtc_state, i) {
                ret = drm_atomic_add_affected_connectors(state, crtc);
                if (ret)
                        return ret;
        }
 
        /* Then recompute connector->crtc links and crtc enabling state. */
-       for (i = 0; i < state->num_connector; i++) {
-               struct drm_connector *connector;
-
-               connector = state->connectors[i];
-               conn_state = state->connector_states[i];
-
-               if (!connector)
-                       continue;
-
+       for_each_connector_in_state(state, connector, conn_state, i) {
                if (conn_state->crtc == set->crtc) {
                        ret = drm_atomic_set_crtc_for_connector(conn_state,
                                                                NULL);
@@ -1552,13 +1558,7 @@ static int update_output_state(struct drm_atomic_state *state,
                }
        }
 
-       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;
-
+       for_each_crtc_in_state(state, crtc, crtc_state, i) {
                /* Don't update ->enable for the CRTC in the set_config request,
                 * since a mismatch would indicate a bug in the upper layers.
                 * The actual modeset code later on will catch any
@@ -1678,12 +1678,13 @@ backoff:
 EXPORT_SYMBOL(drm_atomic_helper_set_config);
 
 /**
- * drm_atomic_helper_crtc_set_property - helper for crtc prorties
+ * drm_atomic_helper_crtc_set_property - helper for crtc properties
  * @crtc: DRM crtc
  * @property: DRM property
  * @val: value of property
  *
- * Provides a default plane disablle handler using the atomic driver interface.
+ * Provides a default crtc set_property handler using the atomic driver
+ * interface.
  *
  * RETURNS:
  * Zero on success, error code on failure
@@ -1737,12 +1738,13 @@ backoff:
 EXPORT_SYMBOL(drm_atomic_helper_crtc_set_property);
 
 /**
- * drm_atomic_helper_plane_set_property - helper for plane prorties
+ * drm_atomic_helper_plane_set_property - helper for plane properties
  * @plane: DRM plane
  * @property: DRM property
  * @val: value of property
  *
- * Provides a default plane disable handler using the atomic driver interface.
+ * Provides a default plane set_property handler using the atomic driver
+ * interface.
  *
  * RETURNS:
  * Zero on success, error code on failure
@@ -1796,12 +1798,13 @@ backoff:
 EXPORT_SYMBOL(drm_atomic_helper_plane_set_property);
 
 /**
- * drm_atomic_helper_connector_set_property - helper for connector prorties
+ * drm_atomic_helper_connector_set_property - helper for connector properties
  * @connector: DRM connector
  * @property: DRM property
  * @val: value of property
  *
- * Provides a default plane disablle handler using the atomic driver interface.
+ * Provides a default connector set_property handler using the atomic driver
+ * interface.
  *
  * RETURNS:
  * Zero on success, error code on failure
@@ -1984,10 +1987,10 @@ retry:
        WARN_ON(!drm_modeset_is_locked(&config->connection_mutex));
 
        list_for_each_entry(tmp_connector, &config->connector_list, head) {
-               if (connector->state->crtc != crtc)
+               if (tmp_connector->state->crtc != crtc)
                        continue;
 
-               if (connector->dpms == DRM_MODE_DPMS_ON) {
+               if (tmp_connector->dpms == DRM_MODE_DPMS_ON) {
                        active = true;
                        break;
                }
@@ -2049,6 +2052,26 @@ void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc)
 }
 EXPORT_SYMBOL(drm_atomic_helper_crtc_reset);
 
+/**
+ * __drm_atomic_helper_crtc_duplicate_state - copy atomic CRTC state
+ * @crtc: CRTC object
+ * @state: atomic CRTC state
+ *
+ * Copies atomic state from a CRTC's current state and resets inferred values.
+ * This is useful for drivers that subclass the CRTC state.
+ */
+void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc,
+                                             struct drm_crtc_state *state)
+{
+       memcpy(state, crtc->state, sizeof(*state));
+
+       state->mode_changed = false;
+       state->active_changed = false;
+       state->planes_changed = false;
+       state->event = NULL;
+}
+EXPORT_SYMBOL(__drm_atomic_helper_crtc_duplicate_state);
+
 /**
  * drm_atomic_helper_crtc_duplicate_state - default state duplicate hook
  * @crtc: drm CRTC
@@ -2064,19 +2087,34 @@ drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc)
        if (WARN_ON(!crtc->state))
                return NULL;
 
-       state = kmemdup(crtc->state, sizeof(*crtc->state), GFP_KERNEL);
-
-       if (state) {
-               state->mode_changed = false;
-               state->active_changed = false;
-               state->planes_changed = false;
-               state->event = NULL;
-       }
+       state = kmalloc(sizeof(*state), GFP_KERNEL);
+       if (state)
+               __drm_atomic_helper_crtc_duplicate_state(crtc, state);
 
        return state;
 }
 EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state);
 
+/**
+ * __drm_atomic_helper_crtc_destroy_state - release CRTC state
+ * @crtc: CRTC object
+ * @state: CRTC state object to release
+ *
+ * Releases all resources stored in the CRTC state without actually freeing
+ * the memory of the CRTC state. This is useful for drivers that subclass the
+ * CRTC state.
+ */
+void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc,
+                                           struct drm_crtc_state *state)
+{
+       /*
+        * This is currently a placeholder so that drivers that subclass the
+        * state will automatically do the right thing if code is ever added
+        * to this function.
+        */
+}
+EXPORT_SYMBOL(__drm_atomic_helper_crtc_destroy_state);
+
 /**
  * drm_atomic_helper_crtc_destroy_state - default state destroy hook
  * @crtc: drm CRTC
@@ -2088,6 +2126,7 @@ EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state);
 void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc,
                                          struct drm_crtc_state *state)
 {
+       __drm_atomic_helper_crtc_destroy_state(crtc, state);
        kfree(state);
 }
 EXPORT_SYMBOL(drm_atomic_helper_crtc_destroy_state);
@@ -2112,6 +2151,24 @@ void drm_atomic_helper_plane_reset(struct drm_plane *plane)
 }
 EXPORT_SYMBOL(drm_atomic_helper_plane_reset);
 
+/**
+ * __drm_atomic_helper_plane_duplicate_state - copy atomic plane state
+ * @plane: plane object
+ * @state: atomic plane state
+ *
+ * Copies atomic state from a plane's current state. This is useful for
+ * drivers that subclass the plane state.
+ */
+void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane,
+                                              struct drm_plane_state *state)
+{
+       memcpy(state, plane->state, sizeof(*state));
+
+       if (state->fb)
+               drm_framebuffer_reference(state->fb);
+}
+EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state);
+
 /**
  * drm_atomic_helper_plane_duplicate_state - default state duplicate hook
  * @plane: drm plane
@@ -2127,15 +2184,31 @@ drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane)
        if (WARN_ON(!plane->state))
                return NULL;
 
-       state = kmemdup(plane->state, sizeof(*plane->state), GFP_KERNEL);
-
-       if (state && state->fb)
-               drm_framebuffer_reference(state->fb);
+       state = kmalloc(sizeof(*state), GFP_KERNEL);
+       if (state)
+               __drm_atomic_helper_plane_duplicate_state(plane, state);
 
        return state;
 }
 EXPORT_SYMBOL(drm_atomic_helper_plane_duplicate_state);
 
+/**
+ * __drm_atomic_helper_plane_destroy_state - release plane state
+ * @plane: plane object
+ * @state: plane state object to release
+ *
+ * Releases all resources stored in the plane state without actually freeing
+ * the memory of the plane state. This is useful for drivers that subclass the
+ * plane state.
+ */
+void __drm_atomic_helper_plane_destroy_state(struct drm_plane *plane,
+                                            struct drm_plane_state *state)
+{
+       if (state->fb)
+               drm_framebuffer_unreference(state->fb);
+}
+EXPORT_SYMBOL(__drm_atomic_helper_plane_destroy_state);
+
 /**
  * drm_atomic_helper_plane_destroy_state - default state destroy hook
  * @plane: drm plane
@@ -2147,9 +2220,7 @@ EXPORT_SYMBOL(drm_atomic_helper_plane_duplicate_state);
 void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane,
                                           struct drm_plane_state *state)
 {
-       if (state->fb)
-               drm_framebuffer_unreference(state->fb);
-
+       __drm_atomic_helper_plane_destroy_state(plane, state);
        kfree(state);
 }
 EXPORT_SYMBOL(drm_atomic_helper_plane_destroy_state);
@@ -2172,6 +2243,22 @@ void drm_atomic_helper_connector_reset(struct drm_connector *connector)
 }
 EXPORT_SYMBOL(drm_atomic_helper_connector_reset);
 
+/**
+ * __drm_atomic_helper_connector_duplicate_state - copy atomic connector state
+ * @connector: connector object
+ * @state: atomic connector state
+ *
+ * Copies atomic state from a connector's current state. This is useful for
+ * drivers that subclass the connector state.
+ */
+void
+__drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector,
+                                           struct drm_connector_state *state)
+{
+       memcpy(state, connector->state, sizeof(*state));
+}
+EXPORT_SYMBOL(__drm_atomic_helper_connector_duplicate_state);
+
 /**
  * drm_atomic_helper_connector_duplicate_state - default state duplicate hook
  * @connector: drm connector
@@ -2182,13 +2269,40 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_reset);
 struct drm_connector_state *
 drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector)
 {
+       struct drm_connector_state *state;
+
        if (WARN_ON(!connector->state))
                return NULL;
 
-       return kmemdup(connector->state, sizeof(*connector->state), GFP_KERNEL);
+       state = kmalloc(sizeof(*state), GFP_KERNEL);
+       if (state)
+               __drm_atomic_helper_connector_duplicate_state(connector, state);
+
+       return state;
 }
 EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state);
 
+/**
+ * __drm_atomic_helper_connector_destroy_state - release connector state
+ * @connector: connector object
+ * @state: connector state object to release
+ *
+ * Releases all resources stored in the connector state without actually
+ * freeing the memory of the connector state. This is useful for drivers that
+ * subclass the connector state.
+ */
+void
+__drm_atomic_helper_connector_destroy_state(struct drm_connector *connector,
+                                           struct drm_connector_state *state)
+{
+       /*
+        * This is currently a placeholder so that drivers that subclass the
+        * state will automatically do the right thing if code is ever added
+        * to this function.
+        */
+}
+EXPORT_SYMBOL(__drm_atomic_helper_connector_destroy_state);
+
 /**
  * drm_atomic_helper_connector_destroy_state - default state destroy hook
  * @connector: drm connector
@@ -2200,6 +2314,7 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state);
 void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector,
                                          struct drm_connector_state *state)
 {
+       __drm_atomic_helper_connector_destroy_state(connector, state);
        kfree(state);
 }
 EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state);
This page took 0.175099 seconds and 5 git commands to generate.