From 7db951ec694f9d1e71530a10c7e7d6c3825b8efe Mon Sep 17 00:00:00 2001 From: Prabhanjan Kandula Date: Tue, 5 Oct 2021 15:22:46 -0700 Subject: [PATCH 1/2] disp: msm: sde: avoid dsc hw allocation switch for an active display While hw resources allocation for an active display during modeset, avoid dsc hw allocation switch by finding which dsc encoders are allocated previously and allocate same dsc hw encoders. This helps in fixing underrun issues in below scenario of dual display power ON/OFF. Use case: Dual DSI display setup, both are DSC enabled, primary in video mode. --> when both displays are in powered off, all hw block are free. --> enable second dsi display Since LM 0/1 marked for primary, LM 2/3 allocated along with DSC 0/1 --> enable primary display LM 0/1 allocated with DSC 2/3 encoders --> Now power off secondary DSI DSC 0/1 are freed up --> Immediate modeset on primary, DSC allocation switched LM 0/1 and DSC 0/1 allocated. DSC 2/3 are freed up as per RM but decoupling DSC 2/3 blocks with respective pingpong or intf is not done. This is causing underruns on primary. Tracking which DSC blocks are freed during resource switch and programming the respective DSC control mux configuration is not feasible and not scalable as any other display can allocate those blocks and would require synchronizing across display threads. So approach taken is avoid dsc resource switch itself. Change-Id: I7f740722a52266740c4b168edc0c619e3cf68989 Signed-off-by: Prabhanjan Kandula --- msm/sde/sde_rm.c | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/msm/sde/sde_rm.c b/msm/sde/sde_rm.c index a594ca30f0..f877ce441e 100644 --- a/msm/sde/sde_rm.c +++ b/msm/sde/sde_rm.c @@ -24,6 +24,9 @@ (((h)->rsvp && ((h)->rsvp->enc_id != (r)->enc_id)) ||\ ((h)->rsvp_nxt && ((h)->rsvp_nxt->enc_id != (r)->enc_id))) +#define RESERVED_BY_CURRENT(h, r) \ + (((h)->rsvp && ((h)->rsvp->enc_id == (r)->enc_id))) + #define RM_RQ_LOCK(r) ((r)->top_ctrl & BIT(SDE_RM_TOPCTL_RESERVE_LOCK)) #define RM_RQ_CLEAR(r) ((r)->top_ctrl & BIT(SDE_RM_TOPCTL_RESERVE_CLEAR)) #define RM_RQ_DSPP(r) ((r)->top_ctrl & BIT(SDE_RM_TOPCTL_DSPP)) @@ -1848,12 +1851,39 @@ static int _sde_rm_make_ctl_rsvp(struct sde_rm *rm, struct sde_rm_rsvp *rsvp, return ret; } +/* + * Returns number of dsc hw blocks previously owned by this encoder. + * Returns 0 if not found or error + */ +static int _sde_rm_find_prev_dsc(struct sde_rm *rm, struct sde_rm_rsvp *rsvp, + u8 *prev_dsc, u32 max_cnt) +{ + int i = 0; + struct sde_rm_hw_iter iter_dsc; + + if ((!prev_dsc) || (max_cnt < MAX_DATA_PATH_PER_DSIPLAY)) + return 0; + + sde_rm_init_hw_iter(&iter_dsc, 0, SDE_HW_BLK_DSC); + + while (_sde_rm_get_hw_locked(rm, &iter_dsc)) { + if (RESERVED_BY_CURRENT(iter_dsc.blk, rsvp)) + prev_dsc[i++] = iter_dsc.blk->id; + + if (i >= MAX_DATA_PATH_PER_DSIPLAY) + return 0; + } + + return i; +} + static int _sde_rm_make_dsc_rsvp(struct sde_rm *rm, struct sde_rm_rsvp *rsvp, struct sde_rm_requirements *reqs, struct sde_splash_display *splash_display) { int i; u8 *hw_ids = NULL; + u8 prev_dsc[MAX_DATA_PATH_PER_DSIPLAY] = {0,}; /* Check if splash data provided dsc_ids */ if (splash_display) { @@ -1866,7 +1896,16 @@ static int _sde_rm_make_dsc_rsvp(struct sde_rm *rm, struct sde_rm_rsvp *rsvp, i, splash_display->dsc_ids[i]); } - return _sde_rm_reserve_dsc(rm, rsvp, reqs, hw_ids); + /* + * find if this encoder has previously allocated dsc hw blocks, use same dsc blocks + * if found to avoid switching dsc encoders during each modeset, as currently we + * dont have feasible way of decoupling previously owned dsc blocks by resetting + * respective dsc encoders mux control and flush them from commit path + */ + if (!hw_ids && _sde_rm_find_prev_dsc(rm, rsvp, prev_dsc, MAX_DATA_PATH_PER_DSIPLAY)) + return _sde_rm_reserve_dsc(rm, rsvp, reqs, prev_dsc); + else + return _sde_rm_reserve_dsc(rm, rsvp, reqs, hw_ids); } From 9e988121fc49e5b9baa58d9dd7ede87a1cbcf71f Mon Sep 17 00:00:00 2001 From: Prabhanjan Kandula Date: Tue, 5 Oct 2021 19:40:20 -0700 Subject: [PATCH 2/2] disp: msm: sde: reset dsc mux config in encoder disable During display encoder disable, reset the dsc control mux configuration during null commit to ensure dsc hw blocks are cleanly freed up. Change-Id: I02e2f074450e4d7b49dc8fec14777f380786c63e Signed-off-by: Prabhanjan Kandula --- msm/sde/sde_encoder.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index fbee58b6b5..ecdac0ec3a 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -3172,6 +3172,7 @@ void sde_encoder_helper_phys_disable(struct sde_encoder_phys *phys_enc, struct sde_encoder_virt *sde_enc; struct sde_hw_ctl *ctl = phys_enc->hw_ctl; struct sde_ctl_flush_cfg cfg; + struct sde_hw_dsc *hw_dsc = NULL; int i; ctl->ops.reset(ctl); @@ -3226,6 +3227,17 @@ void sde_encoder_helper_phys_disable(struct sde_encoder_phys *phys_enc, phys_enc->hw_pp->merge_3d ? phys_enc->hw_pp->merge_3d->idx : 0); + for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { + hw_dsc = sde_enc->hw_dsc[i]; + + if (hw_dsc && hw_dsc->ops.bind_pingpong_blk) { + hw_dsc->ops.bind_pingpong_blk(hw_dsc, false, PINGPONG_MAX); + + if (ctl->ops.update_bitmask) + ctl->ops.update_bitmask(ctl, SDE_HW_FLUSH_DSC, hw_dsc->idx, true); + } + } + sde_crtc_disable_cp_features(sde_enc->base.crtc); ctl->ops.get_pending_flush(ctl, &cfg); SDE_EVT32(DRMID(phys_enc->parent), cfg.pending_flush_mask);