diff options
author | PixelBot AutoMerger <android-nexus-securitybot@system.gserviceaccount.com> | 2023-09-24 18:50:55 -0700 |
---|---|---|
committer | Pindar Yang <pindaryang@google.com> | 2023-09-25 06:43:01 +0000 |
commit | 6009dfa0fbdc801644f9fff127c344f543c54715 (patch) | |
tree | 91679ed2adff617b1e0ae926249027c4df3af552 | |
parent | 209fc208b5d54e33e494a4ebe3024c37ab081309 (diff) | |
parent | f806f9f24145504d065e53ffa696963300016f6e (diff) | |
download | display-6009dfa0fbdc801644f9fff127c344f543c54715.tar.gz |
Merge android13-gs-pixel-5.10-udc-qpr1 into android13-gs-pixel-5.10-24Q1android-u-qpr2-beta-1_r0.6android-u-qpr2-beta-1_r0.5android-u-qpr2-beta-1_r0.4android-u-qpr2-beta-1_r0.3android-u-qpr2-beta-1_r0.2android-u-qpr2-beta-1_r0.1
Bug: 300854197
SBMerger: 558810260
Change-Id: Idbea22b483d0688d52fdde190e88dc23ad845b7a
Signed-off-by: SecurityBot <android-nexus-securitybot@system.gserviceaccount.com>
-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 | ||||
-rw-r--r-- | samsung/panel/panel-samsung-drv.c | 67 | ||||
-rw-r--r-- | samsung/panel/panel-samsung-drv.h | 18 | ||||
-rw-r--r-- | samsung/panel/panel-samsung-s6e3hc4.c | 10 |
8 files changed, 301 insertions, 132 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); diff --git a/samsung/panel/panel-samsung-drv.c b/samsung/panel/panel-samsung-drv.c index 71c802c..080c7dc 100644 --- a/samsung/panel/panel-samsung-drv.c +++ b/samsung/panel/panel-samsung-drv.c @@ -3305,12 +3305,73 @@ static int exynos_panel_bridge_atomic_check(struct drm_bridge *bridge, { struct exynos_panel *ctx = bridge_to_exynos_panel(bridge); struct drm_atomic_state *state = new_crtc_state->state; + const struct drm_display_mode *current_mode = &ctx->current_mode->mode; const struct exynos_panel_funcs *funcs = ctx->desc->exynos_panel_func; int ret; if (unlikely(!new_crtc_state)) return 0; + if (unlikely(!current_mode)) { + dev_warn(ctx->dev, "%s: failed to get current mode, skip mode check\n", __func__); + } else { + struct drm_display_mode *target_mode = &new_crtc_state->adjusted_mode; + int current_vrefresh = drm_mode_vrefresh(current_mode); + int target_vrefresh = drm_mode_vrefresh(target_mode); + + if (current_mode->hdisplay != target_mode->hdisplay && + current_mode->vdisplay != target_mode->vdisplay) { + if (current_vrefresh != target_vrefresh) { + /* + * While switching resolution and refresh rate (from high to low) in + * the same commit, the frame transfer time will become longer due + * to BTS update. In the case, frame done time may cross to the next + * vsync, which will hit DDIC’s constraint and cause the noises. + * Keep the current BTS (higher one) for a few frames to avoid the + * problem. + */ + if (current_vrefresh > target_vrefresh) { + target_mode->clock = + target_mode->htotal * target_mode->vtotal * + current_vrefresh / 1000; + if (target_mode->clock != new_crtc_state->mode.clock) { + new_crtc_state->mode_changed = true; + dev_dbg(ctx->dev, + "%s: keep mode (%s) clock %dhz on rrs\n", + __func__, target_mode->name, + current_vrefresh); + } + } + + ctx->mode_in_progress = MODE_RES_AND_RR_IN_PROGRESS; + } else { + ctx->mode_in_progress = MODE_RES_IN_PROGRESS; + } + } else { + if (ctx->mode_in_progress == MODE_RES_AND_RR_IN_PROGRESS && + new_crtc_state->adjusted_mode.clock != new_crtc_state->mode.clock) { + new_crtc_state->mode_changed = true; + new_crtc_state->adjusted_mode.clock = new_crtc_state->mode.clock; + dev_dbg(ctx->dev, "%s: restore mode (%s) clock after rrs\n", + __func__, new_crtc_state->mode.name); + } + + if (current_vrefresh != target_vrefresh) + ctx->mode_in_progress = MODE_RR_IN_PROGRESS; + else + ctx->mode_in_progress = MODE_DONE; + } + + if (current_mode->hdisplay != target_mode->hdisplay || + current_mode->vdisplay != target_mode->vdisplay || + current_vrefresh != target_vrefresh) + dev_dbg(ctx->dev, + "%s: current %dx%d@%d, target %dx%d@%d, type %d\n", __func__, + current_mode->hdisplay, current_mode->vdisplay, current_vrefresh, + target_mode->hdisplay, target_mode->vdisplay, target_vrefresh, + ctx->mode_in_progress); + } + if (funcs && funcs->atomic_check) { ret = funcs->atomic_check(ctx, state); if (ret) @@ -3441,14 +3502,17 @@ void exynos_panel_wait_for_vsync_done(struct exynos_panel *ctx, u32 te_us, u32 p { u32 delay_us; + DPU_ATRACE_BEGIN(__func__); if (unlikely(exynos_panel_wait_for_vblank(ctx))) { delay_us = period_us + 1000; usleep_range(delay_us, delay_us + 10); + DPU_ATRACE_END(__func__); return; } delay_us = exynos_panel_vsync_start_time_us(te_us, period_us); usleep_range(delay_us, delay_us + 10); + DPU_ATRACE_END(__func__); } EXPORT_SYMBOL(exynos_panel_wait_for_vsync_done); @@ -4200,6 +4264,9 @@ int exynos_panel_common_init(struct mipi_dsi_device *dsi, if (ret) dev_err(ctx->dev, "unable to create cabc_mode\n"); } + + ctx->mode_in_progress = MODE_DONE; + exynos_panel_handoff(ctx); ret = mipi_dsi_attach(dsi); diff --git a/samsung/panel/panel-samsung-drv.h b/samsung/panel/panel-samsung-drv.h index b5b29ae..1d2896b 100644 --- a/samsung/panel/panel-samsung-drv.h +++ b/samsung/panel/panel-samsung-drv.h @@ -529,6 +529,21 @@ enum local_hbm_enable_state { LOCAL_HBM_ENABLING, }; +/** + * enum mode_progress_type - the type while mode switch is in progress + * @MODE_DONE: mode switch is done + * @MODE_RES_IN_PROGRESS: mode switch is in progress, only resolution is changed + * @MODE_RR_IN_PROGRESS: mode switch is in progress, only refresh rate is changed + * @MODE_RES_AND_RR_IN_PROGRESS: mode switch is in progress, both resolution and + * refresh rate are changed + */ +enum mode_progress_type { + MODE_DONE = 0, + MODE_RES_IN_PROGRESS, + MODE_RR_IN_PROGRESS, + MODE_RES_AND_RR_IN_PROGRESS, +}; + struct exynos_bl_notifier { u32 ranges[MAX_BL_RANGES]; u32 num_ranges; @@ -673,6 +688,9 @@ struct exynos_panel { struct workqueue_struct *wq; } hbm; + + /* current type of mode switch */ + enum mode_progress_type mode_in_progress; }; /** diff --git a/samsung/panel/panel-samsung-s6e3hc4.c b/samsung/panel/panel-samsung-s6e3hc4.c index aab5519..bda692e 100644 --- a/samsung/panel/panel-samsung-s6e3hc4.c +++ b/samsung/panel/panel-samsung-s6e3hc4.c @@ -1028,6 +1028,7 @@ static int s6e3hc4_enable(struct drm_panel *panel) const struct drm_display_mode *mode; const bool needs_reset = !is_panel_enabled(ctx); bool is_fhd; + u32 vrefresh; if (!pmode) { dev_err(ctx->dev, "no current mode set\n"); @@ -1035,12 +1036,19 @@ static int s6e3hc4_enable(struct drm_panel *panel) } mode = &pmode->mode; is_fhd = mode->hdisplay == 1080; + vrefresh = needs_reset ? 60 : drm_mode_vrefresh(mode); dev_dbg(ctx->dev, "%s\n", __func__); if (needs_reset) exynos_panel_reset(ctx); + /* wait TE falling for RRS since DSC and framestart must in the same VSYNC */ + if (ctx->mode_in_progress == MODE_RES_IN_PROGRESS) + s6e3hc4_wait_for_vsync_done(ctx, vrefresh); + else if (ctx->mode_in_progress == MODE_RES_AND_RR_IN_PROGRESS) + s6e3hc4_wait_for_vsync_done(ctx, ctx->last_rr); + /* DSC related configuration */ EXYNOS_DCS_WRITE_SEQ(ctx, 0x9D, 0x01); EXYNOS_PPS_WRITE_BUF(ctx, is_fhd ? FHD_PPS_SETTING : WQHD_PPS_SETTING); @@ -1068,8 +1076,6 @@ static int s6e3hc4_enable(struct drm_panel *panel) s6e3hc4_change_frequency(ctx, pmode); if (needs_reset || (ctx->panel_state == PANEL_STATE_BLANK)) { - u32 vrefresh = needs_reset ? 60 : drm_mode_vrefresh(&pmode->mode); - s6e3hc4_wait_for_vsync_done(ctx, vrefresh); exynos_panel_send_cmd_set(ctx, &s6e3hc4_display_on_cmd_set); } |