diff options
author | Ken Huang <kenbshuang@google.com> | 2023-08-11 23:42:23 +0800 |
---|---|---|
committer | Ken Huang <kenbshuang@google.com> | 2023-09-18 02:12:48 +0000 |
commit | 936bc28ebd1aa0149902f6de48396421bc318775 (patch) | |
tree | f8e74e58f661c8618418a30c90f76437c2ad3888 | |
parent | cc6241281f12b6109618586b410272df7c4bf560 (diff) | |
download | display-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.c | 135 | ||||
-rw-r--r-- | samsung/exynos_drm_crtc.h | 9 | ||||
-rw-r--r-- | samsung/exynos_drm_decon.c | 52 | ||||
-rw-r--r-- | samsung/exynos_drm_decon.h | 1 | ||||
-rw-r--r-- | samsung/exynos_drm_recovery.c | 141 |
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 ©->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); |