Merge tag 'drm-misc-fixes-2018-02-21' of git://anongit.freedesktop.org/drm/drm-misc...
[sfrench/cifs-2.6.git] / drivers / gpu / drm / drm_atomic_helper.c
index e8c249361d7e4acebd8e6f2dcf5d50d8d94935dc..ae3cbfe9e01cd6a52cc7c6a15ec809e971139ee4 100644 (file)
@@ -695,6 +695,100 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
 }
 EXPORT_SYMBOL(drm_atomic_helper_check_modeset);
 
+/**
+ * drm_atomic_helper_check_plane_state() - Check plane state for validity
+ * @plane_state: plane state to check
+ * @crtc_state: crtc state to check
+ * @clip: integer clipping coordinates
+ * @min_scale: minimum @src:@dest scaling factor in 16.16 fixed point
+ * @max_scale: maximum @src:@dest scaling factor in 16.16 fixed point
+ * @can_position: is it legal to position the plane such that it
+ *                doesn't cover the entire crtc?  This will generally
+ *                only be false for primary planes.
+ * @can_update_disabled: can the plane be updated while the crtc
+ *                       is disabled?
+ *
+ * Checks that a desired plane update is valid, and updates various
+ * bits of derived state (clipped coordinates etc.). Drivers that provide
+ * their own plane handling rather than helper-provided implementations may
+ * still wish to call this function to avoid duplication of error checking
+ * code.
+ *
+ * RETURNS:
+ * Zero if update appears valid, error code on failure
+ */
+int drm_atomic_helper_check_plane_state(struct drm_plane_state *plane_state,
+                                       const struct drm_crtc_state *crtc_state,
+                                       const struct drm_rect *clip,
+                                       int min_scale,
+                                       int max_scale,
+                                       bool can_position,
+                                       bool can_update_disabled)
+{
+       struct drm_framebuffer *fb = plane_state->fb;
+       struct drm_rect *src = &plane_state->src;
+       struct drm_rect *dst = &plane_state->dst;
+       unsigned int rotation = plane_state->rotation;
+       int hscale, vscale;
+
+       WARN_ON(plane_state->crtc && plane_state->crtc != crtc_state->crtc);
+
+       *src = drm_plane_state_src(plane_state);
+       *dst = drm_plane_state_dest(plane_state);
+
+       if (!fb) {
+               plane_state->visible = false;
+               return 0;
+       }
+
+       /* crtc should only be NULL when disabling (i.e., !fb) */
+       if (WARN_ON(!plane_state->crtc)) {
+               plane_state->visible = false;
+               return 0;
+       }
+
+       if (!crtc_state->enable && !can_update_disabled) {
+               DRM_DEBUG_KMS("Cannot update plane of a disabled CRTC.\n");
+               return -EINVAL;
+       }
+
+       drm_rect_rotate(src, fb->width << 16, fb->height << 16, rotation);
+
+       /* Check scaling */
+       hscale = drm_rect_calc_hscale(src, dst, min_scale, max_scale);
+       vscale = drm_rect_calc_vscale(src, dst, min_scale, max_scale);
+       if (hscale < 0 || vscale < 0) {
+               DRM_DEBUG_KMS("Invalid scaling of plane\n");
+               drm_rect_debug_print("src: ", &plane_state->src, true);
+               drm_rect_debug_print("dst: ", &plane_state->dst, false);
+               return -ERANGE;
+       }
+
+       plane_state->visible = drm_rect_clip_scaled(src, dst, clip, hscale, vscale);
+
+       drm_rect_rotate_inv(src, fb->width << 16, fb->height << 16, rotation);
+
+       if (!plane_state->visible)
+               /*
+                * Plane isn't visible; some drivers can handle this
+                * so we just return success here.  Drivers that can't
+                * (including those that use the primary plane helper's
+                * update function) will return an error from their
+                * update_plane handler.
+                */
+               return 0;
+
+       if (!can_position && !drm_rect_equals(dst, clip)) {
+               DRM_DEBUG_KMS("Plane must cover entire CRTC\n");
+               drm_rect_debug_print("dst: ", dst, false);
+               drm_rect_debug_print("clip: ", clip, false);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_atomic_helper_check_plane_state);
+
 /**
  * drm_atomic_helper_check_planes - validate state object for planes changes
  * @dev: DRM device
@@ -907,6 +1001,12 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
  *
  * Drivers can use this for building their own atomic commit if they don't have
  * a pure helper-based modeset implementation.
+ *
+ * Since these updates are not synchronized with lockings, only code paths
+ * called from &drm_mode_config_helper_funcs.atomic_commit_tail can look at the
+ * legacy state filled out by this helper. Defacto this means this helper and
+ * the legacy state pointers are only really useful for transitioning an
+ * existing driver to the atomic world.
  */
 void
 drm_atomic_helper_update_legacy_modeset_state(struct drm_device *dev,
@@ -1789,11 +1889,8 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
                    !try_wait_for_completion(&old_conn_state->commit->flip_done))
                        return -EBUSY;
 
-               /* commit tracked through new_crtc_state->commit, no need to do it explicitly */
-               if (new_conn_state->crtc)
-                       continue;
-
-               commit = crtc_or_fake_commit(state, old_conn_state->crtc);
+               /* Always track connectors explicitly for e.g. link retraining. */
+               commit = crtc_or_fake_commit(state, new_conn_state->crtc ?: old_conn_state->crtc);
                if (!commit)
                        return -ENOMEM;
 
@@ -1807,10 +1904,7 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
                    !try_wait_for_completion(&old_plane_state->commit->flip_done))
                        return -EBUSY;
 
-               /*
-                * Unlike connectors, always track planes explicitly for
-                * async pageflip support.
-                */
+               /* Always track planes explicitly for async pageflip support. */
                commit = crtc_or_fake_commit(state, new_plane_state->crtc ?: old_plane_state->crtc);
                if (!commit)
                        return -ENOMEM;