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

From: Marijn Suijten
Date: Mon Jan 02 2023 - 06:06:26 EST


On 2023-01-01 15:32:11, Dmitry Baryshkov wrote:
> 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@xxxxxxxxxxxxxx>
>
> 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?

That is mostly because patch 2 only cleaned up non-optional handling of
hw_pp callbacks in dpu_encoder_phys_cmd_prepare_commit, which utilizes
hw_intf's autorefresh callbacks since this patch. I don't think there's
any logic in the encoder currently that is unique to either PP or INTF?

There are quite a few functions that check for NULL hw_pp only, while -
especially after this patch - should also check hw_intf to raise
"invalid encoder". Should I extend those checks as well?

> 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.

Do you mean to duplicate most phy_cmd functions and switch them based on
has_intf_te in dpu_encoder_phys_cmd_init_ops? Or introduce an entirely
new set of callbacks that simply hide / abstract away the check on
has_intf_te? The former would duplicate a bunch of code unless that is
abstracted away into other functions, mainly in
dpu_encoder_phys_cmd_tearcheck_config and
dpu_encoder_phys_cmd_prepare_commit.

Alternatively, could we find a way where these PP and INTF ops share the
same struct and function signature? That might be tricky for passing in
the hw_pp or hw_intf struct without leaking those details to the
callback and/or have the switching logic in there.

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

Fwiw looks like this function is a prime candidate to get updated with
hw_intf information (in error checking and logging), as this callback is
now shared between PP and INTF.

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

setup_tearcheck could include this functionality, but note that
dpu_encoder_phys_cmd_disable currently calls enable_tearcheck(false)
without setup_tearcheck.

- Marijn

> > <snip>