summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKen Huang <kenbshuang@google.com>2023-08-11 23:42:23 +0800
committerKen Huang <kenbshuang@google.com>2023-09-18 02:12:48 +0000
commit936bc28ebd1aa0149902f6de48396421bc318775 (patch)
treef8e74e58f661c8618418a30c90f76437c2ad3888
parentcc6241281f12b6109618586b410272df7c4bf560 (diff)
downloaddisplay-936bc28ebd1aa0149902f6de48396421bc318775.tar.gz
drm: samsung: support to suspend a decon without hibernation
If a decon doesn't support hibernation, decon_suspend() will return directly and sometimes may cause power domain fails to suspend dpu. Bug: 296744776 Test: echo mem > /sys/power/state Test: echo 1 > /d/dri/0/crtc-0/recovery Change-Id: Ib0d9008bd335d1200581e17979b16a6bb877d93a Signed-off-by: Ken Huang <kenbshuang@google.com> (cherry picked from commit ce371a081aacdcf8a988bafbf19f75ebbff633e6)
-rw-r--r--samsung/exynos_drm_crtc.c135
-rw-r--r--samsung/exynos_drm_crtc.h9
-rw-r--r--samsung/exynos_drm_decon.c52
-rw-r--r--samsung/exynos_drm_decon.h1
-rw-r--r--samsung/exynos_drm_recovery.c141
5 files changed, 208 insertions, 130 deletions
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 @@ exynos_drm_crtc_duplicate_state(struct drm_crtc *crtc)
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 @@ bool exynos_crtc_needs_disable(struct drm_crtc_state *old_state,
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 @@ static int decon_runtime_resume(struct device *dev)
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 @@ static int decon_suspend(struct device *dev)
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_suspend(struct device *dev)
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 decon_device {
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);