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 <hjc@rock-chips.com>
Change-Id: Ief832e2bf7e18567f4ea663843c77f0afbd21cf7
This commit is contained in:
Sandy Huang
2021-05-27 15:21:04 +08:00
committed by Tao Huang
parent 2563d569fe
commit 949dbda967
4 changed files with 35 additions and 2 deletions

View File

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

View File

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

View File

@@ -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);

View File

@@ -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;
}