diff options
author | Midas Chien <midaschieh@google.com> | 2023-04-10 02:28:35 +0000 |
---|---|---|
committer | Midas Chien <midaschieh@google.com> | 2023-05-05 01:43:05 +0000 |
commit | a436612d8bfe2b574b72c1ca5e680adee0638e5c (patch) | |
tree | 85780ee2fa218bdc09b83299f4eae16d65b93370 | |
parent | 3f40d8e5835b10bef3c39b70c1c8fb730a5c2ac2 (diff) | |
download | display-a436612d8bfe2b574b72c1ca5e680adee0638e5c.tar.gz |
Revert "Revert "panel: predict nearest te timestamp for mipi sync timing if possible""
This reverts commit e302f182ae12d5d5ce8cec93e5aefe8dfb4c866d.
Bug: 280727794
Test: refresh rate switch, LHBM
Change-Id: I1372996beaade519e49a6256b91c273c43bde4d2
Signed-off-by: Midas Chien <midaschieh@google.com>
-rw-r--r-- | samsung/panel/panel-samsung-drv.c | 105 | ||||
-rw-r--r-- | samsung/panel/panel-samsung-drv.h | 2 |
2 files changed, 107 insertions, 0 deletions
diff --git a/samsung/panel/panel-samsung-drv.c b/samsung/panel/panel-samsung-drv.c index 2f0417f..60dec97 100644 --- a/samsung/panel/panel-samsung-drv.c +++ b/samsung/panel/panel-samsung-drv.c @@ -3452,6 +3452,96 @@ void exynos_panel_wait_for_vsync_done(struct exynos_panel *ctx, u32 te_us, u32 p } EXPORT_SYMBOL(exynos_panel_wait_for_vsync_done); +/* avoid accumulate te varaince cause predicted value is not accurate enough */ +#define ACCEPTABLE_TE_PERIOD_DETLA_NS (3 * NSEC_PER_SEC) +#define ACCEPTABLE_TE_RR_SWITCH_DELTA_US (500) +static ktime_t exynos_panel_te_ts_prediction(struct exynos_panel *ctx, ktime_t last_te, + u32 te_period_us) +{ + const struct exynos_panel_desc *desc = ctx->desc; + s64 rr_switch_delta_us, te_last_rr_switch_delta_us; + u32 te_period_before_rr_switch_us; + u32 rr_switch_applied_te_count; + s64 te_period_delta_ns; + + if (!desc || last_te == 0) + return 0; + + rr_switch_delta_us = ktime_us_delta(last_te, ctx->last_rr_switch_ts); + te_period_before_rr_switch_us = ctx->last_rr != 0 ? USEC_PER_SEC / ctx->last_rr : 0; + + if (ctx->last_rr_switch_ts == ctx->last_lp_exit_ts) { + /* New refresh rate should take effect immediately after exiting AOD mode */ + rr_switch_applied_te_count = 1; + } else { + /* New rr will take effect at the first vsync after sending rr command, but we + * only know te rising ts. The worse case, new rr take effect at 2nd TE + */ + rr_switch_applied_te_count = 2; + } + + if (rr_switch_delta_us < 0 && te_period_before_rr_switch_us != 0) { + /* last know TE ts is before sending last rr switch */ + ktime_t last_te_before_rr_switch, now; + s64 since_last_te_us; + s64 accumlated_te_period_delta_ns; + + /* donʼt predict if last rr switch ts too close to te */ + te_last_rr_switch_delta_us = (-rr_switch_delta_us % te_period_before_rr_switch_us); + if (te_last_rr_switch_delta_us > + (te_period_before_rr_switch_us - ACCEPTABLE_TE_RR_SWITCH_DELTA_US) || + te_last_rr_switch_delta_us < ACCEPTABLE_TE_RR_SWITCH_DELTA_US) { + return 0; + } + + te_period_delta_ns = (-rr_switch_delta_us / te_period_before_rr_switch_us) * + te_period_before_rr_switch_us * NSEC_PER_USEC; + if (te_period_delta_ns < ACCEPTABLE_TE_PERIOD_DETLA_NS) { + /* try to get last TE ts before sending last rr switch command */ + ktime_t first_te_after_rr_switch; + + last_te_before_rr_switch = last_te + te_period_delta_ns; + now = ktime_get(); + since_last_te_us = ktime_us_delta(now, last_te_before_rr_switch); + if (since_last_te_us < te_period_before_rr_switch_us) { + /* now and last predict te is in the same te */ + return last_te_before_rr_switch; + } + + first_te_after_rr_switch = + last_te_before_rr_switch + te_period_before_rr_switch_us; + + if (rr_switch_applied_te_count == 1) { + since_last_te_us = ktime_us_delta(now, first_te_after_rr_switch); + accumlated_te_period_delta_ns = te_period_delta_ns; + te_period_delta_ns = + (since_last_te_us / te_period_us) * + te_period_us * NSEC_PER_USEC; + accumlated_te_period_delta_ns += te_period_delta_ns; + if (accumlated_te_period_delta_ns < ACCEPTABLE_TE_PERIOD_DETLA_NS) + return (first_te_after_rr_switch + te_period_delta_ns); + } else { + return first_te_after_rr_switch; + } + } + } else if (rr_switch_delta_us > ((rr_switch_applied_te_count - 1) * + te_period_before_rr_switch_us)) { + /* new rr has already taken effect at last know TE ts */ + ktime_t now; + s64 since_last_te_us; + + now = ktime_get(); + since_last_te_us = ktime_us_delta(now, last_te); + te_period_delta_ns = + (since_last_te_us / te_period_us) * te_period_us * NSEC_PER_USEC; + + if (te_period_delta_ns < ACCEPTABLE_TE_PERIOD_DETLA_NS) + return (last_te + te_period_delta_ns); + } + + return 0; +} + static void exynos_panel_check_mipi_sync_timing(struct drm_crtc *crtc, const struct exynos_panel_mode *current_mode, struct exynos_panel *ctx) @@ -3506,6 +3596,17 @@ static void exynos_panel_check_mipi_sync_timing(struct drm_crtc *crtc, vblank_counter = drm_crtc_vblank_count_and_time(crtc, &last_te); now = ktime_get(); since_last_te_us = ktime_us_delta(now, last_te); + if (!vblank_taken) { + ktime_t predicted_te = exynos_panel_te_ts_prediction(ctx, last_te, + USEC_PER_SEC / drm_mode_vrefresh(¤t_mode->mode)); + if (predicted_te) { + DPU_ATRACE_BEGIN("predicted_te"); + last_te = predicted_te; + since_last_te_us = ktime_us_delta(now, last_te); + DPU_ATRACE_INT("predicted_te_delta_us", (int)since_last_te_us); + DPU_ATRACE_END("predicted_te"); + } + } /** * If a refresh rate switch happens right before last_te. last TE width could be for * new rr or for old rr depending on if last rr is sent at TE high or low. @@ -3644,6 +3745,7 @@ static void exynos_panel_bridge_mode_set(struct drm_bridge *bridge, const struct exynos_panel_funcs *funcs = ctx->desc->exynos_panel_func; const struct exynos_panel_mode *old_mode; bool need_update_backlight = false; + bool come_out_lp_mode = false; if (WARN_ON(!pmode)) return; @@ -3694,6 +3796,7 @@ static void exynos_panel_bridge_mode_set(struct drm_bridge *bridge, ctx->panel_state = PANEL_STATE_NORMAL; need_update_backlight = true; state_changed = true; + come_out_lp_mode = true; } ctx->current_binned_lp = NULL; } else if (funcs->mode_set) { @@ -3748,6 +3851,8 @@ static void exynos_panel_bridge_mode_set(struct drm_bridge *bridge, ctx->desc->exynos_panel_func->get_te_usec(ctx, old_mode); else ctx->last_rr_te_usec = old_mode->exynos_mode.te_usec; + if (come_out_lp_mode) + ctx->last_lp_exit_ts = ctx->last_rr_switch_ts; sysfs_notify(&ctx->dev->kobj, NULL, "refresh_rate"); } diff --git a/samsung/panel/panel-samsung-drv.h b/samsung/panel/panel-samsung-drv.h index db57213..b5b29ae 100644 --- a/samsung/panel/panel-samsung-drv.h +++ b/samsung/panel/panel-samsung-drv.h @@ -643,6 +643,8 @@ struct exynos_panel { * mean rr switch so it differs from last_mode_set_ts */ ktime_t last_rr_switch_ts; + /* Record the last come out lp mode timestamp */ + ktime_t last_lp_exit_ts; u32 last_rr; /* TE low or high when last rr was sent */ int last_rr_te_gpio_value; |