From 949dbda9675f30a7b8fb370fd201da22536175eb 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_drv.c | 1 + drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 1 + drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 3 ++ drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 32 ++++++++++++++++++-- 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index 91705e7a694c..5fc340ddc2e2 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -592,6 +592,7 @@ static int rockchip_drm_bind(struct device *dev) INIT_LIST_HEAD(&private->psr_list); mutex_init(&private->psr_list_lock); + mutex_init(&private->commit_lock); ret = rockchip_drm_init_iommu(drm_dev); if (ret) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index d96649352b6a..f79368fc8f8c 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -170,6 +170,7 @@ struct rockchip_drm_private { struct drm_mm mm; struct list_head psr_list; struct mutex psr_list_lock; + struct mutex commit_lock; struct drm_property *eotf_prop; struct drm_property *color_space_prop; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index 4b3bb7f459cf..ce697e4ac157 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -144,6 +144,7 @@ static int rockchip_drm_bandwidth_atomic_check(struct drm_device *dev, static void rockchip_drm_atomic_helper_commit_tail_rpm(struct drm_atomic_state *old_state) { struct drm_device *dev = old_state->dev; + struct rockchip_drm_private *prv = dev->dev_private; struct dmcfreq_vop_info vop_info; drm_atomic_helper_commit_modeset_disables(dev, old_state); @@ -154,7 +155,9 @@ static void rockchip_drm_atomic_helper_commit_tail_rpm(struct drm_atomic_state * rockchip_dmcfreq_vop_bandwidth_update(&vop_info); + mutex_lock(&prv->commit_lock); drm_atomic_helper_commit_planes(dev, old_state, DRM_PLANE_COMMIT_ACTIVE_ONLY); + mutex_unlock(&prv->commit_lock); drm_atomic_helper_fake_vblank(old_state); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 185b500a75f0..868a7057a9b0 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -415,6 +415,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; @@ -1033,6 +1034,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, @@ -1052,7 +1056,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) @@ -5290,6 +5310,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); @@ -5750,8 +5776,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; }