diff --git a/msm/sde/sde_encoder_phys.h b/msm/sde/sde_encoder_phys.h index 0e719c8f0d..b11d53fd59 100644 --- a/msm/sde/sde_encoder_phys.h +++ b/msm/sde/sde_encoder_phys.h @@ -415,6 +415,7 @@ struct sde_encoder_phys_cmd_te_timestamp { * @wr_ptr_wait_success: log wr_ptr_wait success for release fence trigger * @te_timestamp_list: List head for the TE timestamp list * @te_timestamp: Array of size MAX_TE_PROFILE_COUNT te_timestamp_list elements + * @qsync_threshold_lines: tearcheck threshold lines calculated based on qsync_min_fps */ struct sde_encoder_phys_cmd { struct sde_encoder_phys base; @@ -427,6 +428,7 @@ struct sde_encoder_phys_cmd { struct list_head te_timestamp_list; struct sde_encoder_phys_cmd_te_timestamp te_timestamp[MAX_TE_PROFILE_COUNT]; + u32 qsync_threshold_lines; }; /** diff --git a/msm/sde/sde_encoder_phys_cmd.c b/msm/sde/sde_encoder_phys_cmd.c index 193c707dfb..42e8a0f39a 100644 --- a/msm/sde/sde_encoder_phys_cmd.c +++ b/msm/sde/sde_encoder_phys_cmd.c @@ -171,6 +171,37 @@ static void _sde_encoder_phys_cmd_update_intf_cfg( } } +static void sde_encoder_override_tearcheck_rd_ptr(struct sde_encoder_phys *phys_enc) +{ + struct sde_hw_intf *hw_intf; + struct drm_display_mode *mode; + struct sde_encoder_phys_cmd *cmd_enc; + struct sde_hw_pp_vsync_info info[MAX_CHANNELS_PER_ENC] = {{0}}; + u32 adjusted_tear_rd_ptr_line_cnt; + + if (!phys_enc || !phys_enc->hw_intf) + return; + + cmd_enc = to_sde_encoder_phys_cmd(phys_enc); + hw_intf = phys_enc->hw_intf; + mode = &phys_enc->cached_mode; + + /* Configure TE rd_ptr_val to the end of qsync Start Window. + * This ensures next frame trigger_start does not get latched in the current + * vsync window. + */ + adjusted_tear_rd_ptr_line_cnt = mode->vdisplay + cmd_enc->qsync_threshold_lines + 1; + + if (hw_intf && hw_intf->ops.override_tear_rd_ptr_val) + hw_intf->ops.override_tear_rd_ptr_val(hw_intf, adjusted_tear_rd_ptr_line_cnt); + + sde_encoder_helper_get_pp_line_count(phys_enc->parent, info); + SDE_EVT32_VERBOSE(phys_enc->hw_intf->idx - INTF_0, mode->vdisplay, + cmd_enc->qsync_threshold_lines, 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); +} + static void _sde_encoder_phys_signal_frame_done(struct sde_encoder_phys *phys_enc) { struct sde_encoder_phys_cmd *cmd_enc; @@ -309,9 +340,9 @@ static void sde_encoder_phys_cmd_te_rd_ptr_irq(void *arg, int irq_idx) sde_encoder_helper_get_pp_line_count(phys_enc->parent, info); SDE_EVT32_IRQ(DRMID(phys_enc->parent), info[0].pp_idx, info[0].intf_idx, - info[0].wr_ptr_line_count, info[0].intf_frame_count, + info[0].wr_ptr_line_count, info[0].intf_frame_count, info[0].rd_ptr_line_count, info[1].pp_idx, info[1].intf_idx, - info[1].wr_ptr_line_count, info[1].intf_frame_count, + info[1].wr_ptr_line_count, info[1].intf_frame_count, info[1].rd_ptr_line_count, scheduler_status, fence_ready); if (phys_enc->parent_ops.handle_vblank_virt) @@ -327,7 +358,7 @@ static void sde_encoder_phys_cmd_wr_ptr_irq(void *arg, int irq_idx) { struct sde_encoder_phys *phys_enc = arg; struct sde_hw_ctl *ctl; - u32 event = 0; + u32 event = 0, qsync_mode = 0; struct sde_hw_pp_vsync_info info[MAX_CHANNELS_PER_ENC] = {{0}}; if (!phys_enc || !phys_enc->hw_ctl) @@ -335,6 +366,7 @@ static void sde_encoder_phys_cmd_wr_ptr_irq(void *arg, int irq_idx) SDE_ATRACE_BEGIN("wr_ptr_irq"); ctl = phys_enc->hw_ctl; + qsync_mode = sde_connector_get_qsync_mode(phys_enc->connector); if (atomic_add_unless(&phys_enc->pending_retire_fence_cnt, -1, 0)) { event = SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE; @@ -350,7 +382,10 @@ static void sde_encoder_phys_cmd_wr_ptr_irq(void *arg, int irq_idx) SDE_EVT32_IRQ(DRMID(phys_enc->parent), ctl->idx - CTL_0, event, info[0].pp_idx, info[0].intf_idx, info[0].wr_ptr_line_count, - info[1].pp_idx, info[1].intf_idx, info[1].wr_ptr_line_count); + info[1].pp_idx, info[1].intf_idx, info[1].wr_ptr_line_count, qsync_mode); + + if (qsync_mode) + sde_encoder_override_tearcheck_rd_ptr(phys_enc); /* Signal any waiting wr_ptr start interrupt */ wake_up_all(&phys_enc->pending_kickoff_wq); @@ -1092,6 +1127,7 @@ static void sde_encoder_phys_cmd_tearcheck_config( 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; SDE_DEBUG_CMDENC(cmd_enc, "tc %d intf %d vsync_clk_speed_hz %u vtotal %u vrefresh %u\n", @@ -1418,6 +1454,7 @@ 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); + cmd_enc->qsync_threshold_lines = tc_cfg.sync_threshold_start; if (phys_enc->has_intf_te && phys_enc->hw_intf->ops.update_tearcheck) phys_enc->hw_intf->ops.update_tearcheck( diff --git a/msm/sde/sde_hw_intf.c b/msm/sde/sde_hw_intf.c index 29c4af13a7..9a33537e7f 100644 --- a/msm/sde/sde_hw_intf.c +++ b/msm/sde/sde_hw_intf.c @@ -858,6 +858,21 @@ static int sde_hw_intf_v1_check_and_reset_tearcheck(struct sde_hw_intf *intf, return 0; } +static void sde_hw_intf_override_tear_rd_ptr_val(struct sde_hw_intf *intf, + u32 adjusted_rd_ptr_val) +{ + struct sde_hw_blk_reg_map *c; + + if (!intf || !adjusted_rd_ptr_val) + return; + + c = &intf->hw; + + SDE_REG_WRITE(c, INTF_TEAR_SYNC_WRCOUNT, (adjusted_rd_ptr_val & 0xFFFF)); + /* ensure rd_ptr_val is written */ + wmb(); +} + static void sde_hw_intf_vsync_sel(struct sde_hw_intf *intf, u32 vsync_source) { @@ -956,6 +971,8 @@ static void _setup_intf_ops(struct sde_hw_intf_ops *ops, ops->vsync_sel = sde_hw_intf_vsync_sel; ops->check_and_reset_tearcheck = sde_hw_intf_v1_check_and_reset_tearcheck; + ops->override_tear_rd_ptr_val = + sde_hw_intf_override_tear_rd_ptr_val; } if (cap & BIT(SDE_INTF_RESET_COUNTER)) diff --git a/msm/sde/sde_hw_intf.h b/msm/sde/sde_hw_intf.h index 5c33b11eff..f33ac1dc5e 100644 --- a/msm/sde/sde_hw_intf.h +++ b/msm/sde/sde_hw_intf.h @@ -247,6 +247,13 @@ struct sde_hw_intf_ops { * Get the INTF interrupt status */ u32 (*get_intr_status)(struct sde_hw_intf *intf); + + /** + * Override tear check rd_ptr_val with adjusted_linecnt + * when qsync is enabled. + */ + void (*override_tear_rd_ptr_val)(struct sde_hw_intf *intf, + u32 adjusted_linecnt); }; struct sde_hw_intf {