1
0

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>
Este cometimento está contido em:
Shirisha Kollapuram
2022-08-16 23:47:46 +05:30
cometido por Veera Sundaram Sankaran
ascendente 2e3ba9430c
cometimento 0d6e7e269a
7 ficheiros modificados com 156 adições e 99 eliminações

Ver ficheiro

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