disp: msm: sde: improve qsync trigger window accuracy
Currently, panel jitter and loss of precision are not compensated when calculating the trigger window size for a QSYNC panel. These errors can be signigicant on panels supporting very slow frame rate (10 Hz). This change improves fixed point calculation and take into account panel jitter when calculating the minimum qsync time period. Change-Id: Ibe620862afbd853580992fccec09cac8307b92bd Signed-off-by: Amine Najahi <quic_anajahi@quicinc.com>
This commit is contained in:

committed by
Gerrit - the friendly Code Review server

parent
83cb3d44af
commit
88a24f8c45
@@ -5622,24 +5622,15 @@ int sde_encoder_wait_for_event(struct drm_encoder *drm_enc,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sde_encoder_helper_get_jitter_bounds_ns(struct drm_encoder *drm_enc,
|
void sde_encoder_helper_get_jitter_bounds_ns(u32 frame_rate,
|
||||||
u64 *l_bound, u64 *u_bound)
|
u32 jitter_num, u32 jitter_denom,
|
||||||
|
ktime_t *l_bound, ktime_t *u_bound)
|
||||||
{
|
{
|
||||||
struct sde_encoder_virt *sde_enc;
|
ktime_t jitter_ns, frametime_ns;
|
||||||
u64 jitter_ns, frametime_ns;
|
|
||||||
struct msm_mode_info *info;
|
|
||||||
|
|
||||||
if (!drm_enc) {
|
frametime_ns = (1 * 1000000000) / frame_rate;
|
||||||
SDE_ERROR("invalid encoder\n");
|
jitter_ns = jitter_num * frametime_ns;
|
||||||
return;
|
do_div(jitter_ns, jitter_denom * 100);
|
||||||
}
|
|
||||||
|
|
||||||
sde_enc = to_sde_encoder_virt(drm_enc);
|
|
||||||
info = &sde_enc->mode_info;
|
|
||||||
|
|
||||||
frametime_ns = (1 * 1000000000) / info->frame_rate;
|
|
||||||
jitter_ns = info->jitter_numer * frametime_ns;
|
|
||||||
do_div(jitter_ns, info->jitter_denom * 100);
|
|
||||||
|
|
||||||
*l_bound = frametime_ns - jitter_ns;
|
*l_bound = frametime_ns - jitter_ns;
|
||||||
*u_bound = frametime_ns + jitter_ns;
|
*u_bound = frametime_ns + jitter_ns;
|
||||||
|
@@ -607,10 +607,15 @@ int sde_encoder_helper_wait_event_timeout(
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* sde_encoder_get_fps - get the allowed panel jitter in nanoseconds
|
* sde_encoder_get_fps - get the allowed panel jitter in nanoseconds
|
||||||
* @encoder: Pointer to drm encoder object
|
* @frame_rate: custom input frame rate
|
||||||
|
* @jitter_num: jitter numerator value
|
||||||
|
* @jitter_denom: jitter denomerator value,
|
||||||
|
* @l_bound: lower frame period boundary
|
||||||
|
* @u_bound: upper frame period boundary
|
||||||
*/
|
*/
|
||||||
void sde_encoder_helper_get_jitter_bounds_ns(struct drm_encoder *encoder,
|
void sde_encoder_helper_get_jitter_bounds_ns(uint32_t frame_rate,
|
||||||
u64 *l_bound, u64 *u_bound);
|
u32 jitter_num, u32 jitter_denom,
|
||||||
|
ktime_t *l_bound, ktime_t *u_bound);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sde_encoder_helper_switch_vsync - switch vsync source to WD or default
|
* sde_encoder_helper_switch_vsync - switch vsync source to WD or default
|
||||||
|
@@ -197,8 +197,8 @@ static void sde_encoder_override_tearcheck_rd_ptr(struct sde_encoder_phys *phys_
|
|||||||
|
|
||||||
sde_encoder_helper_get_pp_line_count(phys_enc->parent, info);
|
sde_encoder_helper_get_pp_line_count(phys_enc->parent, info);
|
||||||
SDE_EVT32_VERBOSE(phys_enc->hw_intf->idx - INTF_0, mode->vdisplay,
|
SDE_EVT32_VERBOSE(phys_enc->hw_intf->idx - INTF_0, mode->vdisplay,
|
||||||
cmd_enc->qsync_threshold_lines, info[0].rd_ptr_line_count,
|
cmd_enc->qsync_threshold_lines, adjusted_tear_rd_ptr_line_cnt,
|
||||||
info[0].rd_ptr_frame_count, info[0].wr_ptr_line_count,
|
info[0].rd_ptr_line_count, info[0].rd_ptr_frame_count, info[0].wr_ptr_line_count,
|
||||||
info[1].rd_ptr_line_count, info[1].rd_ptr_frame_count, info[1].wr_ptr_line_count);
|
info[1].rd_ptr_line_count, info[1].rd_ptr_frame_count, info[1].wr_ptr_line_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -987,28 +987,34 @@ static int _get_tearcheck_threshold(struct sde_encoder_phys *phys_enc)
|
|||||||
struct drm_connector *conn = phys_enc->connector;
|
struct drm_connector *conn = phys_enc->connector;
|
||||||
u32 qsync_mode;
|
u32 qsync_mode;
|
||||||
struct drm_display_mode *mode;
|
struct drm_display_mode *mode;
|
||||||
u32 threshold_lines = DEFAULT_TEARCHECK_SYNC_THRESH_START;
|
u32 threshold_lines, adjusted_threshold_lines;
|
||||||
struct sde_encoder_phys_cmd *cmd_enc =
|
struct sde_encoder_phys_cmd *cmd_enc =
|
||||||
to_sde_encoder_phys_cmd(phys_enc);
|
to_sde_encoder_phys_cmd(phys_enc);
|
||||||
|
struct sde_encoder_virt *sde_enc;
|
||||||
|
struct msm_mode_info *info;
|
||||||
|
|
||||||
if (!conn || !conn->state)
|
if (!conn || !conn->state)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
sde_enc = to_sde_encoder_virt(phys_enc->parent);
|
||||||
|
info = &sde_enc->mode_info;
|
||||||
mode = &phys_enc->cached_mode;
|
mode = &phys_enc->cached_mode;
|
||||||
qsync_mode = sde_connector_get_qsync_mode(conn);
|
qsync_mode = sde_connector_get_qsync_mode(conn);
|
||||||
|
threshold_lines = adjusted_threshold_lines = DEFAULT_TEARCHECK_SYNC_THRESH_START;
|
||||||
|
|
||||||
if (mode && (qsync_mode == SDE_RM_QSYNC_CONTINUOUS_MODE)) {
|
if (mode && (qsync_mode == SDE_RM_QSYNC_CONTINUOUS_MODE)) {
|
||||||
u32 qsync_min_fps = 0;
|
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);
|
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;
|
u32 yres = mode->vtotal;
|
||||||
u32 slow_time_ns;
|
|
||||||
u32 default_time_ns;
|
|
||||||
u32 extra_time_ns;
|
|
||||||
u32 default_line_time_ns;
|
|
||||||
|
|
||||||
if (phys_enc->parent_ops.get_qsync_fps)
|
if (phys_enc->parent_ops.get_qsync_fps)
|
||||||
phys_enc->parent_ops.get_qsync_fps(
|
phys_enc->parent_ops.get_qsync_fps(phys_enc->parent, &qsync_min_fps,
|
||||||
phys_enc->parent, &qsync_min_fps, conn->state);
|
conn->state);
|
||||||
|
|
||||||
if (!qsync_min_fps || !default_fps || !yres) {
|
if (!qsync_min_fps || !default_fps || !yres) {
|
||||||
SDE_ERROR_CMDENC(cmd_enc,
|
SDE_ERROR_CMDENC(cmd_enc,
|
||||||
@@ -1024,32 +1030,51 @@ static int _get_tearcheck_threshold(struct sde_encoder_phys *phys_enc)
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Calculate the number of extra lines*/
|
/*
|
||||||
slow_time_ns = DIV_ROUND_UP(1000000000, qsync_min_fps);
|
* Calculate safe qsync trigger window by compensating
|
||||||
default_time_ns = DIV_ROUND_UP(1000000000, default_fps);
|
* the qsync timeout period by panel jitter value.
|
||||||
extra_time_ns = slow_time_ns - default_time_ns;
|
*
|
||||||
default_line_time_ns = DIV_ROUND_UP(default_time_ns, yres);
|
* 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);
|
||||||
|
|
||||||
threshold_lines = extra_time_ns / default_line_time_ns;
|
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 */
|
/* some DDICs express the timeout value in lines/4, round down to compensate */
|
||||||
threshold_lines = round_down(threshold_lines, 4);
|
adjusted_threshold_lines = round_down(threshold_lines, 4);
|
||||||
/* remove 2 lines to cover for latency */
|
/* remove 2 lines to cover for latency */
|
||||||
if (threshold_lines - 2 > DEFAULT_TEARCHECK_SYNC_THRESH_START)
|
if (adjusted_threshold_lines - 2 > DEFAULT_TEARCHECK_SYNC_THRESH_START)
|
||||||
threshold_lines -= 2;
|
adjusted_threshold_lines -= 2;
|
||||||
|
|
||||||
SDE_DEBUG_CMDENC(cmd_enc, "slow:%d default:%d extra:%d(ns)\n",
|
SDE_DEBUG_CMDENC(cmd_enc,
|
||||||
slow_time_ns, default_time_ns, extra_time_ns);
|
"qsync mode:%u min_fps:%u time:%lld low:%lld up:%lld jitter:%u/%u\n",
|
||||||
SDE_DEBUG_CMDENC(cmd_enc, "min_fps:%d fps:%d yres:%d lines:%d\n",
|
qsync_mode, qsync_min_fps, qsync_time_ns, qsync_l_bound_ns,
|
||||||
qsync_min_fps, default_fps, yres, threshold_lines);
|
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, extra_time_ns, default_fps,
|
SDE_EVT32(qsync_mode, qsync_min_fps, default_fps, info->jitter_numer,
|
||||||
yres, threshold_lines);
|
info->jitter_denom, yres, extra_time_ns, default_line_time_ns,
|
||||||
|
adjusted_threshold_lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
|
|
||||||
return threshold_lines;
|
return adjusted_threshold_lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sde_encoder_phys_cmd_tearcheck_config(
|
static void sde_encoder_phys_cmd_tearcheck_config(
|
||||||
@@ -1485,17 +1510,22 @@ static int sde_encoder_phys_cmd_prepare_for_kickoff(
|
|||||||
static bool _sde_encoder_phys_cmd_needs_vsync_change(
|
static bool _sde_encoder_phys_cmd_needs_vsync_change(
|
||||||
struct sde_encoder_phys *phys_enc, ktime_t profile_timestamp)
|
struct sde_encoder_phys *phys_enc, ktime_t profile_timestamp)
|
||||||
{
|
{
|
||||||
|
struct sde_encoder_virt *sde_enc;
|
||||||
struct sde_encoder_phys_cmd *cmd_enc;
|
struct sde_encoder_phys_cmd *cmd_enc;
|
||||||
struct sde_encoder_phys_cmd_te_timestamp *cur;
|
struct sde_encoder_phys_cmd_te_timestamp *cur;
|
||||||
struct sde_encoder_phys_cmd_te_timestamp *prev = NULL;
|
struct sde_encoder_phys_cmd_te_timestamp *prev = NULL;
|
||||||
ktime_t time_diff;
|
ktime_t time_diff;
|
||||||
u64 l_bound = 0, u_bound = 0;
|
struct msm_mode_info *info;
|
||||||
|
ktime_t l_bound = 0, u_bound = 0;
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
unsigned long lock_flags;
|
unsigned long lock_flags;
|
||||||
|
|
||||||
cmd_enc = to_sde_encoder_phys_cmd(phys_enc);
|
cmd_enc = to_sde_encoder_phys_cmd(phys_enc);
|
||||||
sde_encoder_helper_get_jitter_bounds_ns(phys_enc->parent,
|
sde_enc = to_sde_encoder_virt(phys_enc->parent);
|
||||||
&l_bound, &u_bound);
|
info = &sde_enc->mode_info;
|
||||||
|
|
||||||
|
sde_encoder_helper_get_jitter_bounds_ns(info->frame_rate, info->jitter_numer,
|
||||||
|
info->jitter_denom, &l_bound, &u_bound);
|
||||||
if (!l_bound || !u_bound) {
|
if (!l_bound || !u_bound) {
|
||||||
SDE_ERROR_CMDENC(cmd_enc, "invalid vsync jitter bounds\n");
|
SDE_ERROR_CMDENC(cmd_enc, "invalid vsync jitter bounds\n");
|
||||||
return false;
|
return false;
|
||||||
|
Reference in New Issue
Block a user