From 0d6e7e269af46ddbef559a77ee02664fdb54fdf3 Mon Sep 17 00:00:00 2001 From: Shirisha Kollapuram Date: Tue, 16 Aug 2022 23:47:46 +0530 Subject: [PATCH] disp: msm: sde: program the start window based on "EPT_FPS" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new connector property called “EPT_FPS” for the cmd mode panels. User space will set the “EPT_FPS” based on the intended content fps, relative to the last retire fence timestamp as calculated by Surface flinger. Program start window based on the Expected Present Time fps. Change-Id: I24b93e0f941af9fb2422b2484328254d04a1acbe Signed-off-by: Shirisha Kollapuram Signed-off-by: Veera Sundaram Sankaran --- msm/msm_drv.h | 1 + msm/sde/sde_connector.c | 19 ++- msm/sde/sde_connector.h | 2 + msm/sde/sde_encoder.c | 13 +- msm/sde/sde_encoder_phys_cmd.c | 217 ++++++++++++++++++--------------- msm/sde/sde_hw_catalog.h | 2 + msm/sde/sde_hw_intf.c | 1 + 7 files changed, 156 insertions(+), 99 deletions(-) diff --git a/msm/msm_drv.h b/msm/msm_drv.h index aaa011c331..e0748b70ef 100644 --- a/msm/msm_drv.h +++ b/msm/msm_drv.h @@ -248,6 +248,7 @@ enum msm_mdp_conn_property { CONNECTOR_PROP_SET_PANEL_MODE, CONNECTOR_PROP_AVR_STEP_STATE, CONNECTOR_PROP_EPT, + CONNECTOR_PROP_EPT_FPS, CONNECTOR_PROP_CACHE_STATE, CONNECTOR_PROP_DSC_MODE, CONNECTOR_PROP_WB_USAGE_TYPE, diff --git a/msm/sde/sde_connector.c b/msm/sde/sde_connector.c index 754ffd316e..c0d62f479a 100644 --- a/msm/sde/sde_connector.c +++ b/msm/sde/sde_connector.c @@ -854,7 +854,7 @@ void sde_connector_set_qsync_params(struct drm_connector *connector) { struct sde_connector *c_conn; struct sde_connector_state *c_state; - u32 qsync_propval = 0; + u32 qsync_propval = 0, ept_fps = 0; bool prop_dirty; if (!connector) @@ -882,6 +882,16 @@ void sde_connector_set_qsync_params(struct drm_connector *connector) CONNECTOR_PROP_AVR_STEP_STATE); if (prop_dirty) c_conn->qsync_updated = true; + + prop_dirty = msm_property_is_dirty(&c_conn->property_info, &c_state->property_state, + CONNECTOR_PROP_EPT_FPS); + if (prop_dirty) { + ept_fps = sde_connector_get_property(c_conn->base.state, CONNECTOR_PROP_EPT_FPS); + if (ept_fps != c_conn->ept_fps) { + c_conn->qsync_updated = true; + c_conn->ept_fps = ept_fps; + } + } } void sde_connector_complete_qsync_commit(struct drm_connector *conn, @@ -1794,6 +1804,8 @@ static int sde_connector_atomic_set_property(struct drm_connector *connector, SDE_ERROR_CONN(c_conn, "cannot set hdr info %d\n", rc); break; case CONNECTOR_PROP_QSYNC_MODE: + case CONNECTOR_PROP_AVR_STEP_STATE: + case CONNECTOR_PROP_EPT_FPS: msm_property_set_dirty(&c_conn->property_info, &c_state->property_state, idx); break; @@ -3077,6 +3089,11 @@ static void _sde_connector_install_qsync_properties(struct sde_kms *sde_kms, msm_property_install_enum(&c_conn->property_info, "avr_step_state", 0, 0, e_avr_step_state, ARRAY_SIZE(e_avr_step_state), 0, CONNECTOR_PROP_AVR_STEP_STATE); + + if (test_bit(SDE_FEATURE_EPT_FPS, sde_kms->catalog->features) && + (display_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE)) + msm_property_install_range(&c_conn->property_info, + "EPT_FPS", 0x0, 0, U32_MAX, 0, CONNECTOR_PROP_EPT_FPS); } } diff --git a/msm/sde/sde_connector.h b/msm/sde/sde_connector.h index 2f5781a865..89d82be9d6 100644 --- a/msm/sde/sde_connector.h +++ b/msm/sde/sde_connector.h @@ -567,6 +567,7 @@ struct sde_misr_sign { * @dimming_bl_notify_enabled: Flag to indicate if dimming bl notify is enabled or not * @qsync_mode: Cached Qsync mode, 0=disabled, 1=continuous mode * @qsync_updated: Qsync settings were updated + * @ept_fps: ept fps is updated, 0 means ept_fps is disabled * @colorspace_updated: Colorspace property was updated * @last_cmd_tx_sts: status of the last command transfer * @hdr_capable: external hdr support present @@ -641,6 +642,7 @@ struct sde_connector { u8 hdr_plus_app_ver; u32 qsync_mode; bool qsync_updated; + u32 ept_fps; bool colorspace_updated; diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index 2f7e704286..d52ced8648 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -4659,10 +4659,12 @@ void _sde_encoder_delay_kickoff_processing(struct sde_encoder_virt *sde_enc) ktime_t current_ts, ept_ts; u32 avr_step_fps, min_fps = 0, qsync_mode; u64 timeout_us = 0, ept; + bool is_cmd_mode; struct drm_connector *drm_conn; struct msm_mode_info *info = &sde_enc->mode_info; + struct sde_kms *sde_kms = sde_encoder_get_kms(&sde_enc->base); - if (!sde_enc->cur_master || !sde_enc->cur_master->connector) + if (!sde_enc->cur_master || !sde_enc->cur_master->connector || !sde_kms) return; drm_conn = sde_enc->cur_master->connector; @@ -4675,6 +4677,15 @@ void _sde_encoder_delay_kickoff_processing(struct sde_encoder_virt *sde_enc) _sde_encoder_get_qsync_fps_callback(&sde_enc->base, &min_fps, drm_conn->state); /* use min qsync fps, if feature is enabled; otherwise min default fps */ min_fps = min_fps ? min_fps : DEFAULT_MIN_FPS; + is_cmd_mode = sde_encoder_check_curr_mode(&sde_enc->base, MSM_DISPLAY_CMD_MODE); + + /* for cmd mode with qsync - EPT_FPS will be used to delay the processing */ + if (test_bit(SDE_FEATURE_EPT_FPS, sde_kms->catalog->features) + && is_cmd_mode && qsync_mode) { + SDE_DEBUG("enc:%d, ept:%llu not applicable for cmd mode with qsync enabled", + DRMID(&sde_enc->base), ept); + return; + } avr_step_fps = info->avr_step_fps; current_ts = ktime_get_ns(); diff --git a/msm/sde/sde_encoder_phys_cmd.c b/msm/sde/sde_encoder_phys_cmd.c index 87feb16eb1..6ec2a39c14 100644 --- a/msm/sde/sde_encoder_phys_cmd.c +++ b/msm/sde/sde_encoder_phys_cmd.c @@ -1296,99 +1296,127 @@ void sde_encoder_phys_cmd_irq_control(struct sde_encoder_phys *phys_enc, } } -static int _get_tearcheck_threshold(struct sde_encoder_phys *phys_enc) +static void _get_tearcheck_cfg(struct sde_encoder_phys *phys_enc, + u32 *t_lines, u32 *c_height, u32 *s_pos) { struct drm_connector *conn = phys_enc->connector; - u32 qsync_mode; - struct drm_display_mode *mode; - u32 threshold_lines, adjusted_threshold_lines; - struct sde_encoder_phys_cmd *cmd_enc = - to_sde_encoder_phys_cmd(phys_enc); - struct sde_encoder_virt *sde_enc; - struct msm_mode_info *info; + struct sde_encoder_phys_cmd *cmd_enc = to_sde_encoder_phys_cmd(phys_enc); + struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(phys_enc->parent); + struct msm_mode_info *info = &sde_enc->mode_info; + struct drm_display_mode *mode = &phys_enc->cached_mode; + enum sde_rm_qsync_modes qsync_mode; + ktime_t qsync_time_ns, default_time_ns, default_line_time_ns, ept_time_ns; + ktime_t extra_time_ns = 0, ept_extra_time_ns = 0, qsync_l_bound_ns, qsync_u_bound_ns; + u32 threshold_lines, ept_threshold_lines = 0, yres; + u32 default_fps, qsync_min_fps = 0, ept_fps = 0; + u32 adjusted_threshold_lines, cfg_height, start_pos; - if (!conn || !conn->state) - return 0; + *t_lines = *c_height = *s_pos = 0; + if (!conn || !conn->state || !phys_enc->sde_kms) + return; + + /* + * By setting sync_cfg_height to near max register value, we essentially + * disable sde hw generated TE signal, since hw TE will arrive first. + * Only caveat is if due to error, we hit wrap-around. + */ + if (phys_enc->hw_intf->ops.is_te_32bit_supported + && phys_enc->hw_intf->ops.is_te_32bit_supported(phys_enc->hw_intf)) + cfg_height = 0xFFFFFFF0; + else + cfg_height = 0xFFF0; + + adjusted_threshold_lines = DEFAULT_TEARCHECK_SYNC_THRESH_START; + start_pos = mode->vdisplay; + + yres = mode->vtotal; + default_fps = drm_mode_vrefresh(mode); - sde_enc = to_sde_encoder_virt(phys_enc->parent); - info = &sde_enc->mode_info; - mode = &phys_enc->cached_mode; qsync_mode = sde_connector_get_qsync_mode(conn); - threshold_lines = adjusted_threshold_lines = DEFAULT_TEARCHECK_SYNC_THRESH_START; + if (qsync_mode != SDE_RM_QSYNC_CONTINUOUS_MODE) + goto exit; - if (mode && (qsync_mode == SDE_RM_QSYNC_CONTINUOUS_MODE)) { - u32 qsync_min_fps = 0; - ktime_t qsync_time_ns; - ktime_t qsync_l_bound_ns, qsync_u_bound_ns; - u32 default_fps = drm_mode_vrefresh(mode); - ktime_t default_time_ns; - ktime_t default_line_time_ns; - ktime_t extra_time_ns; - u32 yres = mode->vtotal; + if (phys_enc->parent_ops.get_qsync_fps) + phys_enc->parent_ops.get_qsync_fps(phys_enc->parent, &qsync_min_fps, conn->state); - if (phys_enc->parent_ops.get_qsync_fps) - phys_enc->parent_ops.get_qsync_fps(phys_enc->parent, &qsync_min_fps, - conn->state); - - if (!qsync_min_fps || !default_fps || !yres) { - SDE_ERROR_CMDENC(cmd_enc, - "wrong qsync params %d %d %d\n", + if (!qsync_min_fps || !default_fps || !yres) { + SDE_ERROR_CMDENC(cmd_enc, "wrong qsync params %d %d %d\n", qsync_min_fps, default_fps, yres); - goto exit; - } - - if (qsync_min_fps >= default_fps) { - SDE_ERROR_CMDENC(cmd_enc, - "qsync fps:%d must be less than default:%d\n", + goto exit; + } else if (qsync_min_fps >= default_fps) { + SDE_ERROR_CMDENC(cmd_enc, "qsync fps:%d must be less than default:%d\n", qsync_min_fps, default_fps); - goto exit; - } - - /* - * Calculate safe qsync trigger window by compensating - * the qsync timeout period by panel jitter value. - * - * qsync_safe_window_period = qsync_timeout_period * (1 - jitter) - nominal_period - * nominal_line_time = nominal_period / vtotal - * qsync_safe_window_lines = qsync_safe_window_period / nominal_line_time - */ - qsync_time_ns = mult_frac(1000000000, 1, qsync_min_fps); - default_time_ns = mult_frac(1000000000, 1, default_fps); - - sde_encoder_helper_get_jitter_bounds_ns(qsync_min_fps, info->jitter_numer, - info->jitter_denom, &qsync_l_bound_ns, &qsync_u_bound_ns); - if (!qsync_l_bound_ns || !qsync_u_bound_ns) - qsync_l_bound_ns = qsync_u_bound_ns = qsync_time_ns; - - extra_time_ns = qsync_l_bound_ns - default_time_ns; - default_line_time_ns = mult_frac(1, default_time_ns, yres); - threshold_lines = mult_frac(1, extra_time_ns, default_line_time_ns); - - /* some DDICs express the timeout value in lines/4, round down to compensate */ - adjusted_threshold_lines = round_down(threshold_lines, 4); - /* remove 2 lines to cover for latency */ - if (adjusted_threshold_lines - 2 > DEFAULT_TEARCHECK_SYNC_THRESH_START) - adjusted_threshold_lines -= 2; - - SDE_DEBUG_CMDENC(cmd_enc, - "qsync mode:%u min_fps:%u time:%lld low:%lld up:%lld jitter:%u/%u\n", - qsync_mode, qsync_min_fps, qsync_time_ns, qsync_l_bound_ns, - qsync_u_bound_ns, info->jitter_numer, info->jitter_denom); - SDE_DEBUG_CMDENC(cmd_enc, - "default fps:%u time:%lld yres:%u line_time:%lld\n", - default_fps, default_time_ns, yres, default_line_time_ns); - SDE_DEBUG_CMDENC(cmd_enc, - "extra_time:%lld threshold_lines:%u adjusted_threshold_lines:%u\n", - extra_time_ns, threshold_lines, adjusted_threshold_lines); - - SDE_EVT32(qsync_mode, qsync_min_fps, default_fps, info->jitter_numer, - info->jitter_denom, yres, extra_time_ns, default_line_time_ns, - adjusted_threshold_lines); + goto exit; } -exit: + /* + * Calculate safe qsync trigger window by compensating + * the qsync timeout period by panel jitter value. + * + * qsync_safe_window_period = qsync_timeout_period * (1 - jitter) - nominal_period + * nominal_line_time = nominal_period / vtotal + * qsync_safe_window_lines = qsync_safe_window_period / nominal_line_time + */ + qsync_time_ns = mult_frac(NSEC_PER_SEC, 1, qsync_min_fps); + default_time_ns = mult_frac(NSEC_PER_SEC, 1, default_fps); - return adjusted_threshold_lines; + sde_encoder_helper_get_jitter_bounds_ns(qsync_min_fps, info->jitter_numer, + info->jitter_denom, &qsync_l_bound_ns, &qsync_u_bound_ns); + if (!qsync_l_bound_ns || !qsync_u_bound_ns) + qsync_l_bound_ns = qsync_u_bound_ns = qsync_time_ns; + + extra_time_ns = qsync_l_bound_ns - default_time_ns; + default_line_time_ns = mult_frac(1, default_time_ns, yres); + threshold_lines = mult_frac(1, extra_time_ns, default_line_time_ns); + + /* some DDICs express the timeout value in lines/4, round down to compensate */ + adjusted_threshold_lines = round_down(threshold_lines, 4); + /* remove 2 lines to cover for latency */ + if (adjusted_threshold_lines - 2 > DEFAULT_TEARCHECK_SYNC_THRESH_START) + adjusted_threshold_lines -= 2; + + /* override cfg_height & start_pos only if EPT_FPS feature is enabled */ + if (test_bit(SDE_FEATURE_EPT_FPS, phys_enc->sde_kms->catalog->features)) { + cfg_height -= (start_pos + threshold_lines); + + ept_fps = sde_connector_get_property(conn->state, CONNECTOR_PROP_EPT_FPS); + if (!ept_fps) { + goto end; + } else if (ept_fps > default_fps) { + SDE_ERROR_CMDENC(cmd_enc, "EPT fps:%d must be less than default:%d\n", + ept_fps, default_fps); + goto end; + } + + /* override start_pos, only when ept_fps is valid */ + ept_time_ns = mult_frac(NSEC_PER_SEC, 1, ept_fps); + ept_extra_time_ns = ept_time_ns - default_time_ns; + ept_threshold_lines = mult_frac(1, ept_extra_time_ns, default_line_time_ns); + start_pos += ept_threshold_lines; + } + +end: + SDE_DEBUG_CMDENC(cmd_enc, + "qsync mode:%u min_fps:%u ts:%llu jitter_ns:%llu/%llu jitter:%u/%u\n", + qsync_mode, qsync_min_fps, qsync_time_ns, qsync_l_bound_ns, + qsync_u_bound_ns, info->jitter_numer, info->jitter_denom); + SDE_DEBUG_CMDENC(cmd_enc, "default fps:%u ts:%llu yres:%u line_time:%llu extra_time:%llu\n", + default_fps, default_time_ns, yres, default_line_time_ns, extra_time_ns); + SDE_DEBUG_CMDENC(cmd_enc, "ept_fps:%d ts:%llu ept_extra_time:%llu ept_threshold_lines:%u\n", + ept_fps, ept_time_ns, ept_extra_time_ns, ept_threshold_lines); + SDE_DEBUG_CMDENC(cmd_enc, "threshold_lines:%u cfg_height:%u start_pos:%u\n", + adjusted_threshold_lines, cfg_height, start_pos); + + SDE_EVT32(qsync_mode, qsync_min_fps, default_fps, info->jitter_numer, + info->jitter_denom, yres, extra_time_ns, default_line_time_ns, + adjusted_threshold_lines, start_pos, cfg_height, ept_fps); + +exit: + *t_lines = adjusted_threshold_lines; + *c_height = cfg_height; + *s_pos = start_pos; + + return; } static void sde_encoder_phys_cmd_tearcheck_config( @@ -1399,7 +1427,7 @@ static void sde_encoder_phys_cmd_tearcheck_config( struct sde_hw_tear_check tc_cfg = { 0 }; struct drm_display_mode *mode; bool tc_enable = true; - u32 vsync_hz; + u32 vsync_hz, threshold, cfg_height, start_pos; int vrefresh; struct msm_drm_private *priv; struct sde_kms *sde_kms; @@ -1458,21 +1486,13 @@ static void sde_encoder_phys_cmd_tearcheck_config( /* enable external TE after kickoff to avoid premature autorefresh */ tc_cfg.hw_vsync_mode = 0; - /* - * By setting sync_cfg_height to near max register value, we essentially - * disable sde hw generated TE signal, since hw TE will arrive first. - * Only caveat is if due to error, we hit wrap-around. - */ - if (phys_enc->hw_intf->ops.is_te_32bit_supported - && phys_enc->hw_intf->ops.is_te_32bit_supported(phys_enc->hw_intf)) - tc_cfg.sync_cfg_height = 0xFFFFFFF0; - else - tc_cfg.sync_cfg_height = 0xFFF0; + _get_tearcheck_cfg(phys_enc, &threshold, &cfg_height, &start_pos); + tc_cfg.sync_threshold_start = threshold; + tc_cfg.sync_cfg_height = cfg_height; + tc_cfg.start_pos = start_pos; tc_cfg.vsync_init_val = mode->vdisplay; - tc_cfg.sync_threshold_start = _get_tearcheck_threshold(phys_enc); tc_cfg.sync_threshold_continue = DEFAULT_TEARCHECK_SYNC_THRESH_CONTINUE; - tc_cfg.start_pos = mode->vdisplay; tc_cfg.rd_ptr_irq = mode->vdisplay + 1; tc_cfg.wr_ptr_irq = 1; cmd_enc->qsync_threshold_lines = tc_cfg.sync_threshold_start; @@ -1823,8 +1843,11 @@ static int sde_encoder_phys_cmd_prepare_for_kickoff( } if (sde_connector_is_qsync_updated(phys_enc->connector)) { - tc_cfg.sync_threshold_start = _get_tearcheck_threshold( - phys_enc); + u32 threshold, cfg_height, start_pos; + + _get_tearcheck_cfg(phys_enc, &threshold, &cfg_height, &start_pos); + tc_cfg.sync_threshold_start = threshold; + tc_cfg.start_pos = start_pos; cmd_enc->qsync_threshold_lines = tc_cfg.sync_threshold_start; if (phys_enc->has_intf_te && phys_enc->hw_intf->ops.update_tearcheck) @@ -1833,7 +1856,7 @@ static int sde_encoder_phys_cmd_prepare_for_kickoff( else if (phys_enc->hw_pp->ops.update_tearcheck) phys_enc->hw_pp->ops.update_tearcheck( phys_enc->hw_pp, &tc_cfg); - SDE_EVT32(DRMID(phys_enc->parent), tc_cfg.sync_threshold_start); + SDE_EVT32(DRMID(phys_enc->parent), tc_cfg.sync_threshold_start, tc_cfg.start_pos); } sde_enc = to_sde_encoder_virt(phys_enc->parent); diff --git a/msm/sde/sde_hw_catalog.h b/msm/sde/sde_hw_catalog.h index 3b05df5d0d..142c0cbb70 100644 --- a/msm/sde/sde_hw_catalog.h +++ b/msm/sde/sde_hw_catalog.h @@ -768,6 +768,7 @@ enum { * @SDE_FEATURE_SUI_NS_ALLOWED SecureUI allowed to access non-secure context banks * @SDE_FEATURE_TRUSTED_VM Trusted VM supported * @SDE_FEATURE_EPT Expected present time supported + * @SDE_FEATURE_EPT_FPS Expected fps supported for cmd mode * @SDE_FEATURE_UBWC_STATS UBWC statistics supported * @SDE_FEATURE_VBIF_CLK_SPLIT VBIF clock split supported * @SDE_FEATURE_CTL_DONE Support for CTL DONE irq @@ -815,6 +816,7 @@ enum sde_mdss_features { SDE_FEATURE_SUI_NS_ALLOWED, SDE_FEATURE_TRUSTED_VM, SDE_FEATURE_EPT, + SDE_FEATURE_EPT_FPS, SDE_FEATURE_UBWC_STATS, SDE_FEATURE_VBIF_CLK_SPLIT, SDE_FEATURE_CTL_DONE, diff --git a/msm/sde/sde_hw_intf.c b/msm/sde/sde_hw_intf.c index ecfcf50faf..13457393f8 100644 --- a/msm/sde/sde_hw_intf.c +++ b/msm/sde/sde_hw_intf.c @@ -863,6 +863,7 @@ static void sde_hw_intf_update_te(struct sde_hw_intf *intf, cfg &= ~0xFFFF; cfg |= te->sync_threshold_start; SDE_REG_WRITE(c, INTF_TEAR_SYNC_THRESH, cfg); + SDE_REG_WRITE(c, INTF_TEAR_START_POS, te->start_pos); } static int sde_hw_intf_connect_external_te(struct sde_hw_intf *intf,