drm/atomic: Only update crtc->x/y if it's part of the state, v2.
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / drm_atomic_helper.c
index 1d2ca52530d5f3148fec55d72d22c79f2eaa8949..f94cc371742ef9293c9c4d74f83c1ef6b1715465 100644 (file)
@@ -280,15 +280,14 @@ 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_ATOMIC("Bridge fixup failed\n");
-                               return -EINVAL;
-                       }
+               ret = drm_bridge_mode_fixup(encoder->bridge, &crtc_state->mode,
+                               &crtc_state->adjusted_mode);
+               if (!ret) {
+                       DRM_DEBUG_ATOMIC("Bridge fixup failed\n");
+                       return -EINVAL;
                }
 
                if (funcs->atomic_check) {
@@ -317,6 +316,9 @@ mode_fixup(struct drm_atomic_state *state)
                        continue;
 
                funcs = crtc->helper_private;
+               if (!funcs->mode_fixup)
+                       continue;
+
                ret = funcs->mode_fixup(crtc, &crtc_state->mode,
                                        &crtc_state->adjusted_mode);
                if (!ret) {
@@ -329,12 +331,6 @@ mode_fixup(struct drm_atomic_state *state)
        return 0;
 }
 
-static bool
-needs_modeset(struct drm_crtc_state *state)
-{
-       return state->mode_changed || state->active_changed;
-}
-
 /**
  * drm_atomic_helper_check_modeset - validate state object for modeset changes
  * @dev: DRM device
@@ -412,7 +408,7 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
                        crtc_state->active_changed = true;
                }
 
-               if (!needs_modeset(crtc_state))
+               if (!drm_atomic_crtc_needs_modeset(crtc_state))
                        continue;
 
                DRM_DEBUG_ATOMIC("[CRTC:%d] needs all connectors, enable: %c, active: %c\n",
@@ -424,6 +420,10 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
                if (ret != 0)
                        return ret;
 
+               ret = drm_atomic_add_affected_planes(state, crtc);
+               if (ret != 0)
+                       return ret;
+
                num_connectors = drm_atomic_connectors_for_crtc(state,
                                                                crtc);
 
@@ -558,7 +558,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
                old_crtc_state = old_state->crtc_states[drm_crtc_index(old_conn_state->crtc)];
 
                if (!old_crtc_state->active ||
-                   !needs_modeset(old_conn_state->crtc->state))
+                   !drm_atomic_crtc_needs_modeset(old_conn_state->crtc->state))
                        continue;
 
                encoder = old_conn_state->best_encoder;
@@ -578,8 +578,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
                 * Each encoder has at most one connector (since we always steal
                 * it away), so we won't call disable hooks twice.
                 */
-               if (encoder->bridge)
-                       encoder->bridge->funcs->disable(encoder->bridge);
+               drm_bridge_disable(encoder->bridge);
 
                /* Right function depends upon target state. */
                if (connector->state->crtc && funcs->prepare)
@@ -589,15 +588,14 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
                else
                        funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
 
-               if (encoder->bridge)
-                       encoder->bridge->funcs->post_disable(encoder->bridge);
+               drm_bridge_post_disable(encoder->bridge);
        }
 
        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 (!needs_modeset(crtc->state))
+               if (!drm_atomic_crtc_needs_modeset(crtc->state))
                        continue;
 
                if (!old_crtc_state->active)
@@ -619,8 +617,22 @@ 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)
 {
        struct drm_connector *connector;
        struct drm_connector_state *old_conn_state;
@@ -653,12 +665,23 @@ set_routing_links(struct drm_device *dev, struct drm_atomic_state *old_state)
 
        /* set legacy state in the crtc structure */
        for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
+               struct drm_plane *primary = crtc->primary;
+
                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 (drm_atomic_get_existing_plane_state(old_state, primary) &&
+                   primary->state->crtc == crtc) {
+                       crtc->x = primary->state->src_x >> 16;
+                       crtc->y = 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)
@@ -713,9 +736,7 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state)
                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,
-                                                        mode, adjusted_mode);
+               drm_bridge_mode_set(encoder->bridge, mode, adjusted_mode);
        }
 }
 
@@ -737,7 +758,9 @@ void drm_atomic_helper_commit_modeset_disables(struct drm_device *dev,
                                               struct drm_atomic_state *old_state)
 {
        disable_outputs(dev, old_state);
-       set_routing_links(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_modeset_disables);
@@ -769,7 +792,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
                const struct drm_crtc_helper_funcs *funcs;
 
                /* Need to filter out CRTCs where only planes change. */
-               if (!needs_modeset(crtc->state))
+               if (!drm_atomic_crtc_needs_modeset(crtc->state))
                        continue;
 
                if (!crtc->state->active)
@@ -796,7 +819,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
                        continue;
 
                if (!connector->state->crtc->state->active ||
-                   !needs_modeset(connector->state->crtc->state))
+                   !drm_atomic_crtc_needs_modeset(connector->state->crtc->state))
                        continue;
 
                encoder = connector->state->best_encoder;
@@ -809,16 +832,14 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
                 * Each encoder has at most one connector (since we always steal
                 * it away), so we won't call enable hooks twice.
                 */
-               if (encoder->bridge)
-                       encoder->bridge->funcs->pre_enable(encoder->bridge);
+               drm_bridge_pre_enable(encoder->bridge);
 
                if (funcs->enable)
                        funcs->enable(encoder);
                else
                        funcs->commit(encoder);
 
-               if (encoder->bridge)
-                       encoder->bridge->funcs->enable(encoder->bridge);
+               drm_bridge_enable(encoder->bridge);
        }
 }
 EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_enables);
@@ -1101,6 +1122,10 @@ 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)
@@ -1130,15 +1155,14 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
                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);
        }
 
@@ -1155,6 +1179,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
@@ -1309,13 +1391,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:
@@ -1479,8 +1561,14 @@ static int update_output_state(struct drm_atomic_state *state,
                if (crtc == set->crtc)
                        continue;
 
-               crtc_state->enable =
-                       drm_atomic_connectors_for_crtc(state, crtc);
+               if (!drm_atomic_connectors_for_crtc(state, crtc)) {
+                       ret = drm_atomic_set_mode_prop_for_crtc(crtc_state,
+                                                               NULL);
+                       if (ret < 0)
+                               return ret;
+
+                       crtc_state->active = false;
+               }
        }
 
        return 0;
@@ -1525,7 +1613,10 @@ retry:
                WARN_ON(set->fb);
                WARN_ON(set->num_connectors);
 
-               crtc_state->enable = false;
+               ret = drm_atomic_set_mode_for_crtc(crtc_state, NULL);
+               if (ret != 0)
+                       goto fail;
+
                crtc_state->active = false;
 
                ret = drm_atomic_set_crtc_for_plane(primary_state, NULL);
@@ -1540,9 +1631,11 @@ retry:
        WARN_ON(!set->fb);
        WARN_ON(!set->num_connectors);
 
-       crtc_state->enable = true;
+       ret = drm_atomic_set_mode_for_crtc(crtc_state, set->mode);
+       if (ret != 0)
+               goto fail;
+
        crtc_state->active = true;
-       drm_mode_copy(&crtc_state->mode, set->mode);
 
        ret = drm_atomic_set_crtc_for_plane(primary_state, crtc);
        if (ret != 0)
@@ -1828,10 +1921,6 @@ retry:
        if (ret != 0)
                goto fail;
 
-       /* TODO: ->page_flip is the only driver callback where the core
-        * doesn't update plane->fb. For now patch it up here. */
-       plane->fb = plane->state->fb;
-
        /* Driver takes ownership of state on successful async commit. */
        return 0;
 fail:
@@ -1957,6 +2046,8 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_dpms);
  */
 void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc)
 {
+       if (crtc->state && crtc->state->mode_blob)
+               drm_property_unreference_blob(crtc->state->mode_blob);
        kfree(crtc->state);
        crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL);
 
@@ -1978,6 +2069,8 @@ void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc,
 {
        memcpy(state, crtc->state, sizeof(*state));
 
+       if (state->mode_blob)
+               drm_property_reference_blob(state->mode_blob);
        state->mode_changed = false;
        state->active_changed = false;
        state->planes_changed = false;
@@ -2020,11 +2113,8 @@ EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_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.
-        */
+       if (state->mode_blob)
+               drm_property_unreference_blob(state->mode_blob);
 }
 EXPORT_SYMBOL(__drm_atomic_helper_crtc_destroy_state);