drm/atomic: Add current-mode blob to CRTC state

Add a blob property tracking the current mode to the CRTC state, and
ensure it is properly updated and referenced.

v2: Continue using crtc_state->mode inside getcrtc, instead of reading
    out the mode blob. Use IS_ERR and PTR_ERR from create_blob. Move
    set_mode_prop_for_crtc to later patch where it actually gets used.
    Enforce !!state->enable == !!state->mode_blob inside
    drm_atomic_crtc_check.

Signed-off-by: Daniel Stone <daniels@collabora.com>
Tested-by: Sean Paul <seanpaul@chromium.org>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index e749287..327ccc7 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -304,11 +304,25 @@
 int drm_atomic_set_mode_for_crtc(struct drm_crtc_state *state,
 				 struct drm_display_mode *mode)
 {
+	struct drm_mode_modeinfo umode;
+
 	/* Early return for no change. */
 	if (mode && memcmp(&state->mode, mode, sizeof(*mode)) == 0)
 		return 0;
 
+	if (state->mode_blob)
+		drm_property_unreference_blob(state->mode_blob);
+	state->mode_blob = NULL;
+
 	if (mode) {
+		drm_mode_convert_to_umode(&umode, mode);
+		state->mode_blob =
+			drm_property_create_blob(state->crtc->dev,
+		                                 sizeof(umode),
+		                                 &umode);
+		if (IS_ERR(state->mode_blob))
+			return PTR_ERR(state->mode_blob);
+
 		drm_mode_copy(&state->mode, mode);
 		state->enable = true;
 		DRM_DEBUG_ATOMIC("Set [MODE:%s] for CRTC state %p\n",
@@ -324,7 +338,6 @@
 }
 EXPORT_SYMBOL(drm_atomic_set_mode_for_crtc);
 
-
 /**
  * drm_atomic_crtc_set_property - set property on CRTC
  * @crtc: the drm CRTC to set a property on
@@ -410,6 +423,23 @@
 		return -EINVAL;
 	}
 
+	/* The state->enable vs. state->mode_blob checks can be WARN_ON,
+	 * as this is a kernel-internal detail that userspace should never
+	 * be able to trigger. */
+	if (drm_core_check_feature(crtc->dev, DRIVER_ATOMIC) &&
+	    WARN_ON(state->enable && !state->mode_blob)) {
+		DRM_DEBUG_ATOMIC("[CRTC:%d] enabled without mode blob\n",
+				 crtc->base.id);
+		return -EINVAL;
+	}
+
+	if (drm_core_check_feature(crtc->dev, DRIVER_ATOMIC) &&
+	    WARN_ON(!state->enable && state->mode_blob)) {
+		DRM_DEBUG_ATOMIC("[CRTC:%d] disabled with mode blob\n",
+				 crtc->base.id);
+		return -EINVAL;
+	}
+
 	return 0;
 }