To support generic atomic page flip, this patch customizes ->atomic_commit
for async commits.

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

Reply via email to