From: Val Packett <val@packett.cool>
To: Neil Armstrong <neil.armstrong@linaro.org>,
Jessica Zhang <jesszhan0024@gmail.com>,
Maarten Lankhorst <maarten.lankhorst@linux.intel.com>,
Maxime Ripard <mripard@kernel.org>,
Thomas Zimmermann <tzimmermann@suse.de>,
David Airlie <airlied@gmail.com>, Simona Vetter <simona@ffwll.ch>
Cc: Val Packett <val@packett.cool>,
Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>,
Marijn Suijten <marijn.suijten@somainline.org>,
dri-devel@lists.freedesktop.org,
~postmarketos/upstreaming@lists.sr.ht,
phone-devel@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH 1/2] drm/panel: Add atomic versions of panel ops
Date: Fri, 24 Apr 2026 20:49:50 -0300 [thread overview]
Message-ID: <20260425001130.233935-1-val@packett.cool> (raw)
Modern DSI panels support various modes (resolutions and refresh rates),
which usually requires sending mode-specific DCS commands to configure the
DDIC during operations like prepare(). However existing drm_panel_funcs
only take the panel itself as an argument, so there's no way to access
relevant state (such as the mode being set) inside of these functions.
To allow panel drivers access to the state, introduce new atomic_*
versions of the prepare/enable/disable/unprepare ops, and make the
corresponding drm_panel_* functions call them if available, falling back
to the original ops otherwise. To avoid the need to modify all consumers
at once, the original single-argument drm_panel_* functions are redefined
as aliases to the new ones, passing NULLs for the new extra arguments.
The atomic ops pass the CRTC as well as the atomic state because getting
the mode being set is part of per-CRTC state.
Signed-off-by: Val Packett <val@packett.cool>
---
As discussed in my recent RFC [1] and an older thread I was pointed to
there [2], the right way to expose the mode to panel drivers is to expose
the entire atomic state and let them find the mode there. (Turns out,
the drm_crtc must also be passed along with the state as it's necessary
for drm_atomic_get_new_crtc_state).
Hopefully this first "real" attempt is close enough! :)
I have tested this with the WIP nt37701 driver from the linked RFC,
only slightly updated to receive the new args and not directly the mode.
[1]: https://lore.kernel.org/all/20260412113851.355944-1-val@packett.cool/
[2]: https://lore.kernel.org/dri-devel/nfc6ih43gjpi5u67fpkkxgwwygv53grdldq7tfp5iiukrkiy2u@53fsrtezzkyt/
Thanks,
~val
---
drivers/gpu/drm/drm_panel.c | 88 +++++++++++++++--------
include/drm/drm_panel.h | 137 ++++++++++++++++++++++++++++++++++--
2 files changed, 193 insertions(+), 32 deletions(-)
diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c
index d1e6598ea3bc..48fce5e89bca 100644
--- a/drivers/gpu/drm/drm_panel.c
+++ b/drivers/gpu/drm/drm_panel.c
@@ -102,16 +102,20 @@ void drm_panel_remove(struct drm_panel *panel)
EXPORT_SYMBOL(drm_panel_remove);
/**
- * drm_panel_prepare - power on a panel
+ * drm_panel_atomic_prepare - power on a panel
* @panel: DRM panel
+ * @crtc: the CRTC used to drive the panel, may be NULL
+ * @state: current atomic commit state, may be NULL
*
* Calling this function will enable power and deassert any reset signals to
* the panel. After this has completed it is possible to communicate with any
* integrated circuitry via a command bus. This function cannot fail (as it is
* called from the pre_enable call chain). There will always be a call to
- * drm_panel_disable() afterwards.
+ * drm_panel_atomic_disable() afterwards.
*/
-void drm_panel_prepare(struct drm_panel *panel)
+void drm_panel_atomic_prepare(struct drm_panel *panel,
+ struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
{
struct drm_panel_follower *follower;
int ret;
@@ -126,11 +130,15 @@ void drm_panel_prepare(struct drm_panel *panel)
mutex_lock(&panel->follower_lock);
- if (panel->funcs && panel->funcs->prepare) {
+ if (panel->funcs && panel->funcs->atomic_prepare) {
+ ret = panel->funcs->atomic_prepare(panel, crtc, state);
+ } else if (panel->funcs && panel->funcs->prepare) {
ret = panel->funcs->prepare(panel);
- if (ret < 0)
- goto exit;
+ } else {
+ ret = 0;
}
+ if (ret < 0)
+ goto exit;
panel->prepared = true;
list_for_each_entry(follower, &panel->followers, list) {
@@ -146,18 +154,22 @@ void drm_panel_prepare(struct drm_panel *panel)
exit:
mutex_unlock(&panel->follower_lock);
}
-EXPORT_SYMBOL(drm_panel_prepare);
+EXPORT_SYMBOL(drm_panel_atomic_prepare);
/**
- * drm_panel_unprepare - power off a panel
+ * drm_panel_atomic_unprepare - power off a panel
* @panel: DRM panel
+ * @crtc: the CRTC used to drive the panel, may be NULL
+ * @state: current atomic commit state, may be NULL
*
* Calling this function will completely power off a panel (assert the panel's
* reset, turn off power supplies, ...). After this function has completed, it
* is usually no longer possible to communicate with the panel until another
- * call to drm_panel_prepare().
+ * call to drm_panel_atomic_prepare().
*/
-void drm_panel_unprepare(struct drm_panel *panel)
+void drm_panel_atomic_unprepare(struct drm_panel *panel,
+ struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
{
struct drm_panel_follower *follower;
int ret;
@@ -191,29 +203,37 @@ void drm_panel_unprepare(struct drm_panel *panel)
follower->funcs->panel_unpreparing, ret);
}
- if (panel->funcs && panel->funcs->unprepare) {
+ if (panel->funcs && panel->funcs->atomic_unprepare) {
+ ret = panel->funcs->atomic_unprepare(panel, crtc, state);
+ } else if (panel->funcs && panel->funcs->unprepare) {
ret = panel->funcs->unprepare(panel);
- if (ret < 0)
- goto exit;
+ } else {
+ ret = 0;
}
+ if (ret < 0)
+ goto exit;
panel->prepared = false;
exit:
mutex_unlock(&panel->follower_lock);
}
-EXPORT_SYMBOL(drm_panel_unprepare);
+EXPORT_SYMBOL(drm_panel_atomic_unprepare);
/**
- * drm_panel_enable - enable a panel
+ * drm_panel_atomic_enable - enable a panel
* @panel: DRM panel
+ * @crtc: the CRTC used to drive the panel, may be NULL
+ * @state: current atomic commit state, may be NULL
*
* Calling this function will cause the panel display drivers to be turned on
* and the backlight to be enabled. Content will be visible on screen after
* this call completes. This function cannot fail (as it is called from the
- * enable call chain). There will always be a call to drm_panel_disable()
- * afterwards.
+ * enable call chain). There will always be a call to
+ * drm_panel_atomic_disable() afterwards.
*/
-void drm_panel_enable(struct drm_panel *panel)
+void drm_panel_atomic_enable(struct drm_panel *panel,
+ struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
{
struct drm_panel_follower *follower;
int ret;
@@ -228,11 +248,15 @@ void drm_panel_enable(struct drm_panel *panel)
mutex_lock(&panel->follower_lock);
- if (panel->funcs && panel->funcs->enable) {
+ if (panel->funcs && panel->funcs->atomic_enable) {
+ ret = panel->funcs->atomic_enable(panel, crtc, state);
+ } else if (panel->funcs && panel->funcs->enable) {
ret = panel->funcs->enable(panel);
- if (ret < 0)
- goto exit;
+ } else {
+ ret = 0;
}
+ if (ret < 0)
+ goto exit;
panel->enabled = true;
ret = backlight_enable(panel->backlight);
@@ -253,17 +277,21 @@ void drm_panel_enable(struct drm_panel *panel)
exit:
mutex_unlock(&panel->follower_lock);
}
-EXPORT_SYMBOL(drm_panel_enable);
+EXPORT_SYMBOL(drm_panel_atomic_enable);
/**
- * drm_panel_disable - disable a panel
+ * drm_panel_atomic_disable - disable a panel
* @panel: DRM panel
+ * @crtc: the CRTC used to drive the panel, may be NULL
+ * @state: current atomic commit state, may be NULL
*
* This will typically turn off the panel's backlight or disable the display
* drivers. For smart panels it should still be possible to communicate with
* the integrated circuitry via any command bus after this call.
*/
-void drm_panel_disable(struct drm_panel *panel)
+void drm_panel_atomic_disable(struct drm_panel *panel,
+ struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
{
struct drm_panel_follower *follower;
int ret;
@@ -302,17 +330,21 @@ void drm_panel_disable(struct drm_panel *panel)
DRM_DEV_INFO(panel->dev, "failed to disable backlight: %d\n",
ret);
- if (panel->funcs && panel->funcs->disable) {
+ if (panel->funcs && panel->funcs->atomic_disable) {
+ ret = panel->funcs->atomic_disable(panel, crtc, state);
+ } else if (panel->funcs && panel->funcs->disable) {
ret = panel->funcs->disable(panel);
- if (ret < 0)
- goto exit;
+ } else {
+ ret = 0;
}
+ if (ret < 0)
+ goto exit;
panel->enabled = false;
exit:
mutex_unlock(&panel->follower_lock);
}
-EXPORT_SYMBOL(drm_panel_disable);
+EXPORT_SYMBOL(drm_panel_atomic_disable);
/**
* drm_panel_get_modes - probe the available display modes of a panel
diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h
index 2407bfa60236..696df57cdd37 100644
--- a/include/drm/drm_panel.h
+++ b/include/drm/drm_panel.h
@@ -33,7 +33,9 @@
struct backlight_device;
struct dentry;
struct device_node;
+struct drm_atomic_state;
struct drm_connector;
+struct drm_crtc;
struct drm_panel_follower;
struct drm_panel;
struct display_timing;
@@ -80,6 +82,19 @@ struct drm_panel_funcs {
*/
int (*prepare)(struct drm_panel *panel);
+ /**
+ * @atomic_prepare:
+ *
+ * Turn on panel and perform setup with access to atomic commit state.
+ * Consumers will prefer this function over @prepare if both are provided.
+ * The state may be NULL due to the caller not being aware of it.
+ *
+ * This function is optional.
+ */
+ int (*atomic_prepare)(struct drm_panel *panel,
+ struct drm_crtc *crtc,
+ struct drm_atomic_state *state);
+
/**
* @enable:
*
@@ -89,6 +104,19 @@ struct drm_panel_funcs {
*/
int (*enable)(struct drm_panel *panel);
+ /**
+ * @atomic_enable:
+ *
+ * Enable panel (turn on backlight, etc.) with access to atomic commit state.
+ * Consumers will prefer this function over @enable if both are provided.
+ * The state may be NULL due to the caller not being aware of it.
+ *
+ * This function is optional.
+ */
+ int (*atomic_enable)(struct drm_panel *panel,
+ struct drm_crtc *crtc,
+ struct drm_atomic_state *state);
+
/**
* @disable:
*
@@ -98,6 +126,19 @@ struct drm_panel_funcs {
*/
int (*disable)(struct drm_panel *panel);
+ /**
+ * @atomic_disable:
+ *
+ * Disable panel (turn off backlight, etc.) with access to atomic commit state.
+ * Consumers will prefer this function over @disable if both are provided.
+ * The state may be NULL due to the caller not being aware of it.
+ *
+ * This function is optional.
+ */
+ int (*atomic_disable)(struct drm_panel *panel,
+ struct drm_crtc *crtc,
+ struct drm_atomic_state *state);
+
/**
* @unprepare:
*
@@ -107,6 +148,19 @@ struct drm_panel_funcs {
*/
int (*unprepare)(struct drm_panel *panel);
+ /**
+ * @atomic_unprepare:
+ *
+ * Turn off panel with access to atomic commit state.
+ * Consumers will prefer this function over @unprepare if both are provided.
+ * The state may be NULL due to the caller not being aware of it.
+ *
+ * This function is optional.
+ */
+ int (*atomic_unprepare)(struct drm_panel *panel,
+ struct drm_crtc *crtc,
+ struct drm_atomic_state *state);
+
/**
* @get_modes:
*
@@ -330,11 +384,86 @@ void drm_panel_put(struct drm_panel *panel);
void drm_panel_add(struct drm_panel *panel);
void drm_panel_remove(struct drm_panel *panel);
-void drm_panel_prepare(struct drm_panel *panel);
-void drm_panel_unprepare(struct drm_panel *panel);
+void drm_panel_atomic_prepare(struct drm_panel *panel,
+ struct drm_crtc *crtc,
+ struct drm_atomic_state *state);
-void drm_panel_enable(struct drm_panel *panel);
-void drm_panel_disable(struct drm_panel *panel);
+/**
+ * drm_panel_prepare - power on a panel
+ * @panel: DRM panel
+ *
+ * Calling this function will enable power and deassert any reset signals to
+ * the panel. After this has completed it is possible to communicate with any
+ * integrated circuitry via a command bus. This function cannot fail (as it is
+ * called from the pre_enable call chain). There will always be a call to
+ * drm_panel_disable() afterwards.
+ *
+ * If atomic commit state is available, call drm_panel_atomic_prepare instead.
+ */
+static inline void drm_panel_prepare(struct drm_panel *panel)
+{
+ drm_panel_atomic_prepare(panel, NULL, NULL);
+}
+
+void drm_panel_atomic_unprepare(struct drm_panel *panel,
+ struct drm_crtc *crtc,
+ struct drm_atomic_state *state);
+
+/**
+ * drm_panel_unprepare - power off a panel
+ * @panel: DRM panel
+ *
+ * Calling this function will completely power off a panel (assert the panel's
+ * reset, turn off power supplies, ...). After this function has completed, it
+ * is usually no longer possible to communicate with the panel until another
+ * call to drm_panel_prepare().
+ *
+ * If atomic commit state is available, call drm_panel_atomic_unprepare instead.
+ */
+static inline void drm_panel_unprepare(struct drm_panel *panel)
+{
+ drm_panel_atomic_unprepare(panel, NULL, NULL);
+}
+
+void drm_panel_atomic_enable(struct drm_panel *panel,
+ struct drm_crtc *crtc,
+ struct drm_atomic_state *state);
+
+/**
+ * drm_panel_enable - enable a panel
+ * @panel: DRM panel
+ *
+ * Calling this function will cause the panel display drivers to be turned on
+ * and the backlight to be enabled. Content will be visible on screen after
+ * this call completes. This function cannot fail (as it is called from the
+ * enable call chain). There will always be a call to drm_panel_disable()
+ * afterwards.
+ *
+ * If atomic commit state is available, call drm_panel_atomic_enable instead.
+ */
+static inline void drm_panel_enable(struct drm_panel *panel)
+{
+ drm_panel_atomic_enable(panel, NULL, NULL);
+}
+
+void drm_panel_atomic_disable(struct drm_panel *panel,
+ struct drm_crtc *crtc,
+ struct drm_atomic_state *state);
+
+/**
+ * drm_panel_disable - disable a panel
+ * @panel: DRM panel
+ *
+ * This will typically turn off the panel's backlight or disable the display
+ * drivers. For smart panels it should still be possible to communicate with
+ * the integrated circuitry via any command bus after this call.
+ *
+ * If atomic commit state is available, call drm_panel_atomic_disable instead.
+ */
+static inline void drm_panel_disable(struct drm_panel *panel)
+{
+ drm_panel_atomic_disable(panel, NULL, NULL);
+}
int drm_panel_get_modes(struct drm_panel *panel, struct drm_connector *connector);
--
2.53.0
next reply other threads:[~2026-04-25 0:12 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-24 23:49 Val Packett [this message]
2026-04-24 23:49 ` [PATCH 2/2] drm/bridge: panel: call the new atomic panel ops Val Packett
2026-04-25 12:08 ` [PATCH 1/2] drm/panel: Add atomic versions of " Dmitry Baryshkov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260425001130.233935-1-val@packett.cool \
--to=val@packett.cool \
--cc=airlied@gmail.com \
--cc=dmitry.baryshkov@oss.qualcomm.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=jesszhan0024@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=maarten.lankhorst@linux.intel.com \
--cc=marijn.suijten@somainline.org \
--cc=mripard@kernel.org \
--cc=neil.armstrong@linaro.org \
--cc=phone-devel@vger.kernel.org \
--cc=simona@ffwll.ch \
--cc=tzimmermann@suse.de \
--cc=~postmarketos/upstreaming@lists.sr.ht \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox