[PATCH 13/14] drm/imx: atomic phase 3 step 4: Use generic atomic page flip
Ying Liu
gnuiyl at gmail.com
Wed May 25 09:25:58 UTC 2016
On Tue, May 24, 2016 at 7:11 PM, Daniel Vetter <daniel at 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 at 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 at lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch
More information about the dri-devel
mailing list