[RFC PATCH 6/7] drm/msm/dpu: Implement tearcheck support on INTF block

Dmitry Baryshkov dmitry.baryshkov at linaro.org
Sun Jan 1 13:32:11 UTC 2023


On 31/12/2022 23:50, Marijn Suijten wrote:
> Since DPU 5.0.0 the TEARCHECK registers and interrupts moved out of the
> PINGPONG block and into the INTF.  Implement the necessary callbacks in
> the INTF block, and use these callbacks together with the INTF_TEAR
> interrupts
> 
> Signed-off-by: Marijn Suijten <marijn.suijten at somainline.org>

Generally I have the same question as for the patch 2. Can we have some 
higher level functions in the hw_pp and hw_intf files? Moreover, as I 
review your patch I have the feeling that it would make sense to have to 
two sets of encoder callbacks, one for the hw_pp tearing handling and 
another set for hw_intf-based one.

> ---
>   drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c   |  11 +
>   .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h  |  10 +-
>   .../drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c  | 113 +++++++---
>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c   | 206 ++++++++++++++++++
>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h   |  29 +++
>   drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h   |   2 +
>   6 files changed, 340 insertions(+), 31 deletions(-)
> 
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> index 9c6817b5a194..8b9070220ab2 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
> @@ -673,6 +673,7 @@ static void _dpu_encoder_update_vsync_source(struct dpu_encoder_virt *dpu_enc,
>   	struct dpu_kms *dpu_kms;
>   	struct dpu_hw_mdp *hw_mdptop;
>   	struct drm_encoder *drm_enc;
> +	struct dpu_encoder_phys *phys_enc;
>   	int i;
>   
>   	if (!dpu_enc || !disp_info) {
> @@ -703,12 +704,22 @@ static void _dpu_encoder_update_vsync_source(struct dpu_encoder_virt *dpu_enc,
>   			vsync_cfg.ppnumber[i] = dpu_enc->hw_pp[i]->idx;
>   
>   		vsync_cfg.pp_count = dpu_enc->num_phys_encs;
> +		vsync_cfg.frame_rate = drm_mode_vrefresh(&dpu_enc->base.crtc->state->adjusted_mode);
> +
>   		if (disp_info->is_te_using_watchdog_timer)
>   			vsync_cfg.vsync_source = DPU_VSYNC_SOURCE_WD_TIMER_0;
>   		else
>   			vsync_cfg.vsync_source = DPU_VSYNC0_SOURCE_GPIO;
>   
>   		hw_mdptop->ops.setup_vsync_source(hw_mdptop, &vsync_cfg);
> +
> +		for (i = 0; i < dpu_enc->num_phys_encs; i++) {
> +			phys_enc = dpu_enc->phys_encs[i];
> +
> +			if (phys_enc->has_intf_te && phys_enc->hw_intf->ops.vsync_sel)
> +				phys_enc->hw_intf->ops.vsync_sel(phys_enc->hw_intf,
> +						vsync_cfg.vsync_source);
> +		}
>   	}
>   }
>   
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> index f2af07d87f56..47e79401032c 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
> @@ -148,10 +148,10 @@ struct dpu_encoder_phys_ops {
>   /**
>    * enum dpu_intr_idx - dpu encoder interrupt index
>    * @INTR_IDX_VSYNC:    Vsync interrupt for video mode panel
> - * @INTR_IDX_PINGPONG: Pingpong done unterrupt for cmd mode panel
> - * @INTR_IDX_UNDERRUN: Underrun unterrupt for video and cmd mode panel
> - * @INTR_IDX_RDPTR:    Readpointer done unterrupt for cmd mode panel
> - * @INTR_IDX_WB_DONE:  Writeback fone interrupt for virtual connector
> + * @INTR_IDX_PINGPONG: Pingpong done interrupt for cmd mode panel
> + * @INTR_IDX_UNDERRUN: Underrun interrupt for video and cmd mode panel
> + * @INTR_IDX_RDPTR:    Readpointer done interrupt for cmd mode panel
> + * @INTR_IDX_WB_DONE:  Writeback done interrupt for virtual connector
>    */
>   enum dpu_intr_idx {
>   	INTR_IDX_VSYNC,
> @@ -195,6 +195,7 @@ enum dpu_intr_idx {
>    *                              pending.
>    * @pending_kickoff_wq:		Wait queue for blocking until kickoff completes
>    * @irq:			IRQ indices
> + * @has_intf_te:		Interface TE configuration support
>    */
>   struct dpu_encoder_phys {
>   	struct drm_encoder *parent;
> @@ -220,6 +221,7 @@ struct dpu_encoder_phys {
>   	atomic_t pending_kickoff_cnt;
>   	wait_queue_head_t pending_kickoff_wq;
>   	int irq[INTR_IDX_MAX];
> +	bool has_intf_te;
>   };
>   
>   static inline int dpu_encoder_phys_inc_pending(struct dpu_encoder_phys *phys)
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c
> index 7e5ba52197cd..ca44a8087f01 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c
> @@ -100,12 +100,12 @@ static void dpu_encoder_phys_cmd_pp_tx_done_irq(void *arg, int irq_idx)
>   	DPU_ATRACE_END("pp_done_irq");
>   }
>   
> -static void dpu_encoder_phys_cmd_pp_rd_ptr_irq(void *arg, int irq_idx)
> +static void dpu_encoder_phys_cmd_te_rd_ptr_irq(void *arg, int irq_idx)
>   {
>   	struct dpu_encoder_phys *phys_enc = arg;
>   	struct dpu_encoder_phys_cmd *cmd_enc;
>   
> -	if (!phys_enc->hw_pp)
> +	if (!phys_enc->hw_pp || !phys_enc->hw_intf)
>   		return;
>   
>   	DPU_ATRACE_BEGIN("rd_ptr_irq");
> @@ -147,11 +147,19 @@ static void dpu_encoder_phys_cmd_atomic_mode_set(
>   		struct drm_crtc_state *crtc_state,
>   		struct drm_connector_state *conn_state)
>   {
> +	if (phys_enc->has_intf_te && !phys_enc->hw_intf) {
> +		DPU_ERROR("invalid intf configuration\n");
> +		return;
> +	}
> +
>   	phys_enc->irq[INTR_IDX_CTL_START] = phys_enc->hw_ctl->caps->intr_start;
>   
>   	phys_enc->irq[INTR_IDX_PINGPONG] = phys_enc->hw_pp->caps->intr_done;
>   
> -	phys_enc->irq[INTR_IDX_RDPTR] = phys_enc->hw_pp->caps->intr_rdptr;
> +	if (phys_enc->has_intf_te)
> +		phys_enc->irq[INTR_IDX_RDPTR] = phys_enc->hw_intf->cap->intr_tear_rd_ptr;
> +	else
> +		phys_enc->irq[INTR_IDX_RDPTR] = phys_enc->hw_pp->caps->intr_rdptr;
>   
>   	phys_enc->irq[INTR_IDX_UNDERRUN] = phys_enc->hw_intf->cap->intr_underrun;
>   }
> @@ -264,7 +272,7 @@ static int dpu_encoder_phys_cmd_control_vblank_irq(
>   	if (enable && atomic_inc_return(&phys_enc->vblank_refcount) == 1)
>   		ret = dpu_core_irq_register_callback(phys_enc->dpu_kms,
>   				phys_enc->irq[INTR_IDX_RDPTR],
> -				dpu_encoder_phys_cmd_pp_rd_ptr_irq,
> +				dpu_encoder_phys_cmd_te_rd_ptr_irq,
>   				phys_enc);
>   	else if (!enable && atomic_dec_return(&phys_enc->vblank_refcount) == 0)
>   		ret = dpu_core_irq_unregister_callback(phys_enc->dpu_kms,
> @@ -336,10 +344,18 @@ static void dpu_encoder_phys_cmd_tearcheck_config(
>   
>   	DPU_DEBUG_CMDENC(cmd_enc, "pp %d\n", phys_enc->hw_pp->idx - PINGPONG_0);
>   
> -	if (!phys_enc->hw_pp->ops.setup_tearcheck ||
> -		!phys_enc->hw_pp->ops.enable_tearcheck) {
> -		DPU_DEBUG_CMDENC(cmd_enc, "tearcheck not supported\n");
> -		return;
> +	if (phys_enc->has_intf_te) {
> +		if (!phys_enc->hw_intf->ops.setup_tearcheck ||
> +			!phys_enc->hw_intf->ops.enable_tearcheck) {
> +			DPU_DEBUG_CMDENC(cmd_enc, "tearcheck not supported\n");
> +			return;
> +		}
> +	} else {
> +		if (!phys_enc->hw_pp->ops.setup_tearcheck ||
> +			!phys_enc->hw_pp->ops.enable_tearcheck) {
> +			DPU_DEBUG_CMDENC(cmd_enc, "tearcheck not supported\n");
> +			return;
> +		}
>   	}
>   
>   	dpu_kms = phys_enc->dpu_kms;
> @@ -392,8 +408,13 @@ static void dpu_encoder_phys_cmd_tearcheck_config(
>   		phys_enc->hw_pp->idx - PINGPONG_0, tc_cfg.sync_cfg_height,
>   		tc_cfg.sync_threshold_start, tc_cfg.sync_threshold_continue);
>   
> -	phys_enc->hw_pp->ops.setup_tearcheck(phys_enc->hw_pp, &tc_cfg);
> -	phys_enc->hw_pp->ops.enable_tearcheck(phys_enc->hw_pp, tc_enable);

A simple random example: setup_tearcheck is always followed with the 
enable_tearcheck. If we merge them, the code would be simpler.

> +	if (phys_enc->has_intf_te) {
> +		phys_enc->hw_intf->ops.setup_tearcheck(phys_enc->hw_intf, &tc_cfg);
> +		phys_enc->hw_intf->ops.enable_tearcheck(phys_enc->hw_intf, tc_enable);
> +	} else {
> +		phys_enc->hw_pp->ops.setup_tearcheck(phys_enc->hw_pp, &tc_cfg);
> +		phys_enc->hw_pp->ops.enable_tearcheck(phys_enc->hw_pp, tc_enable);
> +	}
>   }
>   
>   static void _dpu_encoder_phys_cmd_pingpong_config(
> @@ -470,11 +491,19 @@ static void dpu_encoder_phys_cmd_enable(struct dpu_encoder_phys *phys_enc)
>   static void _dpu_encoder_phys_cmd_connect_te(
>   		struct dpu_encoder_phys *phys_enc, bool enable)
>   {
> -	if (!phys_enc->hw_pp || !phys_enc->hw_pp->ops.connect_external_te)
> -		return;
> +	if (phys_enc->has_intf_te) {
> +		if (!phys_enc->hw_intf || !phys_enc->hw_intf->ops.connect_external_te)
> +			return;
>   
> -	trace_dpu_enc_phys_cmd_connect_te(DRMID(phys_enc->parent), enable);
> -	phys_enc->hw_pp->ops.connect_external_te(phys_enc->hw_pp, enable);
> +		trace_dpu_enc_phys_cmd_connect_te(DRMID(phys_enc->parent), enable);
> +		phys_enc->hw_intf->ops.connect_external_te(phys_enc->hw_intf, enable);
> +	} else {
> +		if (!phys_enc->hw_pp || !phys_enc->hw_pp->ops.connect_external_te)
> +			return;
> +
> +		trace_dpu_enc_phys_cmd_connect_te(DRMID(phys_enc->parent), enable);
> +		phys_enc->hw_pp->ops.connect_external_te(phys_enc->hw_pp, enable);
> +	}
>   }
>   
>   static void dpu_encoder_phys_cmd_prepare_idle_pc(
> @@ -487,6 +516,7 @@ static int dpu_encoder_phys_cmd_get_line_count(
>   		struct dpu_encoder_phys *phys_enc)
>   {
>   	struct dpu_hw_pingpong *hw_pp;
> +	struct dpu_hw_intf *hw_intf;
>   
>   	if (!phys_enc->hw_pp)
>   		return -EINVAL;
> @@ -494,10 +524,16 @@ static int dpu_encoder_phys_cmd_get_line_count(
>   	if (!dpu_encoder_phys_cmd_is_master(phys_enc))
>   		return -EINVAL;
>   
> +	if (phys_enc->has_intf_te) {
> +		hw_intf = phys_enc->hw_intf;
> +		if (!hw_intf->ops.get_line_count)
> +			return -EINVAL;
> +		return hw_intf->ops.get_line_count(hw_intf);
> +	}
> +
>   	hw_pp = phys_enc->hw_pp;
>   	if (!hw_pp->ops.get_line_count)
>   		return -EINVAL;
> -
>   	return hw_pp->ops.get_line_count(hw_pp);
>   }
>   
> @@ -520,7 +556,9 @@ static void dpu_encoder_phys_cmd_disable(struct dpu_encoder_phys *phys_enc)
>   		return;
>   	}
>   
> -	if (phys_enc->hw_pp->ops.enable_tearcheck)
> +	if (phys_enc->has_intf_te && phys_enc->hw_intf->ops.enable_tearcheck)
> +		phys_enc->hw_intf->ops.enable_tearcheck(phys_enc->hw_intf, false);
> +	else if (phys_enc->hw_pp->ops.enable_tearcheck)
>   		phys_enc->hw_pp->ops.enable_tearcheck(phys_enc->hw_pp, false);
>   
>   	if (phys_enc->hw_intf->ops.bind_pingpong_blk) {
> @@ -582,10 +620,19 @@ static bool dpu_encoder_phys_cmd_is_ongoing_pptx(
>   {
>   	struct dpu_hw_pp_vsync_info info;
>   
> -	if (!phys_enc || !phys_enc->hw_pp->ops.get_vsync_info)
> +	if (!phys_enc)
>   		return false;
>   
> -	phys_enc->hw_pp->ops.get_vsync_info(phys_enc->hw_pp, &info);
> +	if (phys_enc->has_intf_te) {
> +		if (!phys_enc->hw_intf->ops.get_vsync_info)
> +			return false;
> +		phys_enc->hw_intf->ops.get_vsync_info(phys_enc->hw_intf, &info);
> +	} else {
> +		if (!phys_enc->hw_pp->ops.get_vsync_info)
> +			return false;
> +		phys_enc->hw_pp->ops.get_vsync_info(phys_enc->hw_pp, &info);
> +	}
> +
>   	if (info.wr_ptr_line_count > 0 &&
>   	    info.wr_ptr_line_count < phys_enc->cached_mode.vdisplay)
>   		return true;
> @@ -602,17 +649,23 @@ static void dpu_encoder_phys_cmd_prepare_commit(
>   
>   	if (!phys_enc)
>   		return;
> -	if (!phys_enc->hw_pp)
> -		return;
>   	if (!dpu_encoder_phys_cmd_is_master(phys_enc))
>   		return;
>   
> -	if (!phys_enc->hw_pp->ops.get_autorefresh || !phys_enc->hw_pp->ops.setup_autorefresh)
> -		return;
> -
>   	/* If autorefresh is already disabled, we have nothing to do */
> -	if (!phys_enc->hw_pp->ops.get_autorefresh(phys_enc->hw_pp, NULL))
> -		return;
> +	if (phys_enc->has_intf_te) {
> +		if (!phys_enc->hw_intf || !phys_enc->hw_intf->ops.get_autorefresh ||
> +				!phys_enc->hw_intf->ops.setup_autorefresh)
> +			return;
> +		if (!phys_enc->hw_intf->ops.get_autorefresh(phys_enc->hw_intf, NULL))
> +			return;
> +	} else {
> +		if (!phys_enc->hw_pp || !phys_enc->hw_pp->ops.get_autorefresh ||
> +				!phys_enc->hw_pp->ops.setup_autorefresh)
> +			return;
> +		if (!phys_enc->hw_pp->ops.get_autorefresh(phys_enc->hw_pp, NULL))
> +			return;
> +	}
>   
>   	/*
>   	 * If autorefresh is enabled, disable it and make sure it is safe to
> @@ -623,7 +676,10 @@ static void dpu_encoder_phys_cmd_prepare_commit(
>   	 * 5. Enable TE back
>   	 */
>   	_dpu_encoder_phys_cmd_connect_te(phys_enc, false);
> -	phys_enc->hw_pp->ops.setup_autorefresh(phys_enc->hw_pp, 0, false);
> +	if (phys_enc->has_intf_te)
> +		phys_enc->hw_intf->ops.setup_autorefresh(phys_enc->hw_intf, 0, false);
> +	else
> +		phys_enc->hw_pp->ops.setup_autorefresh(phys_enc->hw_pp, 0, false);
>   
>   	do {
>   		udelay(DPU_ENC_MAX_POLL_TIMEOUT_US);
> @@ -717,7 +773,7 @@ static int dpu_encoder_phys_cmd_wait_for_vblank(
>   
>   	rc = dpu_encoder_helper_wait_for_irq(phys_enc,
>   			phys_enc->irq[INTR_IDX_RDPTR],
> -			dpu_encoder_phys_cmd_pp_rd_ptr_irq,
> +			dpu_encoder_phys_cmd_te_rd_ptr_irq,
>   			&wait_info);
>   
>   	return rc;
> @@ -793,6 +849,9 @@ struct dpu_encoder_phys *dpu_encoder_phys_cmd_init(
>   	for (i = 0; i < ARRAY_SIZE(phys_enc->irq); i++)
>   		phys_enc->irq[i] = -EINVAL;
>   
> +	phys_enc->has_intf_te = test_bit(DPU_INTF_TE,
> +			&phys_enc->dpu_kms->catalog->intf[p->intf_idx - INTF_0].features);
> +
>   	atomic_set(&phys_enc->vblank_refcount, 0);
>   	atomic_set(&phys_enc->pending_kickoff_cnt, 0);
>   	atomic_set(&phys_enc->pending_ctlstart_cnt, 0);
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c
> index 7ce66bf3f4c8..42929278df8e 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c
> @@ -9,6 +9,8 @@
>   #include "dpu_hw_intf.h"
>   #include "dpu_kms.h"
>   
> +#include <linux/iopoll.h>
> +
>   #define INTF_TIMING_ENGINE_EN           0x000
>   #define INTF_CONFIG                     0x004
>   #define INTF_HSYNC_CTL                  0x008
> @@ -62,6 +64,27 @@
>   #define   INTF_LINE_COUNT               0x0B0
>   
>   #define   INTF_MUX                      0x25C
> +#define   INTF_MISR_SIGNATURE           0x184
> +
> +#define INTF_STATUS                     0x26C
> +#define INTF_AVR_CONTROL                0x270
> +#define INTF_AVR_MODE                   0x274
> +#define INTF_AVR_TRIGGER                0x278
> +#define INTF_AVR_VTOTAL                 0x27C
> +#define INTF_TEAR_MDP_VSYNC_SEL         0x280
> +#define INTF_TEAR_TEAR_CHECK_EN         0x284
> +#define INTF_TEAR_SYNC_CONFIG_VSYNC     0x288
> +#define INTF_TEAR_SYNC_CONFIG_HEIGHT    0x28C
> +#define INTF_TEAR_SYNC_WRCOUNT          0x290
> +#define INTF_TEAR_VSYNC_INIT_VAL        0x294
> +#define INTF_TEAR_INT_COUNT_VAL         0x298
> +#define INTF_TEAR_SYNC_THRESH           0x29C
> +#define INTF_TEAR_START_POS             0x2A0
> +#define INTF_TEAR_RD_PTR_IRQ            0x2A4
> +#define INTF_TEAR_WR_PTR_IRQ            0x2A8
> +#define INTF_TEAR_OUT_LINE_COUNT        0x2AC
> +#define INTF_TEAR_LINE_COUNT            0x2B0
> +#define INTF_TEAR_AUTOREFRESH_CONFIG    0x2B4
>   
>   #define INTF_CFG_ACTIVE_H_EN	BIT(29)
>   #define INTF_CFG_ACTIVE_V_EN	BIT(30)
> @@ -309,6 +332,22 @@ static void dpu_hw_intf_get_status(
>   	}
>   }
>   
> +static void dpu_hw_intf_v1_get_status(
> +		struct dpu_hw_intf *intf,
> +		struct intf_status *s)
> +{
> +	struct dpu_hw_blk_reg_map *c = &intf->hw;
> +
> +	s->is_en = DPU_REG_READ(c, INTF_STATUS) & BIT(0);
> +	if (s->is_en) {
> +		s->frame_count = DPU_REG_READ(c, INTF_FRAME_COUNT);
> +		s->line_count = DPU_REG_READ(c, INTF_LINE_COUNT);
> +	} else {
> +		s->line_count = 0;
> +		s->frame_count = 0;
> +	}
> +}
> +
>   static u32 dpu_hw_intf_get_line_count(struct dpu_hw_intf *intf)
>   {
>   	struct dpu_hw_blk_reg_map *c;
> @@ -331,6 +370,161 @@ static int dpu_hw_intf_collect_misr(struct dpu_hw_intf *intf, u32 *misr_value)
>   	return dpu_hw_collect_misr(&intf->hw, INTF_MISR_CTRL, INTF_MISR_SIGNATURE, misr_value);
>   }
>   
> +static int dpu_hw_intf_setup_te_config(struct dpu_hw_intf *intf,
> +		struct dpu_hw_tear_check *te)
> +{
> +	struct dpu_hw_blk_reg_map *c;
> +	int cfg;
> +
> +	if (!intf)
> +		return -EINVAL;
> +
> +	c = &intf->hw;
> +
> +	cfg = BIT(19); /* VSYNC_COUNTER_EN */
> +	if (te->hw_vsync_mode)
> +		cfg |= BIT(20);
> +
> +	cfg |= te->vsync_count;
> +
> +	DPU_REG_WRITE(c, INTF_TEAR_SYNC_CONFIG_VSYNC, cfg);
> +	DPU_REG_WRITE(c, INTF_TEAR_SYNC_CONFIG_HEIGHT, te->sync_cfg_height);
> +	DPU_REG_WRITE(c, INTF_TEAR_VSYNC_INIT_VAL, te->vsync_init_val);
> +	DPU_REG_WRITE(c, INTF_TEAR_RD_PTR_IRQ, te->rd_ptr_irq);
> +	DPU_REG_WRITE(c, INTF_TEAR_START_POS, te->start_pos);
> +	DPU_REG_WRITE(c, INTF_TEAR_SYNC_THRESH,
> +			((te->sync_threshold_continue << 16) |
> +			 te->sync_threshold_start));
> +	DPU_REG_WRITE(c, INTF_TEAR_SYNC_WRCOUNT,
> +			(te->start_pos + te->sync_threshold_start + 1));
> +
> +	return 0;
> +}
> +
> +static void dpu_hw_intf_setup_autorefresh_config(struct dpu_hw_intf *intf,
> +		u32 frame_count, bool enable)
> +{
> +	struct dpu_hw_blk_reg_map *c;
> +	u32 refresh_cfg;
> +
> +	c = &intf->hw;
> +	refresh_cfg = DPU_REG_READ(c, INTF_TEAR_AUTOREFRESH_CONFIG);
> +	if (enable)
> +		refresh_cfg = BIT(31) | frame_count;
> +	else
> +		refresh_cfg &= ~BIT(31);
> +
> +	DPU_REG_WRITE(c, INTF_TEAR_AUTOREFRESH_CONFIG, refresh_cfg);
> +}
> +
> +/*
> + * dpu_hw_intf_get_autorefresh_config - Get autorefresh config from HW
> + * @intf:        DPU intf structure
> + * @frame_count: Used to return the current frame count from hw
> + *
> + * Returns: True if autorefresh enabled, false if disabled.
> + */
> +static bool dpu_hw_intf_get_autorefresh_config(struct dpu_hw_intf *intf,
> +		u32 *frame_count)
> +{
> +	u32 val = DPU_REG_READ(&intf->hw, INTF_TEAR_AUTOREFRESH_CONFIG);
> +
> +	if (frame_count != NULL)
> +		*frame_count = val & 0xffff;
> +	return !!((val & BIT(31)) >> 31);
> +}
> +
> +static int dpu_hw_intf_poll_timeout_wr_ptr(struct dpu_hw_intf *intf,
> +		u32 timeout_us)
> +{
> +	struct dpu_hw_blk_reg_map *c;
> +	u32 val;
> +	int rc;
> +
> +	if (!intf)
> +		return -EINVAL;
> +
> +	c = &intf->hw;
> +	rc = readl_poll_timeout(c->blk_addr + INTF_TEAR_LINE_COUNT,
> +			val, (val & 0xffff) >= 1, 10, timeout_us);
> +
> +	return rc;
> +}
> +
> +static int dpu_hw_intf_enable_te(struct dpu_hw_intf *intf, bool enable)
> +{
> +	struct dpu_hw_blk_reg_map *c;
> +
> +	if (!intf)
> +		return -EINVAL;
> +
> +	c = &intf->hw;
> +	DPU_REG_WRITE(c, INTF_TEAR_TEAR_CHECK_EN, enable);
> +	return 0;
> +}
> +
> +static int dpu_hw_intf_connect_external_te(struct dpu_hw_intf *intf,
> +		bool enable_external_te)
> +{
> +	struct dpu_hw_blk_reg_map *c = &intf->hw;
> +	u32 cfg;
> +	int orig;
> +
> +	if (!intf)
> +		return -EINVAL;
> +
> +	c = &intf->hw;
> +	cfg = DPU_REG_READ(c, INTF_TEAR_SYNC_CONFIG_VSYNC);
> +	orig = (bool)(cfg & BIT(20));
> +	if (enable_external_te)
> +		cfg |= BIT(20);
> +	else
> +		cfg &= ~BIT(20);
> +	DPU_REG_WRITE(c, INTF_TEAR_SYNC_CONFIG_VSYNC, cfg);
> +
> +	return orig;
> +}
> +
> +static int dpu_hw_intf_get_vsync_info(struct dpu_hw_intf *intf,
> +		struct dpu_hw_pp_vsync_info *info)
> +{
> +	struct dpu_hw_blk_reg_map *c = &intf->hw;
> +	u32 val;
> +
> +	if (!intf || !info)
> +		return -EINVAL;
> +
> +	c = &intf->hw;
> +
> +	val = DPU_REG_READ(c, INTF_TEAR_VSYNC_INIT_VAL);
> +	info->rd_ptr_init_val = val & 0xffff;
> +
> +	val = DPU_REG_READ(c, INTF_TEAR_INT_COUNT_VAL);
> +	info->rd_ptr_frame_count = (val & 0xffff0000) >> 16;
> +	info->rd_ptr_line_count = val & 0xffff;
> +
> +	val = DPU_REG_READ(c, INTF_TEAR_LINE_COUNT);
> +	info->wr_ptr_line_count = val & 0xffff;
> +
> +	val = DPU_REG_READ(c, INTF_FRAME_COUNT);
> +	info->intf_frame_count = val;
> +
> +	return 0;
> +}
> +
> +static void dpu_hw_intf_vsync_sel(struct dpu_hw_intf *intf,
> +		u32 vsync_source)
> +{
> +	struct dpu_hw_blk_reg_map *c;
> +
> +	if (!intf)
> +		return;
> +
> +	c = &intf->hw;
> +
> +	DPU_REG_WRITE(c, INTF_TEAR_MDP_VSYNC_SEL, (vsync_source & 0xf));
> +}
> +
>   static void _setup_intf_ops(struct dpu_hw_intf_ops *ops,
>   		unsigned long cap)
>   {
> @@ -343,6 +537,18 @@ static void _setup_intf_ops(struct dpu_hw_intf_ops *ops,
>   		ops->bind_pingpong_blk = dpu_hw_intf_bind_pingpong_blk;
>   	ops->setup_misr = dpu_hw_intf_setup_misr;
>   	ops->collect_misr = dpu_hw_intf_collect_misr;
> +
> +	if (cap & BIT(DPU_INTF_TE)) {
> +		ops->setup_tearcheck = dpu_hw_intf_setup_te_config;
> +		ops->enable_tearcheck = dpu_hw_intf_enable_te;
> +		ops->connect_external_te = dpu_hw_intf_connect_external_te;
> +		ops->get_vsync_info = dpu_hw_intf_get_vsync_info;
> +		ops->setup_autorefresh = dpu_hw_intf_setup_autorefresh_config;
> +		ops->get_autorefresh = dpu_hw_intf_get_autorefresh_config;
> +		ops->poll_timeout_wr_ptr = dpu_hw_intf_poll_timeout_wr_ptr;
> +		ops->vsync_sel = dpu_hw_intf_vsync_sel;
> +		ops->get_status = dpu_hw_intf_v1_get_status;
> +	}
>   }
>   
>   struct dpu_hw_intf *dpu_hw_intf_init(enum dpu_intf idx,
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h
> index 643dd10bc030..1521c9475fad 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h
> @@ -60,6 +60,17 @@ struct intf_status {
>    *                     feed pixels to this interface
>    * @setup_misr: enable/disable MISR
>    * @collect_misr: read MISR signature
> + * @setup_tearcheck:            Enables vsync generation and sets up init value of read
> + *                              pointer and programs the tear check configuration
> + * @enable_tearcheck:           Enables tearcheck block
> + * @connect_external_te:        Read, modify, write to either set or clear listening to external TE
> + *                              Return: 1 if TE was originally connected, 0 if not, or -ERROR
> + * @get_vsync_info:             Provides the programmed and current line_count
> + * @setup_autorefresh:          Configure and enable the autorefresh config
> + * @get_autorefresh:            Retrieve autorefresh config from hardware
> + * @poll_timeout_wr_ptr:        Poll until write pointer transmission starts
> + *                              Return: 0 on success, -ETIMEDOUT on timeout
> + * @vsync_sel:                  Select vsync signal for tear-effect configuration
>    */
>   struct dpu_hw_intf_ops {
>   	void (*setup_timing_gen)(struct dpu_hw_intf *intf,
> @@ -82,6 +93,24 @@ struct dpu_hw_intf_ops {
>   			const enum dpu_pingpong pp);
>   	void (*setup_misr)(struct dpu_hw_intf *intf, bool enable, u32 frame_count);
>   	int (*collect_misr)(struct dpu_hw_intf *intf, u32 *misr_value);
> +
> +	// Tearcheck on INTF since DPU 5.0.0
> +
> +	int (*setup_tearcheck)(struct dpu_hw_intf *intf, struct dpu_hw_tear_check *cfg);
> +
> +	int (*enable_tearcheck)(struct dpu_hw_intf *intf, bool enable);
> +
> +	int (*connect_external_te)(struct dpu_hw_intf *intf, bool enable_external_te);
> +
> +	int (*get_vsync_info)(struct dpu_hw_intf *intf, struct dpu_hw_pp_vsync_info *info);
> +
> +	void (*setup_autorefresh)(struct dpu_hw_intf *intf, u32 frame_count, bool enable);
> +
> +	bool (*get_autorefresh)(struct dpu_hw_intf *intf, u32 *frame_count);
> +
> +	int (*poll_timeout_wr_ptr)(struct dpu_hw_intf *intf, u32 timeout_us);
> +
> +	void (*vsync_sel)(struct dpu_hw_intf *intf, u32 vsync_source);
>   };
>   
>   struct dpu_hw_intf {
> diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h
> index 64b2bf219a34..773579906961 100644
> --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h
> +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h
> @@ -494,12 +494,14 @@ struct dpu_hw_tear_check {
>    * @rd_ptr_frame_count: Num frames sent since enabling interface
>    * @rd_ptr_line_count:  Current line on panel (rd ptr)
>    * @wr_ptr_line_count:  Current line within pp fifo (wr ptr)
> + * @intf_frame_count:   Frames read from intf
>    */
>   struct dpu_hw_pp_vsync_info {
>   	u32 rd_ptr_init_val;
>   	u32 rd_ptr_frame_count;
>   	u32 rd_ptr_line_count;
>   	u32 wr_ptr_line_count;
> +	u32 intf_frame_count;
>   };
>   
>   #endif  /* _DPU_HW_MDSS_H */

-- 
With best wishes
Dmitry



More information about the dri-devel mailing list