[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20251031-drm-crtc-flush-worker-v1-1-9edc94da0fb7@nxp.com>
Date: Fri, 31 Oct 2025 18:21:24 +0800
From: Liu Ying <victor.liu@....com>
To: Maarten Lankhorst <maarten.lankhorst@...ux.intel.com>,
Maxime Ripard <mripard@...nel.org>, Thomas Zimmermann <tzimmermann@...e.de>,
David Airlie <airlied@...il.com>, Simona Vetter <simona@...ll.ch>,
Shawn Guo <shawnguo@...nel.org>, Sascha Hauer <s.hauer@...gutronix.de>,
Pengutronix Kernel Team <kernel@...gutronix.de>,
Fabio Estevam <festevam@...il.com>
Cc: dri-devel@...ts.freedesktop.org, linux-kernel@...r.kernel.org,
imx@...ts.linux.dev, linux-arm-kernel@...ts.infradead.org,
Liu Ying <victor.liu@....com>
Subject: [PATCH 1/2] drm/atomic-helper: Support atomic flush with an
optional kthread worker
Atomic flush could be blocking and hence potential low performance
when flushing for multiple CRTCs sequentially with
drm_atomic_helper_commit_planes(), e.g., page flip for multiple
CRTCs in a single atomic commit. A real case is imx8-dc, where
atomic flush contains shadow load register trigger and waiting for
the shadow load done event as required by i.MX8 display controller
IP specification. Add an optional kthread worker to conduct atomic
flush in drm_atomic_helper_commit_planes() so that multiple CRTCs
could be flushed in parallel for better performance.
Drivers should call drmm_crtc_flush_worker_init() to initialize the
kthread worker if they want to use it.
Signed-off-by: Liu Ying <victor.liu@....com>
---
drivers/gpu/drm/drm_atomic_helper.c | 27 ++++++++++++++++-
drivers/gpu/drm/drm_crtc.c | 59 +++++++++++++++++++++++++++++++++++++
include/drm/drm_crtc.h | 45 ++++++++++++++++++++++++++++
3 files changed, 130 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index d5ebe6ea0acbc5a08aef7fa41ecb9ed5d8fa8e80..e976facba8fc55fb8634d3d87686cd2e1c3fd31c 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -27,6 +27,7 @@
#include <linux/export.h>
#include <linux/dma-fence.h>
+#include <linux/kthread.h>
#include <linux/ktime.h>
#include <drm/drm_atomic.h>
@@ -2977,7 +2978,31 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
if (active_only && !new_crtc_state->active)
continue;
- funcs->atomic_flush(crtc, state);
+ if (crtc->flush_worker) {
+ crtc->flush_work.state = state;
+ kthread_queue_work(crtc->flush_worker, &crtc->flush_work.base);
+ } else {
+ funcs->atomic_flush(crtc, state);
+ }
+ }
+
+ /*
+ * Iterate over all CRTCs again, to make sure flush works have finished
+ * execution if needed.
+ */
+ for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
+ const struct drm_crtc_helper_funcs *funcs;
+
+ funcs = crtc->helper_private;
+
+ if (!funcs || !funcs->atomic_flush)
+ continue;
+
+ if (active_only && !new_crtc_state->active)
+ continue;
+
+ if (crtc->flush_worker)
+ kthread_flush_work(&crtc->flush_work.base);
}
/*
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 46655339003db2a1b43441434839e26f61d79b4e..dad4182d2bb893a3e015cf0573df2c410c5ee226 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -30,7 +30,9 @@
* Jesse Barnes <jesse.barnes@...el.com>
*/
#include <linux/ctype.h>
+#include <linux/kthread.h>
#include <linux/list.h>
+#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/export.h>
#include <linux/dma-fence.h>
@@ -41,6 +43,7 @@
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_managed.h>
+#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_modeset_lock.h>
#include <drm/drm_atomic.h>
#include <drm/drm_auth.h>
@@ -440,6 +443,62 @@ int drmm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
}
EXPORT_SYMBOL(drmm_crtc_init_with_planes);
+static void drm_crtc_flush_work(struct kthread_work *work)
+{
+ struct drm_crtc_flush_work *flush_work = to_drm_crtc_flush_work(work);
+ struct drm_crtc *crtc = flush_work->crtc;
+ const struct drm_crtc_helper_funcs *funcs = crtc->helper_private;
+
+ if (funcs && funcs->atomic_flush)
+ funcs->atomic_flush(crtc, flush_work->state);
+}
+
+static void drmm_crtc_flush_worker_cleanup(struct drm_device *dev, void *ptr)
+{
+ struct drm_crtc *crtc = ptr;
+
+ kthread_destroy_worker(crtc->flush_worker);
+}
+
+/**
+ * drmm_crtc_flush_worker_init - Initialize a worker to conduct CRTC flush
+ * @dev: DRM device
+ * @crtc: CRTC object to be flushed
+ *
+ * Create a &kthread_worker used for executing flush works for the CRTC.
+ * Initialize a work item to be queued to the created &kthread_worker.
+ *
+ * Cleanup is automatically handled through registering
+ * drmm_crtc_flush_worker_cleanup() with drmm_add_action_or_reset().
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drmm_crtc_flush_worker_init(struct drm_device *dev, struct drm_crtc *crtc)
+{
+ struct kthread_worker *flush_worker;
+ int ret;
+
+ flush_worker = kthread_create_worker(0, "card%d-crtc%u-flush",
+ dev->primary->index, crtc->index);
+ if (IS_ERR(flush_worker))
+ return PTR_ERR(flush_worker);
+
+ crtc->flush_worker = flush_worker;
+
+ sched_set_fifo(flush_worker->task);
+
+ ret = drmm_add_action_or_reset(dev, drmm_crtc_flush_worker_cleanup, crtc);
+ if (ret)
+ return ret;
+
+ crtc->flush_work.crtc = crtc;
+ kthread_init_work(&crtc->flush_work.base, drm_crtc_flush_work);
+
+ return 0;
+}
+EXPORT_SYMBOL(drmm_crtc_flush_worker_init);
+
void *__drmm_crtc_alloc_with_planes(struct drm_device *dev,
size_t size, size_t offset,
struct drm_plane *primary,
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index caa56e039da2a748cf40ebf45b37158acda439d9..62e98d714bbb4d1a948cc7c17bafbe587490b081 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -25,6 +25,7 @@
#ifndef __DRM_CRTC_H__
#define __DRM_CRTC_H__
+#include <linux/kthread.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <drm/drm_modeset_lock.h>
@@ -923,6 +924,34 @@ struct drm_crtc_funcs {
bool in_vblank_irq);
};
+/**
+ * struct drm_crtc_flush_work - flush work for CRTC
+ *
+ * See also:
+ * drmm_crtc_flush_worker_init()
+ */
+struct drm_crtc_flush_work {
+ /**
+ * @base: The base &kthread_work item which will be executed by
+ * &drm_crtc.flush_worker. Drivers should not interact with this
+ * directly, but instead rely on drmm_crtc_flush_worker_init() to
+ * initialize this.
+ */
+ struct kthread_work base;
+ /** @crtc: CRTC to be flushed */
+ struct drm_crtc *crtc;
+ /** @state: pointer to global drm_atomic_state to be flushed */
+ struct drm_atomic_state *state;
+};
+
+/**
+ * to_drm_crtc_flush_work - Retrieve &drm_crtc_flush_work instance from a
+ * &kthread_work
+ * @_work: The &kthread_work embedded inside a &drm_crtc_flush_work
+ */
+#define to_drm_crtc_flush_work(_work) \
+ container_of((_work), struct drm_crtc_flush_work, base)
+
/**
* struct drm_crtc - central CRTC control structure
*
@@ -1175,6 +1204,20 @@ struct drm_crtc {
* Initialized via drm_self_refresh_helper_init().
*/
struct drm_self_refresh_data *self_refresh_data;
+
+ /**
+ * @flush_worker:
+ *
+ * The &kthread_worker used for executing flush works.
+ */
+ struct kthread_worker *flush_worker;
+
+ /**
+ * @flush_work:
+ *
+ * Flush work to be executed by @flush_worker.
+ */
+ struct drm_crtc_flush_work flush_work;
};
/**
@@ -1220,6 +1263,8 @@ int drmm_crtc_init_with_planes(struct drm_device *dev,
const struct drm_crtc_funcs *funcs,
const char *name, ...);
+int drmm_crtc_flush_worker_init(struct drm_device *dev, struct drm_crtc *crtc);
+
void drm_crtc_cleanup(struct drm_crtc *crtc);
__printf(7, 8)
--
2.34.1
Powered by blists - more mailing lists