diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 7253175a133a..451899d4fd56 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -130,6 +131,10 @@ struct vop_zpos { int zpos; }; +enum vop_pending { + VOP_PENDING_FB_UNREF, +}; + struct vop_plane_state { struct drm_plane_state base; int format; @@ -182,6 +187,9 @@ struct vop { struct completion wait_update_complete; struct drm_pending_vblank_event *event; + struct drm_flip_work fb_unref_work; + unsigned long pending; + struct completion line_flag_completion; const struct vop_data *data; @@ -2099,7 +2107,11 @@ static void vop_wait_for_irq_handler(struct vop *vop) static void vop_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { + struct drm_atomic_state *old_state = old_crtc_state->state; + struct drm_plane_state *old_plane_state; struct vop *vop = to_vop(crtc); + struct drm_plane *plane; + int i; vop_cfg_update(crtc, old_crtc_state); @@ -2152,6 +2164,19 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc, * signalling flip completion we need to wait for it to finish. */ vop_wait_for_irq_handler(vop); + + for_each_plane_in_state(old_state, plane, old_plane_state, i) { + if (!old_plane_state->fb) + continue; + + if (old_plane_state->fb == plane->state->fb) + continue; + + drm_framebuffer_reference(old_plane_state->fb); + drm_flip_work_queue(&vop->fb_unref_work, old_plane_state->fb); + set_bit(VOP_PENDING_FB_UNREF, &vop->pending); + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + } } static void vop_crtc_atomic_begin(struct drm_crtc *crtc, @@ -2336,6 +2361,15 @@ static const struct drm_crtc_funcs vop_crtc_funcs = { .atomic_destroy_state = vop_crtc_destroy_state, }; +static void vop_fb_unref_worker(struct drm_flip_work *work, void *val) +{ + struct vop *vop = container_of(work, struct vop, fb_unref_work); + struct drm_framebuffer *fb = val; + + drm_crtc_vblank_put(&vop->crtc); + drm_framebuffer_unreference(fb); +} + static void vop_handle_vblank(struct vop *vop) { struct drm_device *drm = vop->drm_dev; @@ -2356,6 +2390,9 @@ static void vop_handle_vblank(struct vop *vop) } if (!completion_done(&vop->wait_update_complete)) complete(&vop->wait_update_complete); + + if (test_and_clear_bit(VOP_PENDING_FB_UNREF, &vop->pending)) + drm_flip_work_commit(&vop->fb_unref_work, system_unbound_wq); } static irqreturn_t vop_isr(int irq, void *data) @@ -2538,6 +2575,9 @@ static int vop_create_crtc(struct vop *vop) goto err_cleanup_crtc; } + drm_flip_work_init(&vop->fb_unref_work, "fb_unref", + vop_fb_unref_worker); + init_completion(&vop->dsp_hold_completion); init_completion(&vop->wait_update_complete); init_completion(&vop->line_flag_completion); @@ -2621,6 +2661,7 @@ static void vop_destroy_crtc(struct vop *vop) * references the CRTC. */ drm_crtc_cleanup(crtc); + drm_flip_work_cleanup(&vop->fb_unref_work); } /*