drm/atomic: add commit_planes_on_crtc helper
[deliverable/linux.git] / drivers / gpu / drm / drm_atomic_helper.c
index 1d2ca52530d5f3148fec55d72d22c79f2eaa8949..424a98bfa6869a283090dc641948452ea485c3d9 100644 (file)
@@ -280,6 +280,8 @@ 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(
@@ -317,6 +319,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) {
@@ -619,8 +624,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;
@@ -657,8 +676,13 @@ set_routing_links(struct drm_device *dev, struct drm_atomic_state *old_state)
                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)
@@ -737,7 +761,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);
@@ -1101,6 +1127,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 +1160,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 +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
@@ -1309,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:
This page took 0.030194 seconds and 5 git commands to generate.