[PATCH v2 09/10] drm/imx: atomic phase 3 step 4: Use generic atomic page flip
Liu Ying
gnuiyl at gmail.com
Tue May 31 09:24:30 UTC 2016
To support generic atomic page flip, this patch customizes ->atomic_commit
for nonblock commits.
Signed-off-by: Liu Ying <gnuiyl at gmail.com>
---
v1->v2:
* s/async/nonblock/ on this patch to address Daniel Vetter's comment.
* Wait for pending commit on each CRTC for both block and nonblock
atomic mode settings. This way, a block commit will not overwrite
the hardware setting when a nonblock page flip is about to finish,
so that the page flip may wait for vblank successfully.
drivers/gpu/drm/imx/imx-drm-core.c | 135 +++++++++++++++++++++++++++++++-
drivers/gpu/drm/imx/ipuv3-crtc.c | 156 ++-----------------------------------
2 files changed, 142 insertions(+), 149 deletions(-)
diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c
index 2a2ab8c..d2bb743 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,130 @@ 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)
+{
+ 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);
+
+ 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);
+}
+
+static int imx_drm_atomic_commit(struct drm_device *dev,
+ struct drm_atomic_state *state, bool nonblock)
+{
+ 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;
+
+ 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;
+ }
+ }
+
+ if (nonblock)
+ INIT_WORK(&commit->work, imx_drm_atomic_work);
+
+ drm_atomic_helper_swap_state(dev, state);
+
+ if (nonblock)
+ schedule_work(&commit->work);
+ else
+ imx_drm_atomic_complete(commit);
+
+ 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,
};
/*
@@ -307,6 +438,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 f20340f..7ee51db 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;
};
@@ -92,150 +70,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;
}
@@ -458,8 +321,6 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
/* Only enable IRQ when we actually need it to trigger work. */
disable_irq(ipu_crtc->irq);
- ipu_crtc->flip_queue = create_singlethread_workqueue("ipu-crtc-flip");
-
return 0;
err_put_plane1_res:
@@ -504,7 +365,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);
if (ipu_crtc->plane[1])
ipu_plane_put_resources(ipu_crtc->plane[1]);
--
2.7.4
More information about the dri-devel
mailing list