[3/3] drm/atomic: refuse changing CRTC for planes while active
diff mbox

Message ID 1438171270-11471-3-git-send-email-daniel.vetter@ffwll.ch
State New
Headers show

Commit Message

Daniel Vetter July 29, 2015, 12:01 p.m. UTC
Very strictly speaking this is possible if you have special hw and
genlocked CRTCs. In general switching a plane between two active CRTC
just won't work so well and is probably not tested at all. Just forbid
it.

The exception is when both CRTC do a full modeset, then it should be
no problem at all to move the planes around (presuming a correct
implementation) so allow that case.

I've put this into the core since I really couldn't come up with a
case where we don't want to enforce that. But if that ever happens it
would be easy to move this check into helpers.

Cc: Thierry Reding <thierry.reding@gmail.com>
Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
---
 drivers/gpu/drm/drm_atomic.c        | 38 +++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/drm_atomic_helper.c |  1 +
 2 files changed, 39 insertions(+)

Patch
diff mbox

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 434915448ea0..422183e7ee7d 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -663,6 +663,38 @@  drm_atomic_plane_get_property(struct drm_plane *plane,
 	return 0;
 }
 
+/* checks whether a plane has its CRTC switched while being in active use. */
+static bool
+active_plane_switching(struct drm_atomic_state *state,
+		       struct drm_plane *plane,
+		       struct drm_plane_state *plane_state)
+{
+	struct drm_crtc_state *crtc_state, *curr_crtc_state;
+
+	if (!plane->state->crtc || !plane_state->crtc)
+		return false;
+
+	if (plane->state->crtc == plane_state->crtc)
+		return false;
+
+	curr_crtc_state = plane->state->crtc->state;
+	if (!curr_crtc_state->active)
+		return false;
+
+	crtc_state = drm_atomic_get_existing_crtc_state(state,
+							plane_state->crtc);
+	if (!crtc_state->active)
+		return false;
+
+	/* plane switching CRTC and both CRTC are active. This is only ok if
+	 * both CRTC do a full modeset. */
+	if (drm_atomic_crtc_needs_modeset(curr_crtc_state) &&
+	    drm_atomic_crtc_needs_modeset(crtc_state))
+		return false;
+
+	return true;
+}
+
 /**
  * drm_atomic_plane_check - check plane state
  * @plane: plane to check
@@ -734,6 +766,12 @@  static int drm_atomic_plane_check(struct drm_plane *plane,
 		return -ENOSPC;
 	}
 
+	if (active_plane_switching(state->state, plane, state)) {
+		DRM_DEBUG_ATOMIC("[PLANE:%d] switching active CRTC without modeset\n",
+				 plane->base.id);
+		return -EINVAL;
+	}
+
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 6be0adb5a0e9..54c59ddc59a5 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -497,6 +497,7 @@  drm_atomic_helper_check_planes(struct drm_device *dev,
 					 plane->base.id);
 			return ret;
 		}
+
 	}
 
 	for_each_crtc_in_state(state, crtc, crtc_state, i) {