Merge "Merge android13-gs-pixel-5.10-udc into android13-gs-pixel-5.10-udc-qpr1" into android13-gs-pixel-5.10-udc-qpr1
diff --git a/samsung/exynos_drm_crtc.c b/samsung/exynos_drm_crtc.c
index 2726dfa..9a6967a 100644
--- a/samsung/exynos_drm_crtc.c
+++ b/samsung/exynos_drm_crtc.c
@@ -15,6 +15,7 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_atomic_uapi.h>
 #include <drm/drm_bridge.h>
 #include <drm/drm_encoder.h>
 #include <drm/drm_color_mgmt.h>
@@ -448,6 +449,140 @@
 	return &copy->base;
 }
 
+struct drm_atomic_state
+*exynos_duplicate_active_crtc_state(struct drm_crtc *crtc,
+				struct drm_modeset_acquire_ctx *ctx)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_atomic_state *state;
+	struct drm_crtc_state *crtc_state;
+	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+	struct decon_device *decon = exynos_crtc->ctx;
+	int err;
+
+	state = drm_atomic_state_alloc(dev);
+	if (!state)
+		return ERR_PTR(-ENOMEM);
+
+	state->acquire_ctx = ctx;
+
+	crtc_state = drm_atomic_get_crtc_state(state, crtc);
+	if (IS_ERR(crtc_state)) {
+		err = PTR_ERR(crtc_state);
+		goto free_state;
+	}
+
+	if (!crtc_state->active) {
+		if (!atomic_read(&decon->recovery.recovering)) {
+			drm_atomic_state_put(state);
+			return NULL;
+		}
+		pr_warn("crtc[%s]: skipping duplication of inactive crtc state\n", crtc->name);
+		err = -EPERM;
+		goto free_state;
+	}
+
+	err = drm_atomic_add_affected_planes(state, crtc);
+	if (err)
+		goto free_state;
+
+	err = drm_atomic_add_affected_connectors(state, crtc);
+	if (err)
+		goto free_state;
+
+	/* clear the acquire context so that it isn't accidentally reused */
+	state->acquire_ctx = NULL;
+
+free_state:
+	if (err < 0) {
+		drm_atomic_state_put(state);
+		state = ERR_PTR(err);
+	}
+
+	return state;
+}
+
+struct drm_atomic_state
+*exynos_crtc_suspend(struct drm_crtc *crtc,
+			struct drm_modeset_acquire_ctx *ctx)
+{
+	struct drm_crtc_state *crtc_state;
+	struct drm_connector *conn;
+	struct drm_connector_state *conn_state;
+	struct drm_plane *plane;
+	struct drm_plane_state *plane_state;
+	struct drm_atomic_state *state, *suspend_state;
+	int ret, i;
+
+	suspend_state = exynos_duplicate_active_crtc_state(crtc, ctx);
+	if (IS_ERR_OR_NULL(suspend_state))
+		return suspend_state;
+
+	state = drm_atomic_state_alloc(crtc->dev);
+	if (!state) {
+		drm_atomic_state_put(suspend_state);
+		return ERR_PTR(-ENOMEM);
+	}
+	state->acquire_ctx = ctx;
+retry:
+	crtc_state = drm_atomic_get_crtc_state(state, crtc);
+	if (IS_ERR(crtc_state)) {
+		ret = PTR_ERR(crtc_state);
+		goto out;
+	}
+
+	crtc_state->active = false;
+
+	ret = drm_atomic_set_mode_prop_for_crtc(crtc_state, NULL);
+	if (ret)
+		goto out;
+
+	ret = drm_atomic_add_affected_planes(state, crtc);
+	if (ret)
+		goto out;
+
+	ret = drm_atomic_add_affected_connectors(state, crtc);
+	if (ret)
+		goto out;
+
+	for_each_new_connector_in_state(state, conn, conn_state, i) {
+		ret = drm_atomic_set_crtc_for_connector(conn_state, NULL);
+		if (ret)
+			goto out;
+	}
+
+	for_each_new_plane_in_state(state, plane, plane_state, i) {
+		ret = drm_atomic_set_crtc_for_plane(plane_state, NULL);
+		if (ret)
+			goto out;
+
+		drm_atomic_set_fb_for_plane(plane_state, NULL);
+	}
+
+	ret = drm_atomic_commit(state);
+out:
+	if (ret == -EDEADLK) {
+		drm_atomic_state_clear(state);
+		drm_atomic_state_clear(suspend_state);
+		ret = drm_modeset_backoff(ctx);
+		if (!ret)
+			goto retry;
+	} else if (ret) {
+		drm_atomic_state_put(suspend_state);
+		suspend_state = ERR_PTR(ret);
+	}
+
+	drm_atomic_state_put(state);
+
+	return suspend_state;
+}
+
+int exynos_crtc_resume(struct drm_atomic_state *state,
+				struct drm_modeset_acquire_ctx *ctx)
+{
+	return drm_atomic_helper_commit_duplicated_state(state, ctx);
+}
+
 static int
 exynos_drm_replace_property_blob_from_id(struct drm_device *dev,
 					 struct drm_property_blob **blob,
diff --git a/samsung/exynos_drm_crtc.h b/samsung/exynos_drm_crtc.h
index 42d4100..d2c75ce 100644
--- a/samsung/exynos_drm_crtc.h
+++ b/samsung/exynos_drm_crtc.h
@@ -65,4 +65,13 @@
 
 void exynos_crtc_set_mode(struct drm_device *dev,
 			struct drm_atomic_state *old_state);
+
+struct drm_atomic_state
+*exynos_duplicate_active_crtc_state(struct drm_crtc *crtc,
+				struct drm_modeset_acquire_ctx *ctx);
+struct drm_atomic_state
+*exynos_crtc_suspend(struct drm_crtc *crtc,
+		struct drm_modeset_acquire_ctx *ctx);
+int exynos_crtc_resume(struct drm_atomic_state *state,
+				struct drm_modeset_acquire_ctx *ctx);
 #endif
diff --git a/samsung/exynos_drm_decon.c b/samsung/exynos_drm_decon.c
index 706e096..27d5f3f 100644
--- a/samsung/exynos_drm_decon.c
+++ b/samsung/exynos_drm_decon.c
@@ -14,6 +14,8 @@
  */
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_atomic_uapi.h>
+#include <drm/drm_modeset_lock.h>
 #include <drm/drm_bridge.h>
 #include <drm/drm_vblank.h>
 #include <drm/exynos_drm.h>
@@ -2319,6 +2321,48 @@
 	return 0;
 }
 
+static int decon_atomic_suspend(struct decon_device *decon)
+{
+	struct drm_modeset_acquire_ctx ctx;
+	struct drm_atomic_state *suspend_state;
+	int ret = 0;
+
+	if (!decon) {
+		decon_err(decon, "%s: decon is not ready\n", __func__);
+		return -EINVAL;
+	}
+	drm_modeset_acquire_init(&ctx, 0);
+	suspend_state = exynos_crtc_suspend(&decon->crtc->base, &ctx);
+	if (!IS_ERR(suspend_state))
+		decon->suspend_state = suspend_state;
+	else
+		ret = PTR_ERR(suspend_state);
+
+	drm_modeset_drop_locks(&ctx);
+	drm_modeset_acquire_fini(&ctx);
+	return ret;
+}
+
+static int decon_atomic_resume(struct decon_device *decon)
+{
+	struct drm_modeset_acquire_ctx ctx;
+	int ret = 0;
+
+	if (!decon) {
+		decon_err(decon, "%s: decon is not ready\n", __func__);
+		return -EINVAL;
+	}
+	drm_modeset_acquire_init(&ctx, 0);
+	if (!IS_ERR_OR_NULL(decon->suspend_state)) {
+		ret = exynos_crtc_resume(decon->suspend_state, &ctx);
+		drm_atomic_state_put(decon->suspend_state);
+	}
+	decon->suspend_state = NULL;
+	drm_modeset_drop_locks(&ctx);
+	drm_modeset_acquire_fini(&ctx);
+	return ret;
+}
+
 static int decon_suspend(struct device *dev)
 {
 	struct decon_device *decon = dev_get_drvdata(dev);
@@ -2327,7 +2371,7 @@
 	decon_debug(decon, "%s\n", __func__);
 
 	if (!decon->hibernation)
-		return 0;
+		return decon_atomic_suspend(decon);
 
 	ret = exynos_hibernation_suspend(decon->hibernation);
 
@@ -2342,15 +2386,19 @@
 static int decon_resume(struct device *dev)
 {
 	struct decon_device *decon = dev_get_drvdata(dev);
+	int ret = 0;
 
 	if (!decon_is_effectively_active(decon))
 		return 0;
 
 	decon_debug(decon, "%s\n", __func__);
 
+	if (!decon->hibernation)
+		ret = decon_atomic_resume(decon);
+
 	DPU_EVENT_LOG(DPU_EVT_DECON_RESUME, decon->id, NULL);
 
-	return 0;
+	return ret;
 }
 #endif
 
diff --git a/samsung/exynos_drm_decon.h b/samsung/exynos_drm_decon.h
index f5c7421..c1c1393 100644
--- a/samsung/exynos_drm_decon.h
+++ b/samsung/exynos_drm_decon.h
@@ -454,6 +454,7 @@
 	struct device			*dev;
 	struct drm_device		*drm_dev;
 	struct exynos_drm_crtc		*crtc;
+	struct drm_atomic_state		*suspend_state;
 	/* dpp information saved in dpp channel number order */
 	struct dpp_device		*dpp[MAX_WIN_PER_DECON];
 	struct dpp_device		*rcd;
diff --git a/samsung/exynos_drm_recovery.c b/samsung/exynos_drm_recovery.c
index 8963147..dbdcb61 100644
--- a/samsung/exynos_drm_recovery.c
+++ b/samsung/exynos_drm_recovery.c
@@ -21,154 +21,39 @@
 #include <linux/export.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_device.h>
-#include <drm/drm_atomic.h>
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_atomic_uapi.h>
 #include <drm/drm_modeset_lock.h>
+#include "exynos_drm_crtc.h"
 #include "exynos_drm_decon.h"
 #include "exynos_drm_recovery.h"
 
-static struct drm_atomic_state *_duplicate_active_crtc_state(struct drm_crtc *crtc,
-							     struct drm_modeset_acquire_ctx *ctx)
-{
-	struct drm_device *dev = crtc->dev;
-	struct drm_atomic_state *state;
-	struct drm_crtc_state *crtc_state;
-	int err;
-
-	state = drm_atomic_state_alloc(dev);
-	if (!state)
-		return ERR_PTR(-ENOMEM);
-
-	state->acquire_ctx = ctx;
-
-	crtc_state = drm_atomic_get_crtc_state(state, crtc);
-	if (IS_ERR(crtc_state)) {
-		err = PTR_ERR(crtc_state);
-		goto free_state;
-	}
-
-	if (!crtc_state->active) {
-		pr_warn("crtc[%s]: skipping duplication of inactive crtc state\n", crtc->name);
-		err = -EINVAL;
-		goto free_state;
-	}
-
-	err = drm_atomic_add_affected_planes(state, crtc);
-	if (err)
-		goto free_state;
-
-	err = drm_atomic_add_affected_connectors(state, crtc);
-	if (err)
-		goto free_state;
-
-	/* clear the acquire context so that it isn't accidentally reused */
-	state->acquire_ctx = NULL;
-
-free_state:
-	if (err < 0) {
-		drm_atomic_state_put(state);
-		state = ERR_PTR(err);
-	}
-
-	return state;
-}
-
 static void exynos_recovery_handler(struct work_struct *work)
 {
 	struct exynos_recovery *recovery = container_of(work,
 					struct exynos_recovery, work);
 	struct decon_device *decon = container_of(recovery, struct decon_device,
 					recovery);
-	struct drm_device *dev = decon->drm_dev;
+	struct drm_atomic_state *rcv_state;
 	struct drm_modeset_acquire_ctx ctx;
-	struct drm_atomic_state *state, *rcv_state;
-	struct drm_crtc_state *crtc_state;
 	struct drm_crtc *crtc = &decon->crtc->base;
-	struct drm_connector *conn;
-	struct drm_connector_state *conn_state;
-	struct drm_plane *plane;
-	struct drm_plane_state *plane_state;
-	int ret, i;
+	int ret;
 
 	pr_info("starting recovery...\n");
 
 	drm_modeset_acquire_init(&ctx, 0);
-
-	rcv_state = _duplicate_active_crtc_state(crtc, &ctx);
-	if (IS_ERR(rcv_state)) {
-		ret = PTR_ERR(rcv_state);
-		goto out_drop_locks;
-	}
-
-	state = drm_atomic_state_alloc(dev);
-	if (!state) {
+	rcv_state = exynos_crtc_suspend(crtc, &ctx);
+	if (!IS_ERR_OR_NULL(rcv_state)) {
+		ret = exynos_crtc_resume(rcv_state, &ctx);
 		drm_atomic_state_put(rcv_state);
-		ret = -ENOMEM;
-		goto out_drop_locks;
+	} else {
+		ret = -EINVAL;
 	}
 
-retry:
-	state->acquire_ctx = &ctx;
-
-	crtc_state = drm_atomic_get_crtc_state(state, crtc);
-	if (IS_ERR(crtc_state)) {
-		ret = PTR_ERR(crtc_state);
-		goto out;
+	if (!ret) {
+		recovery->count++;
+		pr_info("recovery is successfully finished(%d)\n", recovery->count);
+	} else {
+		pr_err("Failed to recover display\n");
 	}
-
-	crtc_state->active = false;
-
-	ret = drm_atomic_set_mode_prop_for_crtc(crtc_state, NULL);
-	if (ret)
-		goto out;
-
-	ret = drm_atomic_add_affected_planes(state, crtc);
-	if (ret)
-		goto out;
-
-	ret = drm_atomic_add_affected_connectors(state, crtc);
-	if (ret)
-		goto out;
-
-	for_each_new_connector_in_state(state, conn, conn_state, i) {
-		ret = drm_atomic_set_crtc_for_connector(conn_state, NULL);
-		if (ret)
-			goto out;
-	}
-
-	for_each_new_plane_in_state(state, plane, plane_state, i) {
-		ret = drm_atomic_set_crtc_for_plane(plane_state, NULL);
-		if (ret)
-			goto out;
-
-		drm_atomic_set_fb_for_plane(plane_state, NULL);
-	}
-
-	ret = drm_atomic_commit(state);
-	if (ret)
-		goto out;
-
-	ret = drm_atomic_helper_commit_duplicated_state(rcv_state, &ctx);
-	if (ret)
-		goto out;
-
-	recovery->count++;
-	pr_info("recovery is successfully finished(%d)\n", recovery->count);
-
-out:
-	if (ret == -EDEADLK) {
-		drm_atomic_state_clear(state);
-		drm_atomic_state_clear(rcv_state);
-		ret = drm_modeset_backoff(&ctx);
-		if (!ret)
-			goto retry;
-	}
-
-	drm_atomic_state_put(state);
-	drm_atomic_state_put(rcv_state);
-
-out_drop_locks:
 	atomic_set(&recovery->recovering, 0);
 	drm_modeset_drop_locks(&ctx);
 	drm_modeset_acquire_fini(&ctx);