From 6dd0792bf246517f6d02541d4f97f230e4ab15c5 Mon Sep 17 00:00:00 2001 From: Sandy Huang Date: Thu, 27 May 2021 15:21:04 +0800 Subject: [PATCH] drm/rockchip: vop2: make sure layer sel is take effect when it's updated when vp0 and vp1 indenpendent config layer_sel register, this register take effect time is prone to error, so we add the following measures to workaround this issue: 1. Add commit_lock to make sure vp0 and vp1 config register is mutually exclusive; 2. Make sure layer sel register is take effect when it's update. Signed-off-by: Sandy Huang Change-Id: Ief832e2bf7e18567f4ea663843c77f0afbd21cf7 --- drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 2 ++ drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 32 ++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index 4a8ba874ab12..2adf1c678125 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -424,7 +424,9 @@ rockchip_atomic_commit_complete(struct rockchip_atomic_commit *commit) rockchip_dmcfreq_vop_bandwidth_update(prv->devfreq, bandwidth, plane_num); + mutex_lock(&prv->commit_lock); drm_atomic_helper_commit_planes(dev, state, true); + mutex_unlock(&prv->commit_lock); rockchip_drm_psr_inhibit_put_state(state); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 1353e7e3b4a3..a40f2c944e36 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -414,6 +414,7 @@ struct vop2_video_port { struct vop2 *vop2; struct clk *dclk; uint8_t id; + bool layer_sel_update; const struct vop2_video_port_regs *regs; struct completion dsp_hold_completion; @@ -1026,6 +1027,9 @@ static inline void vop2_cfg_done(struct drm_crtc *crtc) struct vop2 *vop2 = vp->vop2; uint32_t done_bits; uint32_t val; + u32 old_layer_sel_val, cfg_layer_sel_val; + struct vop2_layer *layer = &vop2->layers[0]; + u32 layer_sel_offset = layer->regs->layer_sel.offset; /* * This is a workaround, the config done bits of VP0, @@ -1045,7 +1049,23 @@ static inline void vop2_cfg_done(struct drm_crtc *crtc) */ done_bits = vop2_pending_done_bits(vp); val = RK3568_VOP2_GLB_CFG_DONE_EN | BIT(vp->id) | done_bits; + old_layer_sel_val = vop2_readl(vop2, layer_sel_offset); + cfg_layer_sel_val = vop2->regsbak[layer_sel_offset >> 2]; + /** + * This is rather low probability for miss some done bit. + */ + val |= vop2_readl(vop2, RK3568_REG_CFG_DONE) & 0x7; vop2_writel(vop2, 0, val); + + /** + * Make sure the layer sel is take effect when it's updated. + */ + if (old_layer_sel_val != cfg_layer_sel_val) { + vp->layer_sel_update = true; + vop2_wait_for_fs_by_done_bit_status(vp); + DRM_DEV_DEBUG(vop2->dev, "vp%d need to wait fs as old layer_sel val[0x%x] != new val[0x%x]\n", + vp->id, old_layer_sel_val, cfg_layer_sel_val); + } } static inline void vop2_wb_cfg_done(struct vop2_video_port *vp) @@ -5114,6 +5134,12 @@ static void vop2_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state */ vop2_wait_for_irq_handler(crtc); + /** + * move here is to make sure current fs call function is complete, + * so when layer_sel_update is true, we can skip current vblank correctly. + */ + vp->layer_sel_update = false; + spin_lock_irq(&crtc->dev->event_lock); if (crtc->state->event) { WARN_ON(drm_crtc_vblank_get(crtc) != 0); @@ -5514,8 +5540,10 @@ static irqreturn_t vop2_isr(int irq, void *data) if (active_irqs & FS_FIELD_INTR) { vop2_wb_handler(vp); - drm_crtc_handle_vblank(crtc); - vop2_handle_vblank(vop2, crtc); + if (vp->layer_sel_update == false) { + drm_crtc_handle_vblank(crtc); + vop2_handle_vblank(vop2, crtc); + } active_irqs &= ~FS_FIELD_INTR; ret = IRQ_HANDLED; }