From 7af58fc74e4348e2a7a953e43771da4210040ab7 Mon Sep 17 00:00:00 2001 From: Mark Yao Date: Fri, 20 Oct 2017 18:32:07 +0800 Subject: [PATCH] drm/rockchip: make multi-display independent on synchronous commit Previous atomic commit force wait for old commit finish, it would make performance bad, if we have two outputs committing at the same time, the two commit will block each other, make things bad. with this patch, difference output commits can be independent, they can commit at the same time. TEST: before this patch: HDMI + MIPI: 40-55fps after this patch: HDMI + MIPI: 60fps Change-Id: Ie6c80f908080a40986d0193af5aff52f1bead455 Signed-off-by: Mark Yao --- drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 4 +- drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 7 ++-- drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 45 +++++++++++++++------ drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 14 +++++-- 4 files changed, 48 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index 8d2901a0b982..8459eccc3033 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -1235,8 +1235,8 @@ static int rockchip_drm_bind(struct device *dev) goto err_free; } - mutex_init(&private->commit.lock); - INIT_WORK(&private->commit.work, rockchip_drm_atomic_work); + mutex_init(&private->commit_lock); + INIT_WORK(&private->commit_work, rockchip_drm_atomic_work); drm_dev->dev_private = private; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index d7171a764531..7f01c908b441 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -66,10 +66,8 @@ struct drm_rockchip_subdrv { }; struct rockchip_atomic_commit { - struct work_struct work; struct drm_atomic_state *state; struct drm_device *dev; - struct mutex lock; size_t bandwidth; }; @@ -150,7 +148,10 @@ struct rockchip_drm_private { const struct rockchip_crtc_funcs *crtc_funcs[ROCKCHIP_MAX_CRTC]; struct drm_atomic_state *state; - struct rockchip_atomic_commit commit; + struct rockchip_atomic_commit *commit; + /* protect async commit */ + struct mutex commit_lock; + struct work_struct commit_work; struct iommu_domain *domain; struct gen_pool *secure_buffer_pool; #ifdef CONFIG_DRM_DMA_SYNC diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index 20e30443ae87..6efb861410ad 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -264,6 +264,8 @@ rockchip_atomic_commit_complete(struct rockchip_atomic_commit *commit) * * See the kerneldoc entries for these three functions for more details. */ + drm_atomic_helper_wait_for_dependencies(state); + drm_atomic_helper_commit_modeset_disables(dev, state); drm_atomic_helper_commit_modeset_enables(dev, state); @@ -274,19 +276,26 @@ rockchip_atomic_commit_complete(struct rockchip_atomic_commit *commit) drm_atomic_helper_commit_planes(dev, state, true); + drm_atomic_helper_commit_hw_done(state); + drm_atomic_helper_wait_for_vblanks(dev, state); drm_atomic_helper_cleanup_planes(dev, state); + drm_atomic_helper_commit_cleanup_done(state); + drm_atomic_state_free(state); + + kfree(commit); } void rockchip_drm_atomic_work(struct work_struct *work) { - struct rockchip_atomic_commit *commit = container_of(work, - struct rockchip_atomic_commit, work); + struct rockchip_drm_private *private = container_of(work, + struct rockchip_drm_private, commit_work); - rockchip_atomic_commit_complete(commit); + rockchip_atomic_commit_complete(private->commit); + private->commit = NULL; } int rockchip_drm_atomic_commit(struct drm_device *dev, @@ -294,10 +303,14 @@ int rockchip_drm_atomic_commit(struct drm_device *dev, bool async) { struct rockchip_drm_private *private = dev->dev_private; - struct rockchip_atomic_commit *commit = &private->commit; + struct rockchip_atomic_commit *commit; size_t bandwidth; int ret; + ret = drm_atomic_helper_setup_commit(state, false); + if (ret) + return ret; + ret = drm_atomic_helper_prepare_planes(dev, state); if (ret) return ret; @@ -311,22 +324,28 @@ int rockchip_drm_atomic_commit(struct drm_device *dev, DRM_ERROR("vop bandwidth too large %zd\n", bandwidth); } - /* serialize outstanding asynchronous commits */ - mutex_lock(&commit->lock); - flush_work(&commit->work); - drm_atomic_helper_swap_state(dev, state); + commit = kmalloc(sizeof(*commit), GFP_KERNEL); + if (!commit) + return -ENOMEM; + commit->dev = dev; commit->state = state; commit->bandwidth = bandwidth; - if (async) - schedule_work(&commit->work); - else - rockchip_atomic_commit_complete(commit); + if (async) { + mutex_lock(&private->commit_lock); - mutex_unlock(&commit->lock); + flush_work(&private->commit_work); + WARN_ON(private->commit); + private->commit = commit; + schedule_work(&private->commit_work); + + mutex_unlock(&private->commit_lock); + } else { + rockchip_atomic_commit_complete(commit); + } return 0; } diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 9371bea21ba1..17abc34b77f3 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -1038,6 +1038,14 @@ static void vop_crtc_disable(struct drm_crtc *crtc) mutex_unlock(&vop->vop_lock); rockchip_clear_system_status(sys_status); + + if (crtc->state->event && !crtc->state->active) { + spin_lock_irq(&crtc->dev->event_lock); + drm_crtc_send_vblank_event(crtc, crtc->state->event); + spin_unlock_irq(&crtc->dev->event_lock); + + crtc->state->event = NULL; + } } static void vop_plane_destroy(struct drm_plane *plane) @@ -2859,15 +2867,13 @@ static void vop_handle_vblank(struct vop *vop) struct drm_crtc *crtc = &vop->crtc; unsigned long flags; + spin_lock_irqsave(&drm->event_lock, flags); if (vop->event) { - spin_lock_irqsave(&drm->event_lock, flags); - drm_crtc_send_vblank_event(crtc, vop->event); drm_crtc_vblank_put(crtc); vop->event = NULL; - - spin_unlock_irqrestore(&drm->event_lock, flags); } + spin_unlock_irqrestore(&drm->event_lock, flags); if (test_and_clear_bit(VOP_PENDING_FB_UNREF, &vop->pending)) drm_flip_work_commit(&vop->fb_unref_work, system_unbound_wq);