summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMidas Chien <midaschieh@google.com>2022-11-23 21:49:47 +0800
committerMidas Chien <midaschieh@google.com>2022-12-15 10:51:41 +0800
commitfb505dd6385d5815a5a51f8c9d5014f9974fa9be (patch)
tree1bfd2d974d30c30a36078ed18e85196ce4d6a44b
parent0562415024fd01db08b54129fbf59f04be3ae73d (diff)
downloaddisplay-fb505dd6385d5815a5a51f8c9d5014f9974fa9be.tar.gz
panel: predict nearest te timestamp for mipi sync timing if possible
When doing mipi sync with frame update, that need to know TE timestamp as a reference point to calculate safe time window to send refresh rate commands. However if the last known TE over one vsync period, wait one vblank to get it will cause frame drop. Try to predict nearest TE timestamp if possible. Bug: 237832158 Test: refresh rate switch, LHBM Change-Id: I1abc953524d2b7dc1b99200e0c6c4c48b0d1e14f Signed-off-by: Midas Chien <midaschieh@google.com>
-rw-r--r--samsung/panel/panel-samsung-drv.c73
-rw-r--r--samsung/panel/panel-samsung-drv.h3
2 files changed, 73 insertions, 3 deletions
diff --git a/samsung/panel/panel-samsung-drv.c b/samsung/panel/panel-samsung-drv.c
index 7bf0767..80eb6f2 100644
--- a/samsung/panel/panel-samsung-drv.c
+++ b/samsung/panel/panel-samsung-drv.c
@@ -3360,6 +3360,59 @@ static u64 exynos_panel_vsync_start_time_us(u32 te_us, u32 te_period_us)
return te_period_us * 55 / 100;
}
+/* avoid accumulate te varaince cause predicted value is not accurate enough */
+#define ACCEPTABLE_TE_PERIOD_DETLA_NS (3 * NSEC_PER_SEC)
+static ktime_t exynos_panel_te_ts_prediction(struct exynos_panel *ctx, ktime_t last_te,
+ s64 since_last_te_us, u32 te_period_us)
+{
+ const struct exynos_panel_desc *desc = ctx->desc;
+ s64 rr_switch_delta_us;
+ u32 te_period_before_rr_switch_us;
+ u32 rr_switch_applied_duration;
+ 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 at first vsync after exiting AOD mode */
+ rr_switch_applied_duration = 0;
+ } else {
+ /* If vrr_switch_duration is not set that mean we don't know the new refresh rate
+ * take effect timing. It may take effect at first or second vsync after sending
+ * rr switch commands.
+ */
+ rr_switch_applied_duration = desc->vrr_switch_duration != 0 ?
+ (desc->vrr_switch_duration - 1) : 1;
+ }
+
+ if (rr_switch_delta_us < 0 && te_period_before_rr_switch_us != 0) {
+ ktime_t first_te_after_rr_switch;
+
+ te_period_delta_ns = ((-rr_switch_delta_us / te_period_before_rr_switch_us) + 1) *
+ te_period_before_rr_switch_us * NSEC_PER_USEC;
+
+ if (te_period_delta_ns < ACCEPTABLE_TE_PERIOD_DETLA_NS) {
+ first_te_after_rr_switch = last_te + te_period_delta_ns;
+ rr_switch_delta_us =
+ ktime_us_delta(first_te_after_rr_switch, ctx->last_rr_switch_ts);
+ }
+ }
+
+ if (rr_switch_delta_us > (rr_switch_applied_duration * te_period_before_rr_switch_us)) {
+ 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)
@@ -3368,6 +3421,7 @@ static void exynos_panel_check_mipi_sync_timing(struct drm_crtc *crtc,
int retry;
u64 left, right;
bool vblank_taken = false;
+ bool rr_applied = false;
if (WARN_ON(!current_mode))
return;
@@ -3402,7 +3456,7 @@ static void exynos_panel_check_mipi_sync_timing(struct drm_crtc *crtc,
retry = te_period_us / USEC_PER_MSEC + 1;
do {
- ktime_t last_te = 0, now;
+ ktime_t last_te = 0, now, predicted_te;
s64 since_last_te_us;
s64 rr_switch_delta_us;
s64 te_period_before_rr_switch_us;
@@ -3411,7 +3465,15 @@ static void exynos_panel_check_mipi_sync_timing(struct drm_crtc *crtc,
now = ktime_get();
since_last_te_us = ktime_us_delta(now, last_te);
/* Need a vblank as a reference point */
- if (since_last_te_us > te_period_us) {
+ predicted_te =
+ exynos_panel_te_ts_prediction(ctx, last_te, since_last_te_us, te_period_us);
+ if (predicted_te) {
+ DPU_ATRACE_BEGIN("predicted_te");
+ last_te = predicted_te;
+ since_last_te_us = ktime_us_delta(now, last_te);
+ rr_applied = true;
+ DPU_ATRACE_END("predicted_te");
+ } else if (since_last_te_us > te_period_us) {
DPU_ATRACE_BEGIN("time_window_wait_crtc");
if (vblank_taken || !drm_crtc_vblank_get(crtc)) {
drm_crtc_wait_one_vblank(crtc);
@@ -3430,7 +3492,8 @@ static void exynos_panel_check_mipi_sync_timing(struct drm_crtc *crtc,
*/
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 (rr_switch_delta_us > 0 && rr_switch_delta_us < te_period_before_rr_switch_us) {
+ if (rr_switch_delta_us > 0 && rr_switch_delta_us < te_period_before_rr_switch_us &&
+ !rr_applied) {
DPU_ATRACE_BEGIN("time_window_wait_crtc2");
if (vblank_taken || !drm_crtc_vblank_get(crtc)) {
drm_crtc_wait_one_vblank(crtc);
@@ -3549,6 +3612,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;
@@ -3599,6 +3663,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) {
@@ -3641,6 +3706,8 @@ static void exynos_panel_bridge_mode_set(struct drm_bridge *bridge,
if (old_mode && drm_mode_vrefresh(&pmode->mode) != drm_mode_vrefresh(&old_mode->mode)) {
ctx->last_rr_switch_ts = ktime_get();
ctx->last_rr = drm_mode_vrefresh(&old_mode->mode);
+ if (come_out_lp_mode)
+ ctx->last_lp_exit_ts = ctx->last_rr_switch_ts;
}
mutex_unlock(&ctx->mode_lock);
diff --git a/samsung/panel/panel-samsung-drv.h b/samsung/panel/panel-samsung-drv.h
index 1f23297..f42d586 100644
--- a/samsung/panel/panel-samsung-drv.h
+++ b/samsung/panel/panel-samsung-drv.h
@@ -484,6 +484,7 @@ struct exynos_panel_desc {
bool is_partial;
bool is_panel_idle_supported;
bool no_lhbm_rr_constraints;
+ bool mipi_sync_te_prediction;
const u32 lhbm_effective_delay_frames;
const unsigned int delay_dsc_reg_init_us;
const struct brightness_capability *brt_capability;
@@ -625,6 +626,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;
struct {