Message ID | 1464084653-16684-14-git-send-email-gnuiyl@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Tue, May 24, 2016 at 06:10:52PM +0800, Liu Ying wrote: > To support generic atomic page flip, this patch customizes ->atomic_commit > for async commits. It's now called nonblocking instead of async. Please run s/async/nonblock/ over this patch. -Daniel > > Signed-off-by: Liu Ying <gnuiyl@gmail.com> > --- > drivers/gpu/drm/imx/imx-drm-core.c | 137 +++++++++++++++++++++++++++++++- > drivers/gpu/drm/imx/ipuv3-crtc.c | 156 ++----------------------------------- > 2 files changed, 144 insertions(+), 149 deletions(-) > > diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c > index 2fa04a0..cb521cb 100644 > --- a/drivers/gpu/drm/imx/imx-drm-core.c > +++ b/drivers/gpu/drm/imx/imx-drm-core.c > @@ -15,10 +15,14 @@ > */ > #include <linux/component.h> > #include <linux/device.h> > +#include <linux/dma-buf.h> > #include <linux/fb.h> > #include <linux/module.h> > #include <linux/platform_device.h> > +#include <linux/reservation.h> > +#include <linux/wait.h> > #include <drm/drmP.h> > +#include <drm/drm_atomic.h> > #include <drm/drm_atomic_helper.h> > #include <drm/drm_fb_helper.h> > #include <drm/drm_crtc_helper.h> > @@ -48,6 +52,14 @@ struct imx_drm_device { > struct imx_drm_crtc { > struct drm_crtc *crtc; > struct imx_drm_crtc_helper_funcs imx_drm_helper_funcs; > + wait_queue_head_t commit_wait; > + bool commit_pending; > +}; > + > +struct imx_drm_commit { > + struct work_struct work; > + struct drm_device *dev; > + struct drm_atomic_state *state; > }; > > #if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION) > @@ -168,11 +180,132 @@ static void imx_drm_output_poll_changed(struct drm_device *drm) > drm_fbdev_cma_hotplug_event(imxdrm->fbhelper); > } > > +static void imx_drm_atomic_complete(struct imx_drm_commit *commit, bool async) > +{ > + struct drm_device *dev = commit->dev; > + struct imx_drm_device *imxdrm = dev->dev_private; > + struct imx_drm_crtc *imx_crtc; > + struct drm_atomic_state *old_state = commit->state; > + struct drm_crtc *crtc; > + struct drm_crtc_state *old_crtc_state; > + struct drm_plane_state *plane_state; > + struct drm_gem_cma_object *cma_obj; > + struct fence *excl; > + unsigned shared_count; > + struct fence **shared; > + unsigned int i, j; > + int ret; > + > + /* Wait for fences. */ > + for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { > + if (crtc->state->event) { > + plane_state = crtc->primary->state; > + cma_obj = drm_fb_cma_get_gem_obj(plane_state->fb, 0); > + if (cma_obj->base.dma_buf) { > + ret = reservation_object_get_fences_rcu( > + cma_obj->base.dma_buf->resv, &excl, > + &shared_count, &shared); > + if (unlikely(ret)) > + DRM_ERROR("failed to get fences " > + "for buffer\n"); > + > + if (excl) { > + fence_wait(excl, false); > + fence_put(excl); > + } > + for (j = 0; j < shared_count; i++) { > + fence_wait(shared[j], false); > + fence_put(shared[j]); > + } > + } > + } > + } > + > + /* Apply the atomic update. */ > + drm_atomic_helper_commit_modeset_disables(dev, old_state); > + drm_atomic_helper_commit_modeset_enables(dev, old_state); > + drm_atomic_helper_commit_planes(dev, old_state, false); > + drm_atomic_helper_wait_for_vblanks(dev, old_state); > + drm_atomic_helper_cleanup_planes(dev, old_state); > + > + if (async) > + for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { > + imx_crtc = imxdrm->crtc[i]; > + > + /* Complete the commit, wake up any waiter. */ > + spin_lock(&imx_crtc->commit_wait.lock); > + imx_crtc->commit_pending = false; > + wake_up_all_locked(&imx_crtc->commit_wait); > + spin_unlock(&imx_crtc->commit_wait.lock); > + } > + > + drm_atomic_state_free(old_state); > + > + kfree(commit); > +} > + > +static void imx_drm_atomic_work(struct work_struct *work) > +{ > + struct imx_drm_commit *commit = > + container_of(work, struct imx_drm_commit, work); > + > + imx_drm_atomic_complete(commit, true); > +} > + > +static int imx_drm_atomic_commit(struct drm_device *dev, > + struct drm_atomic_state *state, bool async) > +{ > + struct imx_drm_device *imxdrm = dev->dev_private; > + struct imx_drm_crtc *imx_crtc; > + struct imx_drm_commit *commit; > + struct drm_crtc *crtc; > + struct drm_crtc_state *crtc_state; > + unsigned int i; > + int ret; > + > + commit = kzalloc(sizeof(*commit), GFP_KERNEL); > + if (commit == NULL) > + return -ENOMEM; > + > + commit->dev = dev; > + commit->state = state; > + > + if (async) { > + for_each_crtc_in_state(state, crtc, crtc_state, i) { > + imx_crtc = imxdrm->crtc[i]; > + > + spin_lock(&imx_crtc->commit_wait.lock); > + ret = wait_event_interruptible_locked( > + imx_crtc->commit_wait, > + !imx_crtc->commit_pending); > + if (ret == 0) > + imx_crtc->commit_pending = true; > + spin_unlock(&imx_crtc->commit_wait.lock); > + > + if (ret) { > + kfree(commit); > + return ret; > + } > + } > + > + INIT_WORK(&commit->work, imx_drm_atomic_work); > + } > + > + drm_atomic_helper_swap_state(dev, state); > + > + if (async) > + schedule_work(&commit->work); > + else > + imx_drm_atomic_complete(commit, async); > + > + return 0; > +} > + > static const struct drm_mode_config_funcs imx_drm_mode_config_funcs = { > .fb_create = drm_fb_cma_create, > .output_poll_changed = imx_drm_output_poll_changed, > .atomic_check = drm_atomic_helper_check, > - .atomic_commit = drm_atomic_helper_commit, > + .atomic_commit = imx_drm_atomic_commit, > }; > > /* > @@ -315,6 +448,8 @@ int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc, > imx_drm_crtc->imx_drm_helper_funcs = *imx_drm_helper_funcs; > imx_drm_crtc->crtc = crtc; > > + init_waitqueue_head(&imx_drm_crtc->commit_wait); > + > crtc->port = port; > > imxdrm->crtc[imxdrm->pipes++] = imx_drm_crtc; > diff --git a/drivers/gpu/drm/imx/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3-crtc.c > index 4d1b911..dcbeb56 100644 > --- a/drivers/gpu/drm/imx/ipuv3-crtc.c > +++ b/drivers/gpu/drm/imx/ipuv3-crtc.c > @@ -24,8 +24,6 @@ > #include <linux/fb.h> > #include <linux/clk.h> > #include <linux/errno.h> > -#include <linux/reservation.h> > -#include <linux/dma-buf.h> > #include <drm/drm_gem_cma_helper.h> > #include <drm/drm_fb_cma_helper.h> > > @@ -35,23 +33,6 @@ > > #define DRIVER_DESC "i.MX IPUv3 Graphics" > > -enum ipu_flip_status { > - IPU_FLIP_NONE, > - IPU_FLIP_PENDING, > - IPU_FLIP_SUBMITTED, > -}; > - > -struct ipu_flip_work { > - struct work_struct unref_work; > - struct drm_gem_object *bo; > - struct drm_pending_vblank_event *page_flip_event; > - struct work_struct fence_work; > - struct ipu_crtc *crtc; > - struct fence *excl; > - unsigned shared_count; > - struct fence **shared; > -}; > - > struct ipu_crtc { > struct device *dev; > struct drm_crtc base; > @@ -62,9 +43,6 @@ struct ipu_crtc { > > struct ipu_dc *dc; > struct ipu_di *di; > - enum ipu_flip_status flip_state; > - struct workqueue_struct *flip_queue; > - struct ipu_flip_work *flip_work; > int irq; > }; > > @@ -94,150 +72,35 @@ static void ipu_crtc_disable(struct drm_crtc *crtc) > drm_crtc_vblank_off(&ipu_crtc->base); > } > > -static void ipu_flip_unref_work_func(struct work_struct *__work) > -{ > - struct ipu_flip_work *work = > - container_of(__work, struct ipu_flip_work, unref_work); > - > - drm_gem_object_unreference_unlocked(work->bo); > - kfree(work); > -} > - > -static void ipu_flip_fence_work_func(struct work_struct *__work) > -{ > - struct ipu_flip_work *work = > - container_of(__work, struct ipu_flip_work, fence_work); > - int i; > - > - /* wait for all fences attached to the FB obj to signal */ > - if (work->excl) { > - fence_wait(work->excl, false); > - fence_put(work->excl); > - } > - for (i = 0; i < work->shared_count; i++) { > - fence_wait(work->shared[i], false); > - fence_put(work->shared[i]); > - } > - > - work->crtc->flip_state = IPU_FLIP_SUBMITTED; > -} > - > -static int ipu_page_flip(struct drm_crtc *crtc, > - struct drm_framebuffer *fb, > - struct drm_pending_vblank_event *event, > - uint32_t page_flip_flags) > -{ > - struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0); > - struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); > - struct ipu_flip_work *flip_work; > - int ret; > - > - if (ipu_crtc->flip_state != IPU_FLIP_NONE) > - return -EBUSY; > - > - ret = imx_drm_crtc_vblank_get(ipu_crtc->imx_crtc); > - if (ret) { > - dev_dbg(ipu_crtc->dev, "failed to acquire vblank counter\n"); > - list_del(&event->base.link); > - > - return ret; > - } > - > - flip_work = kzalloc(sizeof *flip_work, GFP_KERNEL); > - if (!flip_work) { > - ret = -ENOMEM; > - goto put_vblank; > - } > - INIT_WORK(&flip_work->unref_work, ipu_flip_unref_work_func); > - flip_work->page_flip_event = event; > - > - /* get BO backing the old framebuffer and take a reference */ > - flip_work->bo = &drm_fb_cma_get_gem_obj(crtc->primary->fb, 0)->base; > - drm_gem_object_reference(flip_work->bo); > - > - ipu_crtc->flip_work = flip_work; > - /* > - * If the object has a DMABUF attached, we need to wait on its fences > - * if there are any. > - */ > - if (cma_obj->base.dma_buf) { > - INIT_WORK(&flip_work->fence_work, ipu_flip_fence_work_func); > - flip_work->crtc = ipu_crtc; > - > - ret = reservation_object_get_fences_rcu( > - cma_obj->base.dma_buf->resv, &flip_work->excl, > - &flip_work->shared_count, &flip_work->shared); > - > - if (unlikely(ret)) { > - DRM_ERROR("failed to get fences for buffer\n"); > - goto free_flip_work; > - } > - > - /* No need to queue the worker if the are no fences */ > - if (!flip_work->excl && !flip_work->shared_count) { > - ipu_crtc->flip_state = IPU_FLIP_SUBMITTED; > - } else { > - ipu_crtc->flip_state = IPU_FLIP_PENDING; > - queue_work(ipu_crtc->flip_queue, > - &flip_work->fence_work); > - } > - } else { > - ipu_crtc->flip_state = IPU_FLIP_SUBMITTED; > - } > - > - if (crtc->primary->state) > - drm_atomic_set_fb_for_plane(crtc->primary->state, fb); > - > - return 0; > - > -free_flip_work: > - drm_gem_object_unreference_unlocked(flip_work->bo); > - kfree(flip_work); > - ipu_crtc->flip_work = NULL; > -put_vblank: > - imx_drm_crtc_vblank_put(ipu_crtc->imx_crtc); > - > - return ret; > -} > - > static const struct drm_crtc_funcs ipu_crtc_funcs = { > .set_config = drm_atomic_helper_set_config, > .destroy = drm_crtc_cleanup, > - .page_flip = ipu_page_flip, > + .page_flip = drm_atomic_helper_page_flip, > .reset = drm_atomic_helper_crtc_reset, > .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, > .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, > }; > > -static void ipu_crtc_handle_pageflip(struct ipu_crtc *ipu_crtc) > +static void ipu_crtc_handle_pageflip(struct drm_crtc *crtc) > { > + struct drm_device *drm = crtc->dev; > unsigned long flags; > - struct drm_device *drm = ipu_crtc->base.dev; > - struct ipu_flip_work *work = ipu_crtc->flip_work; > > spin_lock_irqsave(&drm->event_lock, flags); > - if (work->page_flip_event) > - drm_crtc_send_vblank_event(&ipu_crtc->base, > - work->page_flip_event); > - imx_drm_crtc_vblank_put(ipu_crtc->imx_crtc); > + drm_crtc_send_vblank_event(crtc, crtc->state->event); > + crtc->state->event = NULL; > spin_unlock_irqrestore(&drm->event_lock, flags); > } > > static irqreturn_t ipu_irq_handler(int irq, void *dev_id) > { > struct ipu_crtc *ipu_crtc = dev_id; > + struct drm_crtc *crtc = &ipu_crtc->base; > > imx_drm_handle_vblank(ipu_crtc->imx_crtc); > > - if (ipu_crtc->flip_state == IPU_FLIP_SUBMITTED) { > - struct ipu_plane *plane = ipu_crtc->plane[0]; > - > - ipu_plane_set_base(plane, ipu_crtc->base.primary->fb); > - ipu_crtc_handle_pageflip(ipu_crtc); > - queue_work(ipu_crtc->flip_queue, > - &ipu_crtc->flip_work->unref_work); > - ipu_crtc->flip_state = IPU_FLIP_NONE; > - } > + if (crtc->state->event) > + ipu_crtc_handle_pageflip(crtc); > > return IRQ_HANDLED; > } > @@ -452,8 +315,6 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc, > > ipu_plane_put_resources(ipu_crtc->plane[0]); > > - ipu_crtc->flip_queue = create_singlethread_workqueue("ipu-crtc-flip"); > - > return 0; > > err_put_plane_res: > @@ -495,7 +356,6 @@ static void ipu_drm_unbind(struct device *dev, struct device *master, > > imx_drm_remove_crtc(ipu_crtc->imx_crtc); > > - destroy_workqueue(ipu_crtc->flip_queue); > ipu_put_resources(ipu_crtc); > } > > -- > 2.7.4 > > _______________________________________________ > dri-devel mailing list > dri-devel@lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/dri-devel
On Tue, May 24, 2016 at 7:11 PM, Daniel Vetter <daniel@ffwll.ch> wrote: > On Tue, May 24, 2016 at 06:10:52PM +0800, Liu Ying wrote: >> To support generic atomic page flip, this patch customizes ->atomic_commit >> for async commits. > > It's now called nonblocking instead of async. Please run s/async/nonblock/ > over this patch. Ok. Will do. Regards, Liu Ying > -Daniel > >> >> Signed-off-by: Liu Ying <gnuiyl@gmail.com> >> --- >> drivers/gpu/drm/imx/imx-drm-core.c | 137 +++++++++++++++++++++++++++++++- >> drivers/gpu/drm/imx/ipuv3-crtc.c | 156 ++----------------------------------- >> 2 files changed, 144 insertions(+), 149 deletions(-) >> >> diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c >> index 2fa04a0..cb521cb 100644 >> --- a/drivers/gpu/drm/imx/imx-drm-core.c >> +++ b/drivers/gpu/drm/imx/imx-drm-core.c >> @@ -15,10 +15,14 @@ >> */ >> #include <linux/component.h> >> #include <linux/device.h> >> +#include <linux/dma-buf.h> >> #include <linux/fb.h> >> #include <linux/module.h> >> #include <linux/platform_device.h> >> +#include <linux/reservation.h> >> +#include <linux/wait.h> >> #include <drm/drmP.h> >> +#include <drm/drm_atomic.h> >> #include <drm/drm_atomic_helper.h> >> #include <drm/drm_fb_helper.h> >> #include <drm/drm_crtc_helper.h> >> @@ -48,6 +52,14 @@ struct imx_drm_device { >> struct imx_drm_crtc { >> struct drm_crtc *crtc; >> struct imx_drm_crtc_helper_funcs imx_drm_helper_funcs; >> + wait_queue_head_t commit_wait; >> + bool commit_pending; >> +}; >> + >> +struct imx_drm_commit { >> + struct work_struct work; >> + struct drm_device *dev; >> + struct drm_atomic_state *state; >> }; >> >> #if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION) >> @@ -168,11 +180,132 @@ static void imx_drm_output_poll_changed(struct drm_device *drm) >> drm_fbdev_cma_hotplug_event(imxdrm->fbhelper); >> } >> >> +static void imx_drm_atomic_complete(struct imx_drm_commit *commit, bool async) >> +{ >> + struct drm_device *dev = commit->dev; >> + struct imx_drm_device *imxdrm = dev->dev_private; >> + struct imx_drm_crtc *imx_crtc; >> + struct drm_atomic_state *old_state = commit->state; >> + struct drm_crtc *crtc; >> + struct drm_crtc_state *old_crtc_state; >> + struct drm_plane_state *plane_state; >> + struct drm_gem_cma_object *cma_obj; >> + struct fence *excl; >> + unsigned shared_count; >> + struct fence **shared; >> + unsigned int i, j; >> + int ret; >> + >> + /* Wait for fences. */ >> + for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { >> + if (crtc->state->event) { >> + plane_state = crtc->primary->state; >> + cma_obj = drm_fb_cma_get_gem_obj(plane_state->fb, 0); >> + if (cma_obj->base.dma_buf) { >> + ret = reservation_object_get_fences_rcu( >> + cma_obj->base.dma_buf->resv, &excl, >> + &shared_count, &shared); >> + if (unlikely(ret)) >> + DRM_ERROR("failed to get fences " >> + "for buffer\n"); >> + >> + if (excl) { >> + fence_wait(excl, false); >> + fence_put(excl); >> + } >> + for (j = 0; j < shared_count; i++) { >> + fence_wait(shared[j], false); >> + fence_put(shared[j]); >> + } >> + } >> + } >> + } >> + >> + /* Apply the atomic update. */ >> + drm_atomic_helper_commit_modeset_disables(dev, old_state); >> + drm_atomic_helper_commit_modeset_enables(dev, old_state); >> + drm_atomic_helper_commit_planes(dev, old_state, false); >> + drm_atomic_helper_wait_for_vblanks(dev, old_state); >> + drm_atomic_helper_cleanup_planes(dev, old_state); >> + >> + if (async) >> + for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { >> + imx_crtc = imxdrm->crtc[i]; >> + >> + /* Complete the commit, wake up any waiter. */ >> + spin_lock(&imx_crtc->commit_wait.lock); >> + imx_crtc->commit_pending = false; >> + wake_up_all_locked(&imx_crtc->commit_wait); >> + spin_unlock(&imx_crtc->commit_wait.lock); >> + } >> + >> + drm_atomic_state_free(old_state); >> + >> + kfree(commit); >> +} >> + >> +static void imx_drm_atomic_work(struct work_struct *work) >> +{ >> + struct imx_drm_commit *commit = >> + container_of(work, struct imx_drm_commit, work); >> + >> + imx_drm_atomic_complete(commit, true); >> +} >> + >> +static int imx_drm_atomic_commit(struct drm_device *dev, >> + struct drm_atomic_state *state, bool async) >> +{ >> + struct imx_drm_device *imxdrm = dev->dev_private; >> + struct imx_drm_crtc *imx_crtc; >> + struct imx_drm_commit *commit; >> + struct drm_crtc *crtc; >> + struct drm_crtc_state *crtc_state; >> + unsigned int i; >> + int ret; >> + >> + commit = kzalloc(sizeof(*commit), GFP_KERNEL); >> + if (commit == NULL) >> + return -ENOMEM; >> + >> + commit->dev = dev; >> + commit->state = state; >> + >> + if (async) { >> + for_each_crtc_in_state(state, crtc, crtc_state, i) { >> + imx_crtc = imxdrm->crtc[i]; >> + >> + spin_lock(&imx_crtc->commit_wait.lock); >> + ret = wait_event_interruptible_locked( >> + imx_crtc->commit_wait, >> + !imx_crtc->commit_pending); >> + if (ret == 0) >> + imx_crtc->commit_pending = true; >> + spin_unlock(&imx_crtc->commit_wait.lock); >> + >> + if (ret) { >> + kfree(commit); >> + return ret; >> + } >> + } >> + >> + INIT_WORK(&commit->work, imx_drm_atomic_work); >> + } >> + >> + drm_atomic_helper_swap_state(dev, state); >> + >> + if (async) >> + schedule_work(&commit->work); >> + else >> + imx_drm_atomic_complete(commit, async); >> + >> + return 0; >> +} >> + >> static const struct drm_mode_config_funcs imx_drm_mode_config_funcs = { >> .fb_create = drm_fb_cma_create, >> .output_poll_changed = imx_drm_output_poll_changed, >> .atomic_check = drm_atomic_helper_check, >> - .atomic_commit = drm_atomic_helper_commit, >> + .atomic_commit = imx_drm_atomic_commit, >> }; >> >> /* >> @@ -315,6 +448,8 @@ int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc, >> imx_drm_crtc->imx_drm_helper_funcs = *imx_drm_helper_funcs; >> imx_drm_crtc->crtc = crtc; >> >> + init_waitqueue_head(&imx_drm_crtc->commit_wait); >> + >> crtc->port = port; >> >> imxdrm->crtc[imxdrm->pipes++] = imx_drm_crtc; >> diff --git a/drivers/gpu/drm/imx/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3-crtc.c >> index 4d1b911..dcbeb56 100644 >> --- a/drivers/gpu/drm/imx/ipuv3-crtc.c >> +++ b/drivers/gpu/drm/imx/ipuv3-crtc.c >> @@ -24,8 +24,6 @@ >> #include <linux/fb.h> >> #include <linux/clk.h> >> #include <linux/errno.h> >> -#include <linux/reservation.h> >> -#include <linux/dma-buf.h> >> #include <drm/drm_gem_cma_helper.h> >> #include <drm/drm_fb_cma_helper.h> >> >> @@ -35,23 +33,6 @@ >> >> #define DRIVER_DESC "i.MX IPUv3 Graphics" >> >> -enum ipu_flip_status { >> - IPU_FLIP_NONE, >> - IPU_FLIP_PENDING, >> - IPU_FLIP_SUBMITTED, >> -}; >> - >> -struct ipu_flip_work { >> - struct work_struct unref_work; >> - struct drm_gem_object *bo; >> - struct drm_pending_vblank_event *page_flip_event; >> - struct work_struct fence_work; >> - struct ipu_crtc *crtc; >> - struct fence *excl; >> - unsigned shared_count; >> - struct fence **shared; >> -}; >> - >> struct ipu_crtc { >> struct device *dev; >> struct drm_crtc base; >> @@ -62,9 +43,6 @@ struct ipu_crtc { >> >> struct ipu_dc *dc; >> struct ipu_di *di; >> - enum ipu_flip_status flip_state; >> - struct workqueue_struct *flip_queue; >> - struct ipu_flip_work *flip_work; >> int irq; >> }; >> >> @@ -94,150 +72,35 @@ static void ipu_crtc_disable(struct drm_crtc *crtc) >> drm_crtc_vblank_off(&ipu_crtc->base); >> } >> >> -static void ipu_flip_unref_work_func(struct work_struct *__work) >> -{ >> - struct ipu_flip_work *work = >> - container_of(__work, struct ipu_flip_work, unref_work); >> - >> - drm_gem_object_unreference_unlocked(work->bo); >> - kfree(work); >> -} >> - >> -static void ipu_flip_fence_work_func(struct work_struct *__work) >> -{ >> - struct ipu_flip_work *work = >> - container_of(__work, struct ipu_flip_work, fence_work); >> - int i; >> - >> - /* wait for all fences attached to the FB obj to signal */ >> - if (work->excl) { >> - fence_wait(work->excl, false); >> - fence_put(work->excl); >> - } >> - for (i = 0; i < work->shared_count; i++) { >> - fence_wait(work->shared[i], false); >> - fence_put(work->shared[i]); >> - } >> - >> - work->crtc->flip_state = IPU_FLIP_SUBMITTED; >> -} >> - >> -static int ipu_page_flip(struct drm_crtc *crtc, >> - struct drm_framebuffer *fb, >> - struct drm_pending_vblank_event *event, >> - uint32_t page_flip_flags) >> -{ >> - struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0); >> - struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); >> - struct ipu_flip_work *flip_work; >> - int ret; >> - >> - if (ipu_crtc->flip_state != IPU_FLIP_NONE) >> - return -EBUSY; >> - >> - ret = imx_drm_crtc_vblank_get(ipu_crtc->imx_crtc); >> - if (ret) { >> - dev_dbg(ipu_crtc->dev, "failed to acquire vblank counter\n"); >> - list_del(&event->base.link); >> - >> - return ret; >> - } >> - >> - flip_work = kzalloc(sizeof *flip_work, GFP_KERNEL); >> - if (!flip_work) { >> - ret = -ENOMEM; >> - goto put_vblank; >> - } >> - INIT_WORK(&flip_work->unref_work, ipu_flip_unref_work_func); >> - flip_work->page_flip_event = event; >> - >> - /* get BO backing the old framebuffer and take a reference */ >> - flip_work->bo = &drm_fb_cma_get_gem_obj(crtc->primary->fb, 0)->base; >> - drm_gem_object_reference(flip_work->bo); >> - >> - ipu_crtc->flip_work = flip_work; >> - /* >> - * If the object has a DMABUF attached, we need to wait on its fences >> - * if there are any. >> - */ >> - if (cma_obj->base.dma_buf) { >> - INIT_WORK(&flip_work->fence_work, ipu_flip_fence_work_func); >> - flip_work->crtc = ipu_crtc; >> - >> - ret = reservation_object_get_fences_rcu( >> - cma_obj->base.dma_buf->resv, &flip_work->excl, >> - &flip_work->shared_count, &flip_work->shared); >> - >> - if (unlikely(ret)) { >> - DRM_ERROR("failed to get fences for buffer\n"); >> - goto free_flip_work; >> - } >> - >> - /* No need to queue the worker if the are no fences */ >> - if (!flip_work->excl && !flip_work->shared_count) { >> - ipu_crtc->flip_state = IPU_FLIP_SUBMITTED; >> - } else { >> - ipu_crtc->flip_state = IPU_FLIP_PENDING; >> - queue_work(ipu_crtc->flip_queue, >> - &flip_work->fence_work); >> - } >> - } else { >> - ipu_crtc->flip_state = IPU_FLIP_SUBMITTED; >> - } >> - >> - if (crtc->primary->state) >> - drm_atomic_set_fb_for_plane(crtc->primary->state, fb); >> - >> - return 0; >> - >> -free_flip_work: >> - drm_gem_object_unreference_unlocked(flip_work->bo); >> - kfree(flip_work); >> - ipu_crtc->flip_work = NULL; >> -put_vblank: >> - imx_drm_crtc_vblank_put(ipu_crtc->imx_crtc); >> - >> - return ret; >> -} >> - >> static const struct drm_crtc_funcs ipu_crtc_funcs = { >> .set_config = drm_atomic_helper_set_config, >> .destroy = drm_crtc_cleanup, >> - .page_flip = ipu_page_flip, >> + .page_flip = drm_atomic_helper_page_flip, >> .reset = drm_atomic_helper_crtc_reset, >> .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, >> .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, >> }; >> >> -static void ipu_crtc_handle_pageflip(struct ipu_crtc *ipu_crtc) >> +static void ipu_crtc_handle_pageflip(struct drm_crtc *crtc) >> { >> + struct drm_device *drm = crtc->dev; >> unsigned long flags; >> - struct drm_device *drm = ipu_crtc->base.dev; >> - struct ipu_flip_work *work = ipu_crtc->flip_work; >> >> spin_lock_irqsave(&drm->event_lock, flags); >> - if (work->page_flip_event) >> - drm_crtc_send_vblank_event(&ipu_crtc->base, >> - work->page_flip_event); >> - imx_drm_crtc_vblank_put(ipu_crtc->imx_crtc); >> + drm_crtc_send_vblank_event(crtc, crtc->state->event); >> + crtc->state->event = NULL; >> spin_unlock_irqrestore(&drm->event_lock, flags); >> } >> >> static irqreturn_t ipu_irq_handler(int irq, void *dev_id) >> { >> struct ipu_crtc *ipu_crtc = dev_id; >> + struct drm_crtc *crtc = &ipu_crtc->base; >> >> imx_drm_handle_vblank(ipu_crtc->imx_crtc); >> >> - if (ipu_crtc->flip_state == IPU_FLIP_SUBMITTED) { >> - struct ipu_plane *plane = ipu_crtc->plane[0]; >> - >> - ipu_plane_set_base(plane, ipu_crtc->base.primary->fb); >> - ipu_crtc_handle_pageflip(ipu_crtc); >> - queue_work(ipu_crtc->flip_queue, >> - &ipu_crtc->flip_work->unref_work); >> - ipu_crtc->flip_state = IPU_FLIP_NONE; >> - } >> + if (crtc->state->event) >> + ipu_crtc_handle_pageflip(crtc); >> >> return IRQ_HANDLED; >> } >> @@ -452,8 +315,6 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc, >> >> ipu_plane_put_resources(ipu_crtc->plane[0]); >> >> - ipu_crtc->flip_queue = create_singlethread_workqueue("ipu-crtc-flip"); >> - >> return 0; >> >> err_put_plane_res: >> @@ -495,7 +356,6 @@ static void ipu_drm_unbind(struct device *dev, struct device *master, >> >> imx_drm_remove_crtc(ipu_crtc->imx_crtc); >> >> - destroy_workqueue(ipu_crtc->flip_queue); >> ipu_put_resources(ipu_crtc); >> } >> >> -- >> 2.7.4 >> >> _______________________________________________ >> dri-devel mailing list >> dri-devel@lists.freedesktop.org >> https://lists.freedesktop.org/mailman/listinfo/dri-devel > > -- > Daniel Vetter > Software Engineer, Intel Corporation > http://blog.ffwll.ch
diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c index 2fa04a0..cb521cb 100644 --- a/drivers/gpu/drm/imx/imx-drm-core.c +++ b/drivers/gpu/drm/imx/imx-drm-core.c @@ -15,10 +15,14 @@ */ #include <linux/component.h> #include <linux/device.h> +#include <linux/dma-buf.h> #include <linux/fb.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/reservation.h> +#include <linux/wait.h> #include <drm/drmP.h> +#include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_fb_helper.h> #include <drm/drm_crtc_helper.h> @@ -48,6 +52,14 @@ struct imx_drm_device { struct imx_drm_crtc { struct drm_crtc *crtc; struct imx_drm_crtc_helper_funcs imx_drm_helper_funcs; + wait_queue_head_t commit_wait; + bool commit_pending; +}; + +struct imx_drm_commit { + struct work_struct work; + struct drm_device *dev; + struct drm_atomic_state *state; }; #if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION) @@ -168,11 +180,132 @@ static void imx_drm_output_poll_changed(struct drm_device *drm) drm_fbdev_cma_hotplug_event(imxdrm->fbhelper); } +static void imx_drm_atomic_complete(struct imx_drm_commit *commit, bool async) +{ + struct drm_device *dev = commit->dev; + struct imx_drm_device *imxdrm = dev->dev_private; + struct imx_drm_crtc *imx_crtc; + struct drm_atomic_state *old_state = commit->state; + struct drm_crtc *crtc; + struct drm_crtc_state *old_crtc_state; + struct drm_plane_state *plane_state; + struct drm_gem_cma_object *cma_obj; + struct fence *excl; + unsigned shared_count; + struct fence **shared; + unsigned int i, j; + int ret; + + /* Wait for fences. */ + for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { + if (crtc->state->event) { + plane_state = crtc->primary->state; + cma_obj = drm_fb_cma_get_gem_obj(plane_state->fb, 0); + if (cma_obj->base.dma_buf) { + ret = reservation_object_get_fences_rcu( + cma_obj->base.dma_buf->resv, &excl, + &shared_count, &shared); + if (unlikely(ret)) + DRM_ERROR("failed to get fences " + "for buffer\n"); + + if (excl) { + fence_wait(excl, false); + fence_put(excl); + } + for (j = 0; j < shared_count; i++) { + fence_wait(shared[j], false); + fence_put(shared[j]); + } + } + } + } + + /* Apply the atomic update. */ + drm_atomic_helper_commit_modeset_disables(dev, old_state); + drm_atomic_helper_commit_modeset_enables(dev, old_state); + drm_atomic_helper_commit_planes(dev, old_state, false); + drm_atomic_helper_wait_for_vblanks(dev, old_state); + drm_atomic_helper_cleanup_planes(dev, old_state); + + if (async) + for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { + imx_crtc = imxdrm->crtc[i]; + + /* Complete the commit, wake up any waiter. */ + spin_lock(&imx_crtc->commit_wait.lock); + imx_crtc->commit_pending = false; + wake_up_all_locked(&imx_crtc->commit_wait); + spin_unlock(&imx_crtc->commit_wait.lock); + } + + drm_atomic_state_free(old_state); + + kfree(commit); +} + +static void imx_drm_atomic_work(struct work_struct *work) +{ + struct imx_drm_commit *commit = + container_of(work, struct imx_drm_commit, work); + + imx_drm_atomic_complete(commit, true); +} + +static int imx_drm_atomic_commit(struct drm_device *dev, + struct drm_atomic_state *state, bool async) +{ + struct imx_drm_device *imxdrm = dev->dev_private; + struct imx_drm_crtc *imx_crtc; + struct imx_drm_commit *commit; + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + unsigned int i; + int ret; + + commit = kzalloc(sizeof(*commit), GFP_KERNEL); + if (commit == NULL) + return -ENOMEM; + + commit->dev = dev; + commit->state = state; + + if (async) { + for_each_crtc_in_state(state, crtc, crtc_state, i) { + imx_crtc = imxdrm->crtc[i]; + + spin_lock(&imx_crtc->commit_wait.lock); + ret = wait_event_interruptible_locked( + imx_crtc->commit_wait, + !imx_crtc->commit_pending); + if (ret == 0) + imx_crtc->commit_pending = true; + spin_unlock(&imx_crtc->commit_wait.lock); + + if (ret) { + kfree(commit); + return ret; + } + } + + INIT_WORK(&commit->work, imx_drm_atomic_work); + } + + drm_atomic_helper_swap_state(dev, state); + + if (async) + schedule_work(&commit->work); + else + imx_drm_atomic_complete(commit, async); + + return 0; +} + static const struct drm_mode_config_funcs imx_drm_mode_config_funcs = { .fb_create = drm_fb_cma_create, .output_poll_changed = imx_drm_output_poll_changed, .atomic_check = drm_atomic_helper_check, - .atomic_commit = drm_atomic_helper_commit, + .atomic_commit = imx_drm_atomic_commit, }; /* @@ -315,6 +448,8 @@ int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc, imx_drm_crtc->imx_drm_helper_funcs = *imx_drm_helper_funcs; imx_drm_crtc->crtc = crtc; + init_waitqueue_head(&imx_drm_crtc->commit_wait); + crtc->port = port; imxdrm->crtc[imxdrm->pipes++] = imx_drm_crtc; diff --git a/drivers/gpu/drm/imx/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3-crtc.c index 4d1b911..dcbeb56 100644 --- a/drivers/gpu/drm/imx/ipuv3-crtc.c +++ b/drivers/gpu/drm/imx/ipuv3-crtc.c @@ -24,8 +24,6 @@ #include <linux/fb.h> #include <linux/clk.h> #include <linux/errno.h> -#include <linux/reservation.h> -#include <linux/dma-buf.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_fb_cma_helper.h> @@ -35,23 +33,6 @@ #define DRIVER_DESC "i.MX IPUv3 Graphics" -enum ipu_flip_status { - IPU_FLIP_NONE, - IPU_FLIP_PENDING, - IPU_FLIP_SUBMITTED, -}; - -struct ipu_flip_work { - struct work_struct unref_work; - struct drm_gem_object *bo; - struct drm_pending_vblank_event *page_flip_event; - struct work_struct fence_work; - struct ipu_crtc *crtc; - struct fence *excl; - unsigned shared_count; - struct fence **shared; -}; - struct ipu_crtc { struct device *dev; struct drm_crtc base; @@ -62,9 +43,6 @@ struct ipu_crtc { struct ipu_dc *dc; struct ipu_di *di; - enum ipu_flip_status flip_state; - struct workqueue_struct *flip_queue; - struct ipu_flip_work *flip_work; int irq; }; @@ -94,150 +72,35 @@ static void ipu_crtc_disable(struct drm_crtc *crtc) drm_crtc_vblank_off(&ipu_crtc->base); } -static void ipu_flip_unref_work_func(struct work_struct *__work) -{ - struct ipu_flip_work *work = - container_of(__work, struct ipu_flip_work, unref_work); - - drm_gem_object_unreference_unlocked(work->bo); - kfree(work); -} - -static void ipu_flip_fence_work_func(struct work_struct *__work) -{ - struct ipu_flip_work *work = - container_of(__work, struct ipu_flip_work, fence_work); - int i; - - /* wait for all fences attached to the FB obj to signal */ - if (work->excl) { - fence_wait(work->excl, false); - fence_put(work->excl); - } - for (i = 0; i < work->shared_count; i++) { - fence_wait(work->shared[i], false); - fence_put(work->shared[i]); - } - - work->crtc->flip_state = IPU_FLIP_SUBMITTED; -} - -static int ipu_page_flip(struct drm_crtc *crtc, - struct drm_framebuffer *fb, - struct drm_pending_vblank_event *event, - uint32_t page_flip_flags) -{ - struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0); - struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); - struct ipu_flip_work *flip_work; - int ret; - - if (ipu_crtc->flip_state != IPU_FLIP_NONE) - return -EBUSY; - - ret = imx_drm_crtc_vblank_get(ipu_crtc->imx_crtc); - if (ret) { - dev_dbg(ipu_crtc->dev, "failed to acquire vblank counter\n"); - list_del(&event->base.link); - - return ret; - } - - flip_work = kzalloc(sizeof *flip_work, GFP_KERNEL); - if (!flip_work) { - ret = -ENOMEM; - goto put_vblank; - } - INIT_WORK(&flip_work->unref_work, ipu_flip_unref_work_func); - flip_work->page_flip_event = event; - - /* get BO backing the old framebuffer and take a reference */ - flip_work->bo = &drm_fb_cma_get_gem_obj(crtc->primary->fb, 0)->base; - drm_gem_object_reference(flip_work->bo); - - ipu_crtc->flip_work = flip_work; - /* - * If the object has a DMABUF attached, we need to wait on its fences - * if there are any. - */ - if (cma_obj->base.dma_buf) { - INIT_WORK(&flip_work->fence_work, ipu_flip_fence_work_func); - flip_work->crtc = ipu_crtc; - - ret = reservation_object_get_fences_rcu( - cma_obj->base.dma_buf->resv, &flip_work->excl, - &flip_work->shared_count, &flip_work->shared); - - if (unlikely(ret)) { - DRM_ERROR("failed to get fences for buffer\n"); - goto free_flip_work; - } - - /* No need to queue the worker if the are no fences */ - if (!flip_work->excl && !flip_work->shared_count) { - ipu_crtc->flip_state = IPU_FLIP_SUBMITTED; - } else { - ipu_crtc->flip_state = IPU_FLIP_PENDING; - queue_work(ipu_crtc->flip_queue, - &flip_work->fence_work); - } - } else { - ipu_crtc->flip_state = IPU_FLIP_SUBMITTED; - } - - if (crtc->primary->state) - drm_atomic_set_fb_for_plane(crtc->primary->state, fb); - - return 0; - -free_flip_work: - drm_gem_object_unreference_unlocked(flip_work->bo); - kfree(flip_work); - ipu_crtc->flip_work = NULL; -put_vblank: - imx_drm_crtc_vblank_put(ipu_crtc->imx_crtc); - - return ret; -} - static const struct drm_crtc_funcs ipu_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .destroy = drm_crtc_cleanup, - .page_flip = ipu_page_flip, + .page_flip = drm_atomic_helper_page_flip, .reset = drm_atomic_helper_crtc_reset, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, }; -static void ipu_crtc_handle_pageflip(struct ipu_crtc *ipu_crtc) +static void ipu_crtc_handle_pageflip(struct drm_crtc *crtc) { + struct drm_device *drm = crtc->dev; unsigned long flags; - struct drm_device *drm = ipu_crtc->base.dev; - struct ipu_flip_work *work = ipu_crtc->flip_work; spin_lock_irqsave(&drm->event_lock, flags); - if (work->page_flip_event) - drm_crtc_send_vblank_event(&ipu_crtc->base, - work->page_flip_event); - imx_drm_crtc_vblank_put(ipu_crtc->imx_crtc); + drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; spin_unlock_irqrestore(&drm->event_lock, flags); } static irqreturn_t ipu_irq_handler(int irq, void *dev_id) { struct ipu_crtc *ipu_crtc = dev_id; + struct drm_crtc *crtc = &ipu_crtc->base; imx_drm_handle_vblank(ipu_crtc->imx_crtc); - if (ipu_crtc->flip_state == IPU_FLIP_SUBMITTED) { - struct ipu_plane *plane = ipu_crtc->plane[0]; - - ipu_plane_set_base(plane, ipu_crtc->base.primary->fb); - ipu_crtc_handle_pageflip(ipu_crtc); - queue_work(ipu_crtc->flip_queue, - &ipu_crtc->flip_work->unref_work); - ipu_crtc->flip_state = IPU_FLIP_NONE; - } + if (crtc->state->event) + ipu_crtc_handle_pageflip(crtc); return IRQ_HANDLED; } @@ -452,8 +315,6 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc, ipu_plane_put_resources(ipu_crtc->plane[0]); - ipu_crtc->flip_queue = create_singlethread_workqueue("ipu-crtc-flip"); - return 0; err_put_plane_res: @@ -495,7 +356,6 @@ static void ipu_drm_unbind(struct device *dev, struct device *master, imx_drm_remove_crtc(ipu_crtc->imx_crtc); - destroy_workqueue(ipu_crtc->flip_queue); ipu_put_resources(ipu_crtc); }
To support generic atomic page flip, this patch customizes ->atomic_commit for async commits. Signed-off-by: Liu Ying <gnuiyl@gmail.com> --- drivers/gpu/drm/imx/imx-drm-core.c | 137 +++++++++++++++++++++++++++++++- drivers/gpu/drm/imx/ipuv3-crtc.c | 156 ++----------------------------------- 2 files changed, 144 insertions(+), 149 deletions(-)