disp: msm: sde: program the start window based on "EPT_FPS"

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 <quic_kshirish@quicinc.com>
Signed-off-by: Veera Sundaram Sankaran <quic_veeras@quicinc.com>
This commit is contained in:
Shirisha Kollapuram
2022-08-16 23:47:46 +05:30
committed by Veera Sundaram Sankaran
parent 2e3ba9430c
commit 0d6e7e269a
7 changed files with 156 additions and 99 deletions

View File

@@ -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,

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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();

View File

@@ -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);

View File

@@ -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,

View File

@@ -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,