* [PATCH RFC v2 01/24] drm/connector: split drmm_connector_hdmi_init() in 3 parts
2026-10-01 12:42 [PATCH RFC v2 00/24] drm bridge hotplug Luca Ceresoli
@ 2026-10-01 12:42 ` Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 02/24] drm/connector: add drm_connector_hdmi_dynamic_init() Luca Ceresoli
` (22 subsequent siblings)
23 siblings, 0 replies; 25+ messages in thread
From: Luca Ceresoli @ 2026-10-01 12:42 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Inki Dae, Jagan Teki, Marek Szyprowski, Jessica Zhang,
Douglas Anderson, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, dri-devel,
linux-kernel, imx, linux-arm-kernel
In preparation for adding hotpluggable bridges we need connectors to be
created dynamically, both regular connectors and HDMI ones.
For non-HDMI connectors drm_connector_init() already has a dynamic
variant, but there is none for HDMI. Creating one would be easy by creating
a mostly-identical copy of drmm_connector_hdmi_init(), but it is a long
function so there would be a lot of duplicated code.
drmm_connector_hdmi_init() currently has 3 sections:
1. sanity checks
2. call drmm_connector_init()
3. initialize HDMI-specific fields not initialized at step 2
For the dynamic variant, sectons 1 and 3 would be an exact copy, while
section 2 needs to be different.
To avoid code duplication, split parts 1 and 3 to subfunctions. The next
commit will introduce the dynamic variant.
No functional changes. Just moving code around.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
Changes in v2:
- expanded commit message to clarify rationale
- rebased on current drm-misc-next, which required a full rewrite after
the signature of drmm_connector_hdmi_init() changed in commit
400c9ede1ea4 ("drm/connector: Add drmm_connector_hdmi_init() with new
signature")
- renamed drm_connector_hdmi_init() to drm_connector_hdmi_initialize() as
or it would look like an HDMI version of drm_connector_init()
---
drivers/gpu/drm/drm_connector.c | 88 +++++++++++++++++++++++++----------------
1 file changed, 55 insertions(+), 33 deletions(-)
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index d94c86bfed86..f5bd08640d25 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -542,37 +542,11 @@ int drmm_connector_init(struct drm_device *dev,
}
EXPORT_SYMBOL(drmm_connector_init);
-/**
- * drmm_connector_hdmi_init - Init a preallocated HDMI connector
- * @dev: DRM device
- * @connector: A pointer to the HDMI connector to init
- * @funcs: callbacks for this connector
- * @hdmi_funcs: HDMI-related callbacks and capabilities for this connector
- * @connector_type: user visible type of the connector
- * @ddc: optional pointer to the associated ddc adapter
- *
- * Initialises a preallocated HDMI connector. Connectors can be
- * subclassed as part of driver connector objects.
- *
- * Cleanup is automatically handled with a call to
- * drm_connector_cleanup() in a DRM-managed action.
- *
- * The connector structure should be allocated with drmm_kzalloc().
- *
- * The @drm_connector_funcs.destroy hook must be NULL.
- *
- * Returns:
- * Zero on success, error code on failure.
- */
-int drmm_connector_hdmi_init(struct drm_device *dev,
- struct drm_connector *connector,
- const struct drm_connector_funcs *funcs,
- const struct drm_connector_hdmi_funcs *hdmi_funcs,
- int connector_type,
- struct i2c_adapter *ddc)
+static int drm_connector_hdmi_sanity_checks(struct drm_device *dev,
+ struct drm_connector *connector,
+ const struct drm_connector_hdmi_funcs *hdmi_funcs,
+ int connector_type)
{
- int ret;
-
if (!hdmi_funcs)
return -EINVAL;
@@ -613,9 +587,15 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
connector_type == DRM_MODE_CONNECTOR_HDMIB))
return -EINVAL;
- ret = drmm_connector_init(dev, connector, funcs, connector_type, ddc);
- if (ret)
- return ret;
+ return 0;
+}
+
+/* Initialize HDMI-specific resources of a connector */
+static int drm_connector_hdmi_initialize(struct drm_device *dev,
+ struct drm_connector *connector,
+ const struct drm_connector_hdmi_funcs *hdmi_funcs)
+{
+ int ret;
/* TODO: remove after conversion to new drmm_connector_hdmi_init() */
connector->hdmi.supported_formats = hdmi_funcs->supported_formats;
@@ -684,6 +664,48 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
return 0;
}
+
+/**
+ * drmm_connector_hdmi_init - Init a preallocated HDMI connector
+ * @dev: DRM device
+ * @connector: A pointer to the HDMI connector to init
+ * @funcs: callbacks for this connector
+ * @hdmi_funcs: HDMI-related callbacks and capabilities for this connector
+ * @connector_type: user visible type of the connector
+ * @ddc: optional pointer to the associated ddc adapter
+ *
+ * Initialises a preallocated HDMI connector. Connectors can be
+ * subclassed as part of driver connector objects.
+ *
+ * Cleanup is automatically handled with a call to
+ * drm_connector_cleanup() in a DRM-managed action.
+ *
+ * The connector structure should be allocated with drmm_kzalloc().
+ *
+ * The @drm_connector_funcs.destroy hook must be NULL.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drmm_connector_hdmi_init(struct drm_device *dev,
+ struct drm_connector *connector,
+ const struct drm_connector_funcs *funcs,
+ const struct drm_connector_hdmi_funcs *hdmi_funcs,
+ int connector_type,
+ struct i2c_adapter *ddc)
+{
+ int ret;
+
+ ret = drm_connector_hdmi_sanity_checks(dev, connector, hdmi_funcs, connector_type);
+ if (ret)
+ return ret;
+
+ ret = drmm_connector_init(dev, connector, funcs, connector_type, ddc);
+ if (ret)
+ return ret;
+
+ return drm_connector_hdmi_initialize(dev, connector, hdmi_funcs);
+}
EXPORT_SYMBOL(drmm_connector_hdmi_init);
/**
--
2.55.0
^ permalink raw reply [flat|nested] 25+ messages in thread* [PATCH RFC v2 02/24] drm/connector: add drm_connector_hdmi_dynamic_init()
2026-10-01 12:42 [PATCH RFC v2 00/24] drm bridge hotplug Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 01/24] drm/connector: split drmm_connector_hdmi_init() in 3 parts Luca Ceresoli
@ 2026-10-01 12:42 ` Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 03/24] drm/display: bridge-connector: split code allocation from initialization Luca Ceresoli
` (21 subsequent siblings)
23 siblings, 0 replies; 25+ messages in thread
From: Luca Ceresoli @ 2026-10-01 12:42 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Inki Dae, Jagan Teki, Marek Szyprowski, Jessica Zhang,
Douglas Anderson, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, dri-devel,
linux-kernel, imx, linux-arm-kernel
In preparation for adding hotpluggable bridges into the
drm_bridge_connector, we need connectors to be created dynamically, both
regular connectors and HDMI ones. drm_connector_init() already has a
dynamic variant, add one for HDMI connectors too.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
Changes in v2:
- rebased on current drm-misc-next: now drm_connector_hdmi_init() can fail,
so call drm_connector_cleanup(connector) if that happens
---
drivers/gpu/drm/drm_connector.c | 42 +++++++++++++++++++++++++++++++++++++++++
include/drm/drm_connector.h | 6 ++++++
2 files changed, 48 insertions(+)
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index f5bd08640d25..f264cab0d184 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -708,6 +708,48 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
}
EXPORT_SYMBOL(drmm_connector_hdmi_init);
+/**
+ * drmm_connector_hdmi_init - Init a preallocated dynamic HDMI connector
+ * @dev: DRM device
+ * @connector: A pointer to the HDMI connector to init
+ * @funcs: callbacks for this connector
+ * @hdmi_funcs: HDMI-related callbacks and capabilities for this connector
+ * @connector_type: user visible type of the connector
+ * @ddc: optional pointer to the associated ddc adapter
+ *
+ * Initialises a preallocated dynamic HDMI connector. Connectors can be
+ * subclassed as part of driver connector objects.
+ *
+ * See drm_connector_dynamic_init(), the same constraints apply here. This
+ * is just the HDMI version.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_connector_hdmi_dynamic_init(struct drm_device *dev,
+ struct drm_connector *connector,
+ const struct drm_connector_funcs *funcs,
+ const struct drm_connector_hdmi_funcs *hdmi_funcs,
+ int connector_type,
+ struct i2c_adapter *ddc)
+{
+ int ret;
+
+ ret = drm_connector_hdmi_sanity_checks(dev, connector, hdmi_funcs, connector_type);
+ if (ret)
+ return ret;
+
+ ret = drm_connector_dynamic_init(dev, connector, funcs, connector_type, ddc);
+ if (ret)
+ return ret;
+
+ if (ret)
+ drm_connector_cleanup(connector);
+
+ return drm_connector_hdmi_initialize(dev, connector, hdmi_funcs);
+}
+EXPORT_SYMBOL(drm_connector_hdmi_dynamic_init);
+
/**
* drmm_connector_hdmi_ini2 - Init a preallocated HDMI connector
* @dev: DRM device
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 2ee7c59329aa..48bbbc365c93 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -2743,6 +2743,12 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
const struct drm_connector_hdmi_funcs *hdmi_funcs,
int connector_type,
struct i2c_adapter *ddc);
+int drm_connector_hdmi_dynamic_init(struct drm_device *dev,
+ struct drm_connector *connector,
+ const struct drm_connector_funcs *funcs,
+ const struct drm_connector_hdmi_funcs *hdmi_funcs,
+ int connector_type,
+ struct i2c_adapter *ddc);
int drmm_connector_hdmi_ini2(struct drm_device *dev,
struct drm_connector *connector,
const char *vendor, const char *product,
--
2.55.0
^ permalink raw reply [flat|nested] 25+ messages in thread* [PATCH RFC v2 03/24] drm/display: bridge-connector: split code allocation from initialization
2026-10-01 12:42 [PATCH RFC v2 00/24] drm bridge hotplug Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 01/24] drm/connector: split drmm_connector_hdmi_init() in 3 parts Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 02/24] drm/connector: add drm_connector_hdmi_dynamic_init() Luca Ceresoli
@ 2026-10-01 12:42 ` Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 04/24] drm/display: bridge-connector: hoist error management to common code Luca Ceresoli
` (20 subsequent siblings)
23 siblings, 0 replies; 25+ messages in thread
From: Luca Ceresoli @ 2026-10-01 12:42 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Inki Dae, Jagan Teki, Marek Szyprowski, Jessica Zhang,
Douglas Anderson, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, dri-devel,
linux-kernel, imx, linux-arm-kernel
Currently drm_bridge_connector_init() does two things:
* allocate and initialize the drm_bridge_connector
(which embeds a drm_connector) using drmm
* initialize and register the embedded drm_connector
For bridge hotplug drmm allocations are not suitable because a connector
may have to be added and removed multiple times in the lifetime of a card.
In preparation to support that, split out from drm_bridge_connector_init()
the code to allocate the drm_bridge_connector, so new (de)allocation code
can reuse all the initialization code.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 62 ++++++++++++++++----------
1 file changed, 38 insertions(+), 24 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index 81f3e26f6fdf..41b51f0f13ae 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -837,27 +837,11 @@ static void drm_bridge_connector_put_bridges(struct drm_device *dev, void *data)
drm_bridge_put(bridge_connector->bridge_hdmi_cec);
}
-/**
- * drm_bridge_connector_init - Initialise a connector for a chain of bridges
- * @drm: the DRM device
- * @encoder: the encoder where the bridge chain starts
- *
- * Create a new &drm_bridge_connector for the @drm device. The connector is
- * allocated, initialised, registered with the @drm device and attached to
- * @encoder.
- *
- * The connector is associated with a chain of bridges that starts at
- * the @encoder. All bridges in the chain shall report bridge operation flags
- * (&drm_bridge->ops) and bridge output type (&drm_bridge->type), and none of
- * them may create a DRM connector directly.
- *
- * Returns a pointer to the new connector on success, or a negative error
- * pointer otherwise.
- */
-struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
- struct drm_encoder *encoder)
+static struct drm_connector *
+drm_bridge_connector_initialize(struct drm_bridge_connector *bridge_connector,
+ struct drm_device *drm,
+ struct drm_encoder *encoder)
{
- struct drm_bridge_connector *bridge_connector;
struct drm_connector *connector;
struct i2c_adapter *ddc = NULL;
struct drm_bridge *panel_bridge __free(drm_bridge_put) = NULL;
@@ -865,10 +849,6 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
int connector_type;
int ret;
- bridge_connector = drmm_kzalloc(drm, sizeof(*bridge_connector), GFP_KERNEL);
- if (!bridge_connector)
- return ERR_PTR(-ENOMEM);
-
ret = drmm_add_action(drm, drm_bridge_connector_put_bridges, bridge_connector);
if (ret)
return ERR_PTR(ret);
@@ -1154,4 +1134,38 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
return connector;
}
+
+/**
+ * drm_bridge_connector_init - Initialise a connector for a chain of bridges
+ * @drm: the DRM device
+ * @encoder: the encoder where the bridge chain starts
+ *
+ * Create a new &drm_bridge_connector for the @drm device. The connector is
+ * allocated, initialised, registered with the @drm device and attached to
+ * @encoder.
+ *
+ * The connector is associated with a chain of bridges that starts at
+ * the @encoder. All bridges in the chain shall report bridge operation flags
+ * (&drm_bridge->ops) and bridge output type (&drm_bridge->type), and none of
+ * them may create a DRM connector directly.
+ *
+ * Returns a pointer to the new connector on success, or a negative error
+ * pointer otherwise.
+ */
+struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
+ struct drm_encoder *encoder)
+{
+ struct drm_bridge_connector *bridge_connector;
+ struct drm_connector *connector;
+
+ bridge_connector = drmm_kzalloc(drm, sizeof(*bridge_connector), GFP_KERNEL);
+ if (!bridge_connector)
+ return ERR_PTR(-ENOMEM);
+
+ connector = drm_bridge_connector_initialize(bridge_connector, drm, encoder);
+ if (IS_ERR(connector))
+ return connector;
+
+ return connector;
+}
EXPORT_SYMBOL_GPL(drm_bridge_connector_init);
--
2.55.0
^ permalink raw reply [flat|nested] 25+ messages in thread* [PATCH RFC v2 04/24] drm/display: bridge-connector: hoist error management to common code
2026-10-01 12:42 [PATCH RFC v2 00/24] drm bridge hotplug Luca Ceresoli
` (2 preceding siblings ...)
2026-10-01 12:42 ` [PATCH RFC v2 03/24] drm/display: bridge-connector: split code allocation from initialization Luca Ceresoli
@ 2026-10-01 12:42 ` Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 05/24] drm/display: bridge-connector: use a dynamic connector Luca Ceresoli
` (19 subsequent siblings)
23 siblings, 0 replies; 25+ messages in thread
From: Luca Ceresoli @ 2026-10-01 12:42 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Inki Dae, Jagan Teki, Marek Szyprowski, Jessica Zhang,
Douglas Anderson, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, dri-devel,
linux-kernel, imx, linux-arm-kernel
In prepataion to add more error management code common to the HDMI and
non-HDMI branches, move error management to be common to both cases.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index 41b51f0f13ae..2aff745f0147 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -1057,15 +1057,13 @@ drm_bridge_connector_initialize(struct drm_bridge_connector *bridge_connector,
&drm_bridge_connector_funcs,
&bridge_connector->hdmi_funcs,
connector_type, ddc);
- if (ret)
- return ERR_PTR(ret);
} else {
ret = drmm_connector_init(drm, connector,
&drm_bridge_connector_funcs,
connector_type, ddc);
- if (ret)
- return ERR_PTR(ret);
}
+ if (ret)
+ return ret;
if (bridge_connector->bridge_hdmi_audio ||
bridge_connector->bridge_dp_audio) {
--
2.55.0
^ permalink raw reply [flat|nested] 25+ messages in thread* [PATCH RFC v2 05/24] drm/display: bridge-connector: use a dynamic connector
2026-10-01 12:42 [PATCH RFC v2 00/24] drm bridge hotplug Luca Ceresoli
` (3 preceding siblings ...)
2026-10-01 12:42 ` [PATCH RFC v2 04/24] drm/display: bridge-connector: hoist error management to common code Luca Ceresoli
@ 2026-10-01 12:42 ` Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 06/24] drm/display: bridge-connector: add APIs to add/remove the connector dynamically Luca Ceresoli
` (18 subsequent siblings)
23 siblings, 0 replies; 25+ messages in thread
From: Luca Ceresoli @ 2026-10-01 12:42 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Inki Dae, Jagan Teki, Marek Szyprowski, Jessica Zhang,
Douglas Anderson, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, dri-devel,
linux-kernel, imx, linux-arm-kernel
Currently the drm_bridge_connector uses drmm functions to add the
drm_connector. For bridge hotplug drmm allocations are not suitable because
a connector may have to be added and removed multiple times in the lifetime
of a card.
In preparation for bridge hotplug, use the dynamic variants of
drm_connector[_hdmi]_init() so the drm_connector can be removed without
removing the whole card.
| [TODO]
| - there is a hack about state creation, already discussed,
| still to be sorted out
| - there are still 2 drmm calls to be converted to non-drmm:
| drmm_connector_hdmi_cec_notifier_register() and
| drmm_connector_hdmi_cec_register
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 86 ++++++++++++++++++--------
1 file changed, 59 insertions(+), 27 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index 2aff745f0147..1c222e27767d 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -137,6 +137,18 @@ struct drm_bridge_connector {
#define to_drm_bridge_connector(x) \
container_of(x, struct drm_bridge_connector, base)
+static void drm_bridge_connector_put_bridges(struct drm_bridge_connector *bridge_connector)
+{
+ drm_bridge_put(bridge_connector->bridge_edid);
+ drm_bridge_put(bridge_connector->bridge_hpd);
+ drm_bridge_put(bridge_connector->bridge_detect);
+ drm_bridge_put(bridge_connector->bridge_modes);
+ drm_bridge_put(bridge_connector->bridge_hdmi);
+ drm_bridge_put(bridge_connector->bridge_hdmi_audio);
+ drm_bridge_put(bridge_connector->bridge_dp_audio);
+ drm_bridge_put(bridge_connector->bridge_hdmi_cec);
+}
+
/* -----------------------------------------------------------------------------
* Bridge Connector Hot-Plug Handling
*/
@@ -267,6 +279,14 @@ drm_bridge_connector_color_format(const struct drm_connector_state *conn_state)
return conn_state->color_format;
}
+static void drm_bridge_connector_dynconn_destroy(struct drm_connector *connector)
+{
+ struct drm_bridge_connector *bridge_connector = to_drm_bridge_connector(connector);
+
+ drm_connector_cleanup(connector);
+ drm_bridge_connector_put_bridges(bridge_connector);
+}
+
static const struct drm_connector_funcs drm_bridge_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.atomic_create_state = drm_bridge_connector_create_state,
@@ -275,6 +295,7 @@ static const struct drm_connector_funcs drm_bridge_connector_funcs = {
.debugfs_init = drm_bridge_connector_debugfs_init,
.oob_hotplug_event = drm_bridge_connector_oob_hotplug_event,
.color_format = drm_bridge_connector_color_format,
+ .destroy = drm_bridge_connector_dynconn_destroy,
};
/* -----------------------------------------------------------------------------
@@ -823,20 +844,6 @@ static const struct drm_connector_hdmi_cec_funcs drm_bridge_connector_hdmi_cec_f
* Bridge Connector Initialisation
*/
-static void drm_bridge_connector_put_bridges(struct drm_device *dev, void *data)
-{
- struct drm_bridge_connector *bridge_connector = (struct drm_bridge_connector *)data;
-
- drm_bridge_put(bridge_connector->bridge_edid);
- drm_bridge_put(bridge_connector->bridge_hpd);
- drm_bridge_put(bridge_connector->bridge_detect);
- drm_bridge_put(bridge_connector->bridge_modes);
- drm_bridge_put(bridge_connector->bridge_hdmi);
- drm_bridge_put(bridge_connector->bridge_hdmi_audio);
- drm_bridge_put(bridge_connector->bridge_dp_audio);
- drm_bridge_put(bridge_connector->bridge_hdmi_cec);
-}
-
static struct drm_connector *
drm_bridge_connector_initialize(struct drm_bridge_connector *bridge_connector,
struct drm_device *drm,
@@ -849,10 +856,6 @@ drm_bridge_connector_initialize(struct drm_bridge_connector *bridge_connector,
int connector_type;
int ret;
- ret = drmm_add_action(drm, drm_bridge_connector_put_bridges, bridge_connector);
- if (ret)
- return ERR_PTR(ret);
-
bridge_connector->encoder = encoder;
/*
@@ -1053,17 +1056,21 @@ drm_bridge_connector_initialize(struct drm_bridge_connector *bridge_connector,
drm_bridge_connector_scrambler_disable;
}
- ret = drmm_connector_hdmi_init(drm, connector,
- &drm_bridge_connector_funcs,
- &bridge_connector->hdmi_funcs,
- connector_type, ddc);
+ ret = drm_connector_hdmi_dynamic_init(drm, connector,
+ &drm_bridge_connector_funcs,
+ &bridge_connector->hdmi_funcs,
+ connector_type, ddc);
} else {
- ret = drmm_connector_init(drm, connector,
- &drm_bridge_connector_funcs,
- connector_type, ddc);
+ ret = drm_connector_dynamic_init(drm, connector,
+ &drm_bridge_connector_funcs,
+ connector_type, ddc);
}
- if (ret)
- return ret;
+ if (ret) {
+ drm_bridge_connector_put_bridges(bridge_connector);
+ return ERR_PTR(ret);
+ }
+
+ /* From now on the connector is referenced and has to be put */
if (bridge_connector->bridge_hdmi_audio ||
bridge_connector->bridge_dp_audio) {
@@ -1113,6 +1120,9 @@ drm_bridge_connector_initialize(struct drm_bridge_connector *bridge_connector,
drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs);
+ if (!connector->state)
+ connector->state = drm_bridge_connector_create_state(connector);
+
if (bridge_connector->bridge_hpd)
connector->polled = DRM_CONNECTOR_POLL_HPD;
else if (bridge_connector->bridge_detect)
@@ -1130,9 +1140,26 @@ drm_bridge_connector_initialize(struct drm_bridge_connector *bridge_connector,
if (ret)
return ERR_PTR(ret);
+ ret = drm_connector_dynamic_register(connector);
+ if (ret)
+ return ERR_PTR(ret);
+
return connector;
}
+static void drm_bridge_connector_fini(struct drm_bridge_connector *bridge_connector)
+{
+ drm_connector_unregister(&bridge_connector->base);
+ drm_connector_put(&bridge_connector->base);
+}
+
+static void drmm_bridge_connector_fini(struct drm_device *dev, void *res)
+{
+ struct drm_bridge_connector *bridge_connector = (struct drm_bridge_connector *)res;
+
+ drm_bridge_connector_fini(bridge_connector);
+}
+
/**
* drm_bridge_connector_init - Initialise a connector for a chain of bridges
* @drm: the DRM device
@@ -1155,6 +1182,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
{
struct drm_bridge_connector *bridge_connector;
struct drm_connector *connector;
+ int ret;
bridge_connector = drmm_kzalloc(drm, sizeof(*bridge_connector), GFP_KERNEL);
if (!bridge_connector)
@@ -1164,6 +1192,10 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
if (IS_ERR(connector))
return connector;
+ ret = drmm_add_action_or_reset(drm, drmm_bridge_connector_fini, bridge_connector);
+ if (ret)
+ return ERR_PTR(ret);
+
return connector;
}
EXPORT_SYMBOL_GPL(drm_bridge_connector_init);
--
2.55.0
^ permalink raw reply [flat|nested] 25+ messages in thread* [PATCH RFC v2 06/24] drm/display: bridge-connector: add APIs to add/remove the connector dynamically
2026-10-01 12:42 [PATCH RFC v2 00/24] drm bridge hotplug Luca Ceresoli
` (4 preceding siblings ...)
2026-10-01 12:42 ` [PATCH RFC v2 05/24] drm/display: bridge-connector: use a dynamic connector Luca Ceresoli
@ 2026-10-01 12:42 ` Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 07/24] drm/bridge: samsung-dsim: move drm_bridge_add() call to probe Luca Ceresoli
` (17 subsequent siblings)
23 siblings, 0 replies; 25+ messages in thread
From: Luca Ceresoli @ 2026-10-01 12:42 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Inki Dae, Jagan Teki, Marek Szyprowski, Jessica Zhang,
Douglas Anderson, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, dri-devel,
linux-kernel, imx, linux-arm-kernel
drm_bridge_connector_init() adds a drmm-allocated connector. For bridge
hotplug drmm allocations are not suitable because a connector may have to
be added and removed multiple times in the lifetime of a card.
In preparation for bridge hotplug, add APIs to add and remove a connector
using regular non-managed allocations.
For the dynamic connector, this requires the kfree() the allocated struct
drm_bridge_connector in the destroy func. However that func will be called
even when using the pre-existing drmm API, leading to a double free
(kfree() in the destroy callback + drmm).
One option to avoid this issue is introducing two mostly identical
drm_connector_funcs instances, one with .destroy and one without. But that
would be an annoying code duplication. Instead take a different approach:
always allocate using non-drmm kzalloc_obj(), so that deallocation always
happen in destroy->kfree().
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
Uhm, maybe the change from drmm_kzalloc to kzalloc_obj and addition of
kfree to the destroy callback should eb a separate commit?
---
drivers/gpu/drm/display/drm_bridge_connector.c | 24 +++++++++++++++++++++++-
include/drm/drm_bridge_connector.h | 4 ++++
2 files changed, 27 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index 1c222e27767d..2a0065208fb8 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -285,6 +285,7 @@ static void drm_bridge_connector_dynconn_destroy(struct drm_connector *connector
drm_connector_cleanup(connector);
drm_bridge_connector_put_bridges(bridge_connector);
+ kfree(bridge_connector);
}
static const struct drm_connector_funcs drm_bridge_connector_funcs = {
@@ -1184,7 +1185,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
struct drm_connector *connector;
int ret;
- bridge_connector = drmm_kzalloc(drm, sizeof(*bridge_connector), GFP_KERNEL);
+ bridge_connector = kzalloc_obj(*bridge_connector);
if (!bridge_connector)
return ERR_PTR(-ENOMEM);
@@ -1199,3 +1200,24 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
return connector;
}
EXPORT_SYMBOL_GPL(drm_bridge_connector_init);
+
+struct drm_connector *drm_bridge_connector_dynamic_add(struct drm_device *drm,
+ struct drm_encoder *encoder)
+{
+ struct drm_bridge_connector *bridge_connector;
+
+ bridge_connector = kzalloc_obj(*bridge_connector);
+ if (!bridge_connector)
+ return ERR_PTR(-ENOMEM);
+
+ return drm_bridge_connector_initialize(bridge_connector, drm, encoder);
+}
+EXPORT_SYMBOL_GPL(drm_bridge_connector_dynamic_add);
+
+void drm_bridge_connector_dynamic_remove(struct drm_connector *connector)
+{
+ struct drm_bridge_connector *bridge_connector = to_drm_bridge_connector(connector);
+
+ drm_bridge_connector_fini(bridge_connector);
+}
+EXPORT_SYMBOL_GPL(drm_bridge_connector_dynamic_remove);
diff --git a/include/drm/drm_bridge_connector.h b/include/drm/drm_bridge_connector.h
index 69630815fb09..de6ec91dbfab 100644
--- a/include/drm/drm_bridge_connector.h
+++ b/include/drm/drm_bridge_connector.h
@@ -9,8 +9,12 @@
struct drm_connector;
struct drm_device;
struct drm_encoder;
+struct drm_bridge_connector;
struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
struct drm_encoder *encoder);
+struct drm_connector *drm_bridge_connector_dynamic_add(struct drm_device *drm,
+ struct drm_encoder *encoder);
+void drm_bridge_connector_dynamic_remove(struct drm_connector *connector);
#endif /* __DRM_BRIDGE_CONNECTOR_H__ */
--
2.55.0
^ permalink raw reply [flat|nested] 25+ messages in thread* [PATCH RFC v2 07/24] drm/bridge: samsung-dsim: move drm_bridge_add() call to probe
2026-10-01 12:42 [PATCH RFC v2 00/24] drm bridge hotplug Luca Ceresoli
` (5 preceding siblings ...)
2026-10-01 12:42 ` [PATCH RFC v2 06/24] drm/display: bridge-connector: add APIs to add/remove the connector dynamically Luca Ceresoli
@ 2026-10-01 12:42 ` Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 08/24] drm/bridge: initialize chain_node list head on allocation Luca Ceresoli
` (16 subsequent siblings)
23 siblings, 0 replies; 25+ messages in thread
From: Luca Ceresoli @ 2026-10-01 12:42 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Inki Dae, Jagan Teki, Marek Szyprowski, Jessica Zhang,
Douglas Anderson, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, dri-devel,
linux-kernel, imx, linux-arm-kernel
This bridge driver calls drm_bridge_add() in the DSI host .attach callback
instead of in the probe function. This looks strange, even though
apparently not a problem for currently supported use cases.
However it is a problem for supporting hotplug of DRM bridges, which is in
the works [0][1][2][3]. The problematic case is when this DSI host is
always present while its DSI device is hot-pluggable. In such case with the
current code the DRM card will not be populated until after the DSI device
attaches to the host, and which could happen a very long time after
booting, or even not happen at all.
The reason is that the previous pipeline component (the encoder in this
case) when probing cannot find the samsung-dsim bridge. What happens is:
[1 and 2 can happen in any order, same result]
1) samsung-dsim probes (does not drm_bridge_add() itself)
2) The lcdif starts probing multiple times, but
lcdif_probe
-> lcdif_load
-> lcdif_attach_bridge
-> devm_drm_of_get_bridge() returns -EPROBE_DEFER because
the samsung-dsim is not in the global bridge_list
(deferred probe pending: imx-lcdif: Cannot connect bridge)
The samsung-dsim will not drm_bridge_add() itself until a DSI device will
try to mipi_dsi_attach() to the DSI Host, which can happen arbitratily late
or never on hot-pluggable hardware.
As a preliminary step to supporting hotplug move drm_bridge_add() at probe
time, so that the samsung-dsim DSI host bridge is available during boot,
even without a connected DSI device. This results in:
1) samsung-dsim probes (and adds to drm_bridge_add() itself)
2) The lcdif starts probing multiple times, but
lcdif_probe
-> lcdif_load
-> lcdif_attach_bridge
-> devm_drm_of_get_bridge() --> OK, returns samsung-dsim ptr
-> drm_bridge_attach()
-> samsung_dsim_attach()
-> drm_bridge_attach()
-> -EINVAL because dsi->bridge.next_bridge is still NULL
So moving drm_bridge_add() allows one step further but it is not
enough. The reason is:
* now the encoder driver finds this bridge instead of getting
-EPROBE_DEFER as before
* but it cannot attach it because the bridge attach function in turn tries
to attach to the following bridge, which has not yet been hot-plugged
Solve this by returning 0 in the bridge attach function in case the
following bridge (i.e. the DSI device) is not yet present. In other words,
for the samsung-dsim bridge it is OK to not have a following bridge. It can
be hotplugged later on.
[0] https://lpc.events/event/18/contributions/1750/
[1] https://www.youtube.com/watch?v=C8dEQ4OzMnc
[2] https://lore.kernel.org/lkml/20240924174254.711c7138@booty/
[3] https://lore.kernel.org/lkml/20260507-drm-bridge-alloc-getput-panel_or_bridge-v5-0-472b913b5cb7@bootlin.com/
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
This patch is similar to [4] but different in code and with a largely
rewritten commit message.
[4] https://lore.kernel.org/lkml/20250725-drm-bridge-samsung-dsim-add-in-probe-v1-1-b23d29c23fbd@bootlin.com/
---
drivers/gpu/drm/bridge/samsung-dsim.c | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c
index dc3ff880d7ac..6c48404fd60a 100644
--- a/drivers/gpu/drm/bridge/samsung-dsim.c
+++ b/drivers/gpu/drm/bridge/samsung-dsim.c
@@ -1827,6 +1827,9 @@ static int samsung_dsim_attach(struct drm_bridge *bridge,
{
struct samsung_dsim *dsi = bridge_to_dsi(bridge);
+ if (!dsi->bridge.next_bridge)
+ return 0;
+
return drm_bridge_attach(encoder, dsi->bridge.next_bridge, bridge,
flags);
}
@@ -1965,8 +1968,6 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
mipi_dsi_pixel_format_to_bpp(device->format),
device->mode_flags);
- drm_bridge_add(&dsi->bridge);
-
/*
* This is a temporary solution and should be made by more generic way.
*
@@ -1976,7 +1977,7 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO)) {
ret = samsung_dsim_register_te_irq(dsi, &device->dev);
if (ret)
- goto err_remove_bridge;
+ return ret;
}
// The next bridge can be used by host_ops->attach
@@ -1998,8 +1999,6 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
drm_bridge_clear_and_put(&dsi->bridge.next_bridge);
if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO))
samsung_dsim_unregister_te_irq(dsi);
-err_remove_bridge:
- drm_bridge_remove(&dsi->bridge);
return ret;
}
@@ -2016,8 +2015,6 @@ static int samsung_dsim_host_detach(struct mipi_dsi_host *host,
samsung_dsim_unregister_te_irq(dsi);
- drm_bridge_remove(&dsi->bridge);
-
return 0;
}
@@ -2216,6 +2213,8 @@ int samsung_dsim_probe(struct platform_device *pdev)
goto err_disable_runtime;
}
+ drm_bridge_add(&dsi->bridge);
+
return 0;
err_disable_runtime:
@@ -2229,6 +2228,8 @@ void samsung_dsim_remove(struct platform_device *pdev)
{
struct samsung_dsim *dsi = platform_get_drvdata(pdev);
+ drm_bridge_remove(&dsi->bridge);
+
pm_runtime_disable(&pdev->dev);
if (dsi->plat_data->host_ops && dsi->plat_data->host_ops->unregister_host)
--
2.55.0
^ permalink raw reply [flat|nested] 25+ messages in thread* [PATCH RFC v2 08/24] drm/bridge: initialize chain_node list head on allocation
2026-10-01 12:42 [PATCH RFC v2 00/24] drm bridge hotplug Luca Ceresoli
` (6 preceding siblings ...)
2026-10-01 12:42 ` [PATCH RFC v2 07/24] drm/bridge: samsung-dsim: move drm_bridge_add() call to probe Luca Ceresoli
@ 2026-10-01 12:42 ` Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 09/24] drm/bridge: initialize chain_node list head on detach and attach errors Luca Ceresoli
` (15 subsequent siblings)
23 siblings, 0 replies; 25+ messages in thread
From: Luca Ceresoli @ 2026-10-01 12:42 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Inki Dae, Jagan Teki, Marek Szyprowski, Jessica Zhang,
Douglas Anderson, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, dri-devel,
linux-kernel, imx, linux-arm-kernel
In preparation to add a check to detect whether a bridge is not yet
attached, ensure the chain_node list_head is always empty [as in
list_empty()] since it is allocated, until it is attached.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/drm_bridge.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index 1b4ee746acb9..cd0c246f7e99 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -418,6 +418,7 @@ void *__devm_drm_bridge_alloc(struct device *dev, size_t size, size_t offset,
return ERR_PTR(-ENOMEM);
bridge = container + offset;
+ INIT_LIST_HEAD(&bridge->chain_node);
INIT_LIST_HEAD(&bridge->list);
bridge->container = container;
bridge->funcs = funcs;
--
2.55.0
^ permalink raw reply [flat|nested] 25+ messages in thread* [PATCH RFC v2 09/24] drm/bridge: initialize chain_node list head on detach and attach errors
2026-10-01 12:42 [PATCH RFC v2 00/24] drm bridge hotplug Luca Ceresoli
` (7 preceding siblings ...)
2026-10-01 12:42 ` [PATCH RFC v2 08/24] drm/bridge: initialize chain_node list head on allocation Luca Ceresoli
@ 2026-10-01 12:42 ` Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 10/24] drm/encoder: add drm_encoder_cleanup_from() Luca Ceresoli
` (14 subsequent siblings)
23 siblings, 0 replies; 25+ messages in thread
From: Luca Ceresoli @ 2026-10-01 12:42 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Inki Dae, Jagan Teki, Marek Szyprowski, Jessica Zhang,
Douglas Anderson, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, dri-devel,
linux-kernel, imx, linux-arm-kernel
When a bridge is detached it is removed from the encoder bridge_chain list,
but the bridge::chain_node list head is not cleared. This is going to be
problematic with the upcoming hotplug bridge support because if a bridge is
detached from the encoder chain but not yet removed, when later detaching
it the encoder code may think it is still attached, thus trying to detach
it twice.
Avoid this by clearing the list head on detach, so there's a clear and
simple way to know when a bridge is not attached anymore.
Do the same in the error management code in drm_bridge_attach(), so that
chain_node is always empty [as in list_empty()] when it is not
(yet|anymore) in the bridge chain.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/drm_bridge.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index cd0c246f7e99..2a112ed69e20 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -663,7 +663,7 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
bridge->dev = NULL;
bridge->encoder = NULL;
mutex_lock(&encoder->bridge_chain_mutex);
- list_del(&bridge->chain_node);
+ list_del_init(&bridge->chain_node);
mutex_unlock(&encoder->bridge_chain_mutex);
if (ret != -EPROBE_DEFER)
@@ -693,7 +693,7 @@ void drm_bridge_detach(struct drm_bridge *bridge)
if (bridge->funcs->detach)
bridge->funcs->detach(bridge);
- list_del(&bridge->chain_node);
+ list_del_init(&bridge->chain_node);
bridge->dev = NULL;
drm_bridge_put(bridge);
}
--
2.55.0
^ permalink raw reply [flat|nested] 25+ messages in thread* [PATCH RFC v2 10/24] drm/encoder: add drm_encoder_cleanup_from()
2026-10-01 12:42 [PATCH RFC v2 00/24] drm bridge hotplug Luca Ceresoli
` (8 preceding siblings ...)
2026-10-01 12:42 ` [PATCH RFC v2 09/24] drm/bridge: initialize chain_node list head on detach and attach errors Luca Ceresoli
@ 2026-10-01 12:42 ` Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 11/24] drm/atomic: move drm_atomic_helper_disable_all() and drm_atomic_helper_shutdown() from drm_atomic_helper to drm_atomic Luca Ceresoli
` (13 subsequent siblings)
23 siblings, 0 replies; 25+ messages in thread
From: Luca Ceresoli @ 2026-10-01 12:42 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Inki Dae, Jagan Teki, Marek Szyprowski, Jessica Zhang,
Douglas Anderson, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, dri-devel,
linux-kernel, imx, linux-arm-kernel
Supporting hardware whose final part of the DRM pipeline can be physically
removed requires the ability to detach all bridges from a given point to
the end of the pipeline.
Introduce a variant of drm_encoder_cleanup() for this.
Take particular care to not try to detach non-attached bridges. This is
needed because when 2 or more bridges are removed not in the backwards
order, drm_encoder_cleanup_from() is called more than once for bridges
closer to the panel.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
Note: in theory drm_encoder_cleanup() is now a superset of
drm_encoder_cleanup_from() and may be simplified to jut call
drm_encoder_cleanup_from() and then do the extra actions. However the
common code is subtly different in terms of locking and checks, so this
would complicate the code in this patch and has thus been kept separate for
the time being. Reimplementing drm_encoder_cleanup() by using
drm_encoder_cleanup_from() is still an option, either in a new iteration of
this patch or as a future patch.
A much simpler and now obsolete version of this patch (missing locking and
checks) previously appeared in
https://lore.kernel.org/lkml/20250206-hotplug-drm-bridge-v6-13-9d6f2c9c3058@bootlin.com/
---
drivers/gpu/drm/drm_encoder.c | 38 ++++++++++++++++++++++++++++++++++++++
include/drm/drm_encoder.h | 1 +
2 files changed, 39 insertions(+)
diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c
index 0d5dbed06db4..40ece477b302 100644
--- a/drivers/gpu/drm/drm_encoder.c
+++ b/drivers/gpu/drm/drm_encoder.c
@@ -179,6 +179,44 @@ int drm_encoder_init(struct drm_device *dev,
}
EXPORT_SYMBOL(drm_encoder_init);
+/**
+ * drm_encoder_cleanup_from - remove a given bridge and all the following
+ * @encoder: encoder whole list of bridges shall be pruned
+ * @bridge: first bridge to remove
+ *
+ * Removes from an encoder all the bridges starting with a given bridge
+ * and until the end of the chain.
+ *
+ * Does nothing if the bridge is not attached to an encoder chain.
+ *
+ * This should not be used in "normal" DRM pipelines. It is only useful for
+ * devices whose final part of the DRM chain can be physically removed and
+ * later reconnected (possibly with different hardware).
+ */
+void drm_encoder_cleanup_from(struct drm_encoder *encoder, struct drm_bridge *bridge)
+{
+ struct drm_bridge *next;
+ LIST_HEAD(tmplist);
+
+ /*
+ * We need the bridge_chain_mutex to modify the chain, but
+ * drm_bridge_detach() will call DRM_MODESET_LOCK_ALL_BEGIN() (in
+ * drm_modeset_lock_fini()), resulting in a possible ABBA circular
+ * deadlock. Avoid it by first moving all the bridges to a
+ * temporary list holding the lock, and then calling
+ * drm_bridge_detach() without the lock.
+ */
+ mutex_lock(&encoder->bridge_chain_mutex);
+ if (!list_empty(&bridge->chain_node))
+ list_for_each_entry_safe_from(bridge, next, &encoder->bridge_chain, chain_node)
+ list_move_tail(&bridge->chain_node, &tmplist);
+ mutex_unlock(&encoder->bridge_chain_mutex);
+
+ while (!list_empty(&tmplist))
+ drm_bridge_detach(list_first_entry(&tmplist, struct drm_bridge, chain_node));
+}
+EXPORT_SYMBOL(drm_encoder_cleanup_from);
+
/**
* drm_encoder_cleanup - cleans up an initialised encoder
* @encoder: encoder to cleanup
diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h
index eded7c34481a..d2a59f95692f 100644
--- a/include/drm/drm_encoder.h
+++ b/include/drm/drm_encoder.h
@@ -324,6 +324,7 @@ static inline struct drm_encoder *drm_encoder_find(struct drm_device *dev,
}
void drm_encoder_cleanup(struct drm_encoder *encoder);
+void drm_encoder_cleanup_from(struct drm_encoder *encoder, struct drm_bridge *bridge);
/**
* drm_for_each_encoder_mask - iterate over encoders specified by bitmask
--
2.55.0
^ permalink raw reply [flat|nested] 25+ messages in thread* [PATCH RFC v2 11/24] drm/atomic: move drm_atomic_helper_disable_all() and drm_atomic_helper_shutdown() from drm_atomic_helper to drm_atomic
2026-10-01 12:42 [PATCH RFC v2 00/24] drm bridge hotplug Luca Ceresoli
` (9 preceding siblings ...)
2026-10-01 12:42 ` [PATCH RFC v2 10/24] drm/encoder: add drm_encoder_cleanup_from() Luca Ceresoli
@ 2026-10-01 12:42 ` Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 12/24] drm/bridge: shutdown and cleanup on bridge unplug Luca Ceresoli
` (12 subsequent siblings)
23 siblings, 0 replies; 25+ messages in thread
From: Luca Ceresoli @ 2026-10-01 12:42 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Inki Dae, Jagan Teki, Marek Szyprowski, Jessica Zhang,
Douglas Anderson, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, dri-devel,
linux-kernel, imx, linux-arm-kernel
drm_atomic_helper_shutdown() is needed to support the upcoming DRM bridge
hot-unplug, and will have to be called by the encoder code when a bridge
device is removed in order to detach it from the encoder chain. However
this would create a module dependency loop between the drm module (where
drm_encoder is) and the drm_kms_helper module where
drm_atomic_helper_shutdown() function currently is.
Solve by moving it, along with its callee drm_atomic_helper_disable_all(),
to drm_atomic which is in the drm module. Use identical names except for
dropping the "_atomic" infix, and make the original functions a deprecated
wrapper to the new ones.
No changes to the functions body.
No functional changes except for moving the code to a different module.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/drm_atomic.c | 115 ++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/drm_atomic_helper.c | 76 ++----------------------
include/drm/drm_atomic.h | 3 +
3 files changed, 124 insertions(+), 70 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 61fd7f12a474..d39aaa8c9b8d 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -2087,6 +2087,121 @@ int __drm_atomic_helper_set_config(struct drm_mode_set *set,
}
EXPORT_SYMBOL(__drm_atomic_helper_set_config);
+/**
+ * drm_atomic_disable_all - disable all currently active outputs
+ * @dev: DRM device
+ * @ctx: lock acquisition context
+ *
+ * Loops through all connectors, finding those that aren't turned off and then
+ * turns them off by setting their DPMS mode to OFF and deactivating the CRTC
+ * that they are connected to.
+ *
+ * This is used for example in suspend/resume to disable all currently active
+ * functions when suspending. If you just want to shut down everything at e.g.
+ * driver unload, look at drm_atomic_helper_shutdown().
+ *
+ * Note that if callers haven't already acquired all modeset locks this might
+ * return -EDEADLK, which must be handled by calling drm_modeset_backoff().
+ *
+ * Returns:
+ * 0 on success or a negative error code on failure.
+ *
+ * See also:
+ * drm_atomic_helper_suspend(), drm_atomic_helper_resume() and
+ * drm_atomic_helper_shutdown().
+ */
+int drm_atomic_disable_all(struct drm_device *dev,
+ struct drm_modeset_acquire_ctx *ctx)
+{
+ struct drm_atomic_commit *state;
+ struct drm_connector_state *conn_state;
+ struct drm_connector *conn;
+ struct drm_plane_state *plane_state;
+ struct drm_plane *plane;
+ struct drm_crtc_state *crtc_state;
+ struct drm_crtc *crtc;
+ int ret, i;
+
+ state = drm_atomic_commit_alloc(dev);
+ if (!state)
+ return -ENOMEM;
+
+ state->acquire_ctx = ctx;
+
+ drm_for_each_crtc(crtc, dev) {
+ crtc_state = drm_atomic_get_crtc_state(state, crtc);
+ if (IS_ERR(crtc_state)) {
+ ret = PTR_ERR(crtc_state);
+ goto free;
+ }
+
+ crtc_state->active = false;
+
+ ret = drm_atomic_set_mode_prop_for_crtc(crtc_state, NULL);
+ if (ret < 0)
+ goto free;
+
+ ret = drm_atomic_add_affected_planes(state, crtc);
+ if (ret < 0)
+ goto free;
+
+ ret = drm_atomic_add_affected_connectors(state, crtc);
+ if (ret < 0)
+ goto free;
+ }
+
+ for_each_new_connector_in_state(state, conn, conn_state, i) {
+ ret = drm_atomic_set_crtc_for_connector(conn_state, NULL);
+ if (ret < 0)
+ goto free;
+ }
+
+ for_each_new_plane_in_state(state, plane, plane_state, i) {
+ ret = drm_atomic_set_crtc_for_plane(plane_state, NULL);
+ if (ret < 0)
+ goto free;
+
+ drm_atomic_set_fb_for_plane(plane_state, NULL);
+ }
+
+ ret = drm_atomic_commit(state);
+free:
+ drm_atomic_commit_put(state);
+ return ret;
+}
+EXPORT_SYMBOL(drm_atomic_disable_all);
+
+/**
+ * drm_atomic_shutdown - shutdown all CRTC
+ * @dev: DRM device
+ *
+ * This shuts down all CRTC, which is useful for driver unloading. Shutdown on
+ * suspend should instead be handled with drm_atomic_helper_suspend(), since
+ * that also takes a snapshot of the modeset state to be restored on resume.
+ *
+ * This is just a convenience wrapper around drm_atomic_helper_disable_all(),
+ * and it is the atomic version of drm_helper_force_disable_all().
+ */
+void drm_atomic_shutdown(struct drm_device *dev)
+{
+ struct drm_modeset_acquire_ctx ctx;
+ int ret;
+
+ if (dev == NULL)
+ return;
+
+ DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, ret);
+
+ ret = drm_atomic_disable_all(dev, &ctx);
+ if (ret)
+ drm_err(dev,
+ "Disabling all crtc's during unload failed with %i\n",
+ ret);
+
+ DRM_MODESET_LOCK_ALL_END(dev, ctx, ret);
+}
+EXPORT_SYMBOL(drm_atomic_shutdown);
+
static void drm_atomic_private_obj_print_state(struct drm_printer *p,
const struct drm_private_state *state)
{
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 9d006f98413a..3b8301a385b5 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -3539,6 +3539,8 @@ EXPORT_SYMBOL(drm_atomic_helper_set_config);
* @dev: DRM device
* @ctx: lock acquisition context
*
+ * Deprecated wrapper to drm_atomic_disable_all().
+ *
* Loops through all connectors, finding those that aren't turned off and then
* turns them off by setting their DPMS mode to OFF and deactivating the CRTC
* that they are connected to.
@@ -3560,61 +3562,7 @@ EXPORT_SYMBOL(drm_atomic_helper_set_config);
int drm_atomic_helper_disable_all(struct drm_device *dev,
struct drm_modeset_acquire_ctx *ctx)
{
- struct drm_atomic_commit *state;
- struct drm_connector_state *conn_state;
- struct drm_connector *conn;
- struct drm_plane_state *plane_state;
- struct drm_plane *plane;
- struct drm_crtc_state *crtc_state;
- struct drm_crtc *crtc;
- int ret, i;
-
- state = drm_atomic_commit_alloc(dev);
- if (!state)
- return -ENOMEM;
-
- state->acquire_ctx = ctx;
-
- drm_for_each_crtc(crtc, dev) {
- crtc_state = drm_atomic_get_crtc_state(state, crtc);
- if (IS_ERR(crtc_state)) {
- ret = PTR_ERR(crtc_state);
- goto free;
- }
-
- crtc_state->active = false;
-
- ret = drm_atomic_set_mode_prop_for_crtc(crtc_state, NULL);
- if (ret < 0)
- goto free;
-
- ret = drm_atomic_add_affected_planes(state, crtc);
- if (ret < 0)
- goto free;
-
- ret = drm_atomic_add_affected_connectors(state, crtc);
- if (ret < 0)
- goto free;
- }
-
- for_each_new_connector_in_state(state, conn, conn_state, i) {
- ret = drm_atomic_set_crtc_for_connector(conn_state, NULL);
- if (ret < 0)
- goto free;
- }
-
- for_each_new_plane_in_state(state, plane, plane_state, i) {
- ret = drm_atomic_set_crtc_for_plane(plane_state, NULL);
- if (ret < 0)
- goto free;
-
- drm_atomic_set_fb_for_plane(plane_state, NULL);
- }
-
- ret = drm_atomic_commit(state);
-free:
- drm_atomic_commit_put(state);
- return ret;
+ return drm_atomic_disable_all(dev, ctx);
}
EXPORT_SYMBOL(drm_atomic_helper_disable_all);
@@ -3670,6 +3618,8 @@ EXPORT_SYMBOL(drm_atomic_helper_reset_crtc);
* drm_atomic_helper_shutdown - shutdown all CRTC
* @dev: DRM device
*
+ * Deprecated wrapper to drm_atomic_shutdown().
+ *
* This shuts down all CRTC, which is useful for driver unloading. Shutdown on
* suspend should instead be handled with drm_atomic_helper_suspend(), since
* that also takes a snapshot of the modeset state to be restored on resume.
@@ -3679,21 +3629,7 @@ EXPORT_SYMBOL(drm_atomic_helper_reset_crtc);
*/
void drm_atomic_helper_shutdown(struct drm_device *dev)
{
- struct drm_modeset_acquire_ctx ctx;
- int ret;
-
- if (dev == NULL)
- return;
-
- DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, ret);
-
- ret = drm_atomic_helper_disable_all(dev, &ctx);
- if (ret)
- drm_err(dev,
- "Disabling all crtc's during unload failed with %i\n",
- ret);
-
- DRM_MODESET_LOCK_ALL_END(dev, ctx, ret);
+ return drm_atomic_shutdown(dev);
}
EXPORT_SYMBOL(drm_atomic_helper_shutdown);
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
index 3ae35b09c0cf..aa6de7d959b2 100644
--- a/include/drm/drm_atomic.h
+++ b/include/drm/drm_atomic.h
@@ -1409,5 +1409,8 @@ drm_atomic_get_old_bridge_state(const struct drm_atomic_commit *state,
struct drm_bridge_state *
drm_atomic_get_new_bridge_state(const struct drm_atomic_commit *state,
struct drm_bridge *bridge);
+int drm_atomic_disable_all(struct drm_device *dev,
+ struct drm_modeset_acquire_ctx *ctx);
+void drm_atomic_shutdown(struct drm_device *dev);
#endif /* DRM_ATOMIC_H_ */
--
2.55.0
^ permalink raw reply [flat|nested] 25+ messages in thread* [PATCH RFC v2 12/24] drm/bridge: shutdown and cleanup on bridge unplug
2026-10-01 12:42 [PATCH RFC v2 00/24] drm bridge hotplug Luca Ceresoli
` (10 preceding siblings ...)
2026-10-01 12:42 ` [PATCH RFC v2 11/24] drm/atomic: move drm_atomic_helper_disable_all() and drm_atomic_helper_shutdown() from drm_atomic_helper to drm_atomic Luca Ceresoli
@ 2026-10-01 12:42 ` Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 13/24] drm/mipi-dsi: turn DRM_MIPI_DSI into a tristate Luca Ceresoli
` (11 subsequent siblings)
23 siblings, 0 replies; 25+ messages in thread
From: Luca Ceresoli @ 2026-10-01 12:42 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Inki Dae, Jagan Teki, Marek Szyprowski, Jessica Zhang,
Douglas Anderson, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, dri-devel,
linux-kernel, imx, linux-arm-kernel
With the upcoming support for DRM bridge hot(un)plugging, bridges can be
removed at any time. When this happens, shutdown the pipeline and detach
from the encoder chain the bridge being removed along with all the
following ones.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/drm_bridge.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index 2a112ed69e20..c0a2f7f1ce62 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -509,9 +509,17 @@ EXPORT_SYMBOL(devm_drm_bridge_add);
* it won't be found by users via of_drm_find_and_get_bridge(), and add it
* to the lingering bridge list, to keep track of it until its allocated
* memory is eventually freed.
+ *
+ * If the bridge was attached, also shutdown CRTCs and detach this bridge
+ * and the following ones.
*/
void drm_bridge_remove(struct drm_bridge *bridge)
{
+ if (bridge->encoder) {
+ drm_atomic_shutdown(bridge->dev);
+ drm_encoder_cleanup_from(bridge->encoder, bridge);
+ }
+
mutex_lock(&bridge_lock);
list_move_tail(&bridge->list, &bridge_lingering_list);
mutex_unlock(&bridge_lock);
--
2.55.0
^ permalink raw reply [flat|nested] 25+ messages in thread* [PATCH RFC v2 13/24] drm/mipi-dsi: turn DRM_MIPI_DSI into a tristate
2026-10-01 12:42 [PATCH RFC v2 00/24] drm bridge hotplug Luca Ceresoli
` (11 preceding siblings ...)
2026-10-01 12:42 ` [PATCH RFC v2 12/24] drm/bridge: shutdown and cleanup on bridge unplug Luca Ceresoli
@ 2026-10-01 12:42 ` Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 14/24] drm: event-notifier: add mechanism to notify about hotplug events Luca Ceresoli
` (10 subsequent siblings)
23 siblings, 0 replies; 25+ messages in thread
From: Luca Ceresoli @ 2026-10-01 12:42 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Inki Dae, Jagan Teki, Marek Szyprowski, Jessica Zhang,
Douglas Anderson, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, dri-devel,
linux-kernel, imx, linux-arm-kernel
DRM_MIPI_DSI is currently a bool, but there's no reason to not be allowed
to build it as a loadable module.
Moreover being a bool prevents DRM_MIPI_DSI to depend on a tristate module
that is configured as 'm'.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 65d46dfa7266..9bdd9110e754 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -39,7 +39,7 @@ config DRM_MIPI_DBI
select DRM_KMS_HELPER
config DRM_MIPI_DSI
- bool
+ tristate
depends on DRM
config DRM_KMS_HELPER
--
2.55.0
^ permalink raw reply [flat|nested] 25+ messages in thread* [PATCH RFC v2 14/24] drm: event-notifier: add mechanism to notify about hotplug events
2026-10-01 12:42 [PATCH RFC v2 00/24] drm bridge hotplug Luca Ceresoli
` (12 preceding siblings ...)
2026-10-01 12:42 ` [PATCH RFC v2 13/24] drm/mipi-dsi: turn DRM_MIPI_DSI into a tristate Luca Ceresoli
@ 2026-10-01 12:42 ` Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 15/24] drm/bridge: notify about detached bridges Luca Ceresoli
` (9 subsequent siblings)
23 siblings, 0 replies; 25+ messages in thread
From: Luca Ceresoli @ 2026-10-01 12:42 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Inki Dae, Jagan Teki, Marek Szyprowski, Jessica Zhang,
Douglas Anderson, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, dri-devel,
linux-kernel, imx, linux-arm-kernel
In preparation for supporting DRM bridge hotplug, add an event notifier to
allow interested parties to be notified about events they need to react to.
For the initial implementation of bridge hotplug, two events are needed:
bridge detach (happening in drm_bridge.c) and MIPI device attach to MIPI
host (happening in drm_mipi_dsi.c).
For this reason implement the event notifier in a new common file that
event producers can easily use to send events.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
A different approach I have considered is keeping the event notifier in
drm_bridge.c (as in [0]) instead of a new centralized file. But then
another notifier would be needed in drm_mipi_dsi.c for the DSI attach
event. That would be particularly awkward because the designated component
to implement hotplug is the drm_bridge_connector, which would then need to
depend on DRM_MIPI_DSI even though it does nothing MIPI specific.
Changes in v2:
- added missing include
[0] https://lore.kernel.org/lkml/20250206-hotplug-drm-bridge-v6-12-9d6f2c9c3058@bootlin.com/
---
drivers/gpu/drm/Kconfig | 3 ++
drivers/gpu/drm/Makefile | 2 ++
drivers/gpu/drm/drm_event_notifier.c | 58 ++++++++++++++++++++++++++++++++++++
include/drm/drm_event_notifier.h | 38 +++++++++++++++++++++++
4 files changed, 101 insertions(+)
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 9bdd9110e754..e661241ed1df 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -33,6 +33,9 @@ endmenu
if DRM
+config DRM_EVENT_NOTIFIER
+ tristate
+
config DRM_MIPI_DBI
tristate
depends on DRM
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index cadc8529c995..ac75a13592c4 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -94,6 +94,8 @@ drm-$(CONFIG_DRM_DRAW) += drm_draw.o
drm-$(CONFIG_DRM_RAS) += drm_ras.o drm_ras_nl.o drm_ras_genl_family.o
obj-$(CONFIG_DRM) += drm.o
+obj-$(CONFIG_DRM_EVENT_NOTIFIER) += drm_event_notifier.o
+
obj-$(CONFIG_DRM_PANEL) += drm_panel.o
obj-$(CONFIG_DRM_PANEL_ORIENTATION_QUIRKS) += drm_panel_orientation_quirks.o
obj-$(CONFIG_DRM_PANEL_BACKLIGHT_QUIRKS) += drm_panel_backlight_quirks.o
diff --git a/drivers/gpu/drm/drm_event_notifier.c b/drivers/gpu/drm/drm_event_notifier.c
new file mode 100644
index 000000000000..76af4dd4cdb0
--- /dev/null
+++ b/drivers/gpu/drm/drm_event_notifier.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Internal event notifier for DRM drivers
+ *
+ * Copyright (C) 2026 GE HealthCare
+ * Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
+ */
+
+#include <linux/module.h>
+#include <linux/notifier.h>
+
+#include <drm/drm_event_notifier.h>
+
+static BLOCKING_NOTIFIER_HEAD(drm_event_notifier);
+
+/**
+ * drm_event_notifier_register - Register to be notified of DRM events
+ * @nb: the notifier block to be registered
+ *
+ * @nb will be notified of events defined in &drm_event_notifier_event
+ *
+ * Returns 0 on success, %-EEXIST on error.
+ */
+int drm_event_notifier_register(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&drm_event_notifier, nb);
+}
+EXPORT_SYMBOL(drm_event_notifier_register);
+
+/**
+ * drm_event_notifier_unregister - Unregister from be notified of DRM events
+ * @nb: the notifier block to be unregistered
+ *
+ * @nb will stop being notified of events defined in &drm_event_notifier_event
+ *
+ * Returns zero on success or %-ENOENT on failure.
+ */
+int drm_event_notifier_unregister(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&drm_event_notifier, nb);
+}
+EXPORT_SYMBOL(drm_event_notifier_unregister);
+
+/**
+ * drm_event_notifier_notify - Emit an event to be notified to registered
+ * entities
+ * @event: event ID as defined in &drm_event_notifier_event
+ * @data: metadata associated to the event
+ */
+void drm_event_notifier_notify(unsigned long event, void *data)
+{
+ blocking_notifier_call_chain(&drm_event_notifier, event, data);
+}
+EXPORT_SYMBOL(drm_event_notifier_notify);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>");
+MODULE_DESCRIPTION("Notifier for DRM components addition/removal and attach/detach");
diff --git a/include/drm/drm_event_notifier.h b/include/drm/drm_event_notifier.h
new file mode 100644
index 000000000000..2457719d50fe
--- /dev/null
+++ b/include/drm/drm_event_notifier.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Internal event notifier for DRM drivers
+ *
+ * Copyright (C) 2026 GE HealthCare
+ * Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
+ */
+
+#ifndef _DRM_EVENT_NOTIFIER_H_
+#define _DRM_EVENT_NOTIFIER_H_
+
+#include <linux/notifier.h>
+
+/**
+ * enum drm_event_notifier_event - DRM bridge events
+ */
+enum drm_event_notifier_event {
+ /**
+ * @DRM_MIPI_DSI_ATTACHED: A MIPI DSI device has just been attached
+ * to its MIPI DSI host. @data is a pointer to the &struct
+ * mipi_dsi_device that has just attached.
+ */
+ DRM_MIPI_DSI_ATTACHED,
+ /**
+ * @DRM_BRIDGE_NOTIFY_DETACHED: A bridge has just been detached
+ * from the encoder bridge chain. Emitted at the end of
+ * drm_bridge_detach(), after removing the bridge from the encoder
+ * chain. @data is a pointer to the &struct drm_bridge that has
+ * just been detached.
+ */
+ DRM_BRIDGE_DETACHED,
+};
+
+int drm_event_notifier_register(struct notifier_block *nb);
+int drm_event_notifier_unregister(struct notifier_block *nb);
+void drm_event_notifier_notify(unsigned long event, void *data);
+
+#endif /* _DRM_EVENT_NOTIFIER_H_ */
--
2.55.0
^ permalink raw reply [flat|nested] 25+ messages in thread* [PATCH RFC v2 15/24] drm/bridge: notify about detached bridges
2026-10-01 12:42 [PATCH RFC v2 00/24] drm bridge hotplug Luca Ceresoli
` (13 preceding siblings ...)
2026-10-01 12:42 ` [PATCH RFC v2 14/24] drm: event-notifier: add mechanism to notify about hotplug events Luca Ceresoli
@ 2026-10-01 12:42 ` Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 16/24] drm/mipi-dsi: notify about DSI attach Luca Ceresoli
` (8 subsequent siblings)
23 siblings, 0 replies; 25+ messages in thread
From: Luca Ceresoli @ 2026-10-01 12:42 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Inki Dae, Jagan Teki, Marek Szyprowski, Jessica Zhang,
Douglas Anderson, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, dri-devel,
linux-kernel, imx, linux-arm-kernel
In preparation to support DRM bridge hotplug, let the drm_bridge code emit
an event when a bridge is detached, so that this event can trigger the
actions needed to deconfigure the pipeline and unregister the connector as
appropriate.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/Kconfig | 1 +
drivers/gpu/drm/drm_bridge.c | 4 ++++
2 files changed, 5 insertions(+)
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index e661241ed1df..17c86244c75a 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -17,6 +17,7 @@ menuconfig DRM
# device and dmabuf fd. Let's make sure that is available for our userspace.
select KCMP
select VIDEO
+ select DRM_EVENT_NOTIFIER
help
Kernel-level support for the Direct Rendering Infrastructure (DRI)
introduced in XFree86 4.0. If you say Y here, you need to select
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index c0a2f7f1ce62..c825e80b9a7f 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -34,6 +34,7 @@
#include <drm/drm_debugfs.h>
#include <drm/drm_edid.h>
#include <drm/drm_encoder.h>
+#include <drm/drm_event_notifier.h>
#include <drm/drm_file.h>
#include <drm/drm_of.h>
#include <drm/drm_print.h>
@@ -702,6 +703,9 @@ void drm_bridge_detach(struct drm_bridge *bridge)
bridge->funcs->detach(bridge);
list_del_init(&bridge->chain_node);
+
+ drm_event_notifier_notify(DRM_BRIDGE_DETACHED, bridge);
+
bridge->dev = NULL;
drm_bridge_put(bridge);
}
--
2.55.0
^ permalink raw reply [flat|nested] 25+ messages in thread* [PATCH RFC v2 16/24] drm/mipi-dsi: notify about DSI attach
2026-10-01 12:42 [PATCH RFC v2 00/24] drm bridge hotplug Luca Ceresoli
` (14 preceding siblings ...)
2026-10-01 12:42 ` [PATCH RFC v2 15/24] drm/bridge: notify about detached bridges Luca Ceresoli
@ 2026-10-01 12:42 ` Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 17/24] drm/bridge: add drm_bridge_get_next() and supporting func Luca Ceresoli
` (7 subsequent siblings)
23 siblings, 0 replies; 25+ messages in thread
From: Luca Ceresoli @ 2026-10-01 12:42 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Inki Dae, Jagan Teki, Marek Szyprowski, Jessica Zhang,
Douglas Anderson, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, dri-devel,
linux-kernel, imx, linux-arm-kernel
In preparation to support DRM bridge hotplug, let the drm_mipi_dsi code
emit an event when a DSI device is attached to the corresponding DSI host,
so that this event can trigger the actions needed to deconfigure the
pipeline and unregister the connector as appropriate.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/Kconfig | 1 +
drivers/gpu/drm/drm_mipi_dsi.c | 3 +++
2 files changed, 4 insertions(+)
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 17c86244c75a..59015e89de92 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -45,6 +45,7 @@ config DRM_MIPI_DBI
config DRM_MIPI_DSI
tristate
depends on DRM
+ select DRM_EVENT_NOTIFIER
config DRM_KMS_HELPER
tristate
diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index 3ac1dd5ad640..eaa474da4a51 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -34,6 +34,7 @@
#include <linux/slab.h>
#include <drm/display/drm_dsc.h>
+#include <drm/drm_event_notifier.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_print.h>
@@ -386,6 +387,8 @@ int mipi_dsi_attach(struct mipi_dsi_device *dsi)
dsi->attached = true;
+ drm_event_notifier_notify(DRM_MIPI_DSI_ATTACHED, dsi);
+
return 0;
}
EXPORT_SYMBOL(mipi_dsi_attach);
--
2.55.0
^ permalink raw reply [flat|nested] 25+ messages in thread* [PATCH RFC v2 17/24] drm/bridge: add drm_bridge_get_next() and supporting func
2026-10-01 12:42 [PATCH RFC v2 00/24] drm bridge hotplug Luca Ceresoli
` (15 preceding siblings ...)
2026-10-01 12:42 ` [PATCH RFC v2 16/24] drm/mipi-dsi: notify about DSI attach Luca Ceresoli
@ 2026-10-01 12:42 ` Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 18/24] drm/panel: implement .get_next_bridge Luca Ceresoli
` (6 subsequent siblings)
23 siblings, 0 replies; 25+ messages in thread
From: Luca Ceresoli @ 2026-10-01 12:42 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Inki Dae, Jagan Teki, Marek Szyprowski, Jessica Zhang,
Douglas Anderson, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, dri-devel,
linux-kernel, imx, linux-arm-kernel
For bridge hotplug we need to successfully probe a card with an incomplete
bridge chain, i.e. a chain whose last bridge currently in bridge_chain
needs another bridge at its output. Such a card would have no connector,
and be able to add one as soon as the followong bridges are added up to the
bridge that requires no further ones (like a panel or a connector_bridge).
So we need a way to know whether the pipeline is complete in the hardware
(all bridges probed)), in order to complete it in software (attach all
bridges not yet attached). Currently common DRM code has no way to know
that.
Add drm_bridge_get_next() and a supporting get_next_bridge func so each
bridge can expose its next bridge, and whether there's supposed to be one.
A subsequent commit will use this function to detect whether the pipeline
is complete in the hardware or not.
Link: https://lore.kernel.org/r/20260624-vagabond-neon-gorilla-cd6487@houat
Suggested-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/drm_bridge.c | 28 ++++++++++++++++++++++++++++
include/drm/drm_bridge.h | 26 ++++++++++++++++++++++++++
2 files changed, 54 insertions(+)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index c825e80b9a7f..6a00dba0c2c8 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -710,6 +710,34 @@ void drm_bridge_detach(struct drm_bridge *bridge)
drm_bridge_put(bridge);
}
+/**
+ * drm_bridge_get_next - return the bridge at this bridge's output port
+ *
+ * Return the next bridge, i.e. the bridge that is connected at the output
+ * port of @bridge. The next bridge might or not be in the encoder chain.
+ *
+ * Returns:
+ * * Pointer to a bridge connected to the output port of this bridge,
+ * with refcount incremented; call drm_bridge_put() when done
+ * * ERR_PTR(-ENODEV): this bridge has an output port where a next bridge
+ * needs to be present for video output, but the next
+ * bridge is not currently available
+ * * NULL: this bridge does not have an output port where a next bridge
+ * is expected
+ * * ERR_PTR(-ENOENT): the bridge does not implement the func
+ * * Another negative error returned by the bridge func
+ */
+struct drm_bridge *drm_bridge_get_next(struct drm_bridge *bridge)
+{
+ if (!(bridge->ops & DRM_BRIDGE_OP_GET_NEXT_BRIDGE)) {
+ drm_warn_once(bridge->dev, "get_next_bridge func not implemented!");
+ return ERR_PTR(-ENOENT);
+ }
+
+ return bridge->funcs->get_next_bridge(bridge);
+}
+EXPORT_SYMBOL(drm_bridge_get_next);
+
/**
* DOC: bridge operations
*
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 1981d24a700d..f020e0c2a268 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -62,6 +62,26 @@ enum drm_bridge_attach_flags {
* struct drm_bridge_funcs - drm_bridge control functions
*/
struct drm_bridge_funcs {
+ /**
+ * @get_next_bridge:
+ *
+ * Return a pointer to the bridge connected at the output port of
+ * this bridge.
+ *
+ * Returns:
+ * * Pointer to a bridge connected to the output port of this bridge,
+ * with refcount incremented; call drm_bridge_put() when done
+ * * PTR_ERR(-ENODEV): this bridge has an output port where a next
+ * bridge needs to be present for video output,
+ * but the nextbridge is not currently
+ * available
+ * * NULL: this bridge does not have an output port where a next
+ * bridge
+ * is expected
+ * * Another negative error returned by the bridge func
+ */
+ struct drm_bridge *(*get_next_bridge)(struct drm_bridge *bridge);
+
/**
* @attach:
*
@@ -1021,6 +1041,11 @@ enum drm_bridge_ops {
* &drm_bridge_funcs->hdmi_clear_spd_infoframe callbacks.
*/
DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME = BIT(10),
+ /**
+ * @DRM_BRIDGE_GET_NEXT_BRIDGE: The bridge implements the
+ * &drm_bridge_funcs->get_next_bridge callback.
+ */
+ DRM_BRIDGE_OP_GET_NEXT_BRIDGE = BIT(11),
};
/**
@@ -1270,6 +1295,7 @@ void drm_bridge_remove(struct drm_bridge *bridge);
int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
struct drm_bridge *previous,
enum drm_bridge_attach_flags flags);
+struct drm_bridge *drm_bridge_get_next(struct drm_bridge *bridge);
#ifdef CONFIG_OF
struct drm_bridge *of_drm_find_and_get_bridge(struct device_node *np);
--
2.55.0
^ permalink raw reply [flat|nested] 25+ messages in thread* [PATCH RFC v2 18/24] drm/panel: implement .get_next_bridge
2026-10-01 12:42 [PATCH RFC v2 00/24] drm bridge hotplug Luca Ceresoli
` (16 preceding siblings ...)
2026-10-01 12:42 ` [PATCH RFC v2 17/24] drm/bridge: add drm_bridge_get_next() and supporting func Luca Ceresoli
@ 2026-10-01 12:42 ` Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 19/24] drm/bridge: display-connector: " Luca Ceresoli
` (5 subsequent siblings)
23 siblings, 0 replies; 25+ messages in thread
From: Luca Ceresoli @ 2026-10-01 12:42 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Inki Dae, Jagan Teki, Marek Szyprowski, Jessica Zhang,
Douglas Anderson, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, dri-devel,
linux-kernel, imx, linux-arm-kernel
This bridge never has a next bridge. Add get_next_callback func to expose
this.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/drm_panel.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c
index c00529bfb706..8a2a99cca683 100644
--- a/drivers/gpu/drm/drm_panel.c
+++ b/drivers/gpu/drm/drm_panel.c
@@ -77,6 +77,11 @@ static const struct drm_connector_funcs panel_bridge_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
+static struct drm_bridge *drm_panel_bridge_get_next_bridge(struct drm_bridge *bridge)
+{
+ return NULL;
+}
+
static int panel_bridge_attach(struct drm_bridge *bridge,
struct drm_encoder *encoder,
enum drm_bridge_attach_flags flags)
@@ -217,6 +222,7 @@ static void panel_bridge_debugfs_init(struct drm_bridge *bridge,
}
static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
+ .get_next_bridge = drm_panel_bridge_get_next_bridge,
.attach = panel_bridge_attach,
.detach = panel_bridge_detach,
.atomic_pre_enable = panel_bridge_atomic_pre_enable,
@@ -600,7 +606,7 @@ void drm_panel_add(struct drm_panel *panel)
mutex_unlock(&panel_lock);
panel->bridge.of_node = panel->dev->of_node;
- panel->bridge.ops = DRM_BRIDGE_OP_MODES;
+ panel->bridge.ops = DRM_BRIDGE_OP_MODES | DRM_BRIDGE_OP_GET_NEXT_BRIDGE;
panel->bridge.type = panel->connector_type;
panel->bridge.pre_enable_prev_first = panel->prepare_prev_first;
--
2.55.0
^ permalink raw reply [flat|nested] 25+ messages in thread* [PATCH RFC v2 19/24] drm/bridge: display-connector: implement .get_next_bridge
2026-10-01 12:42 [PATCH RFC v2 00/24] drm bridge hotplug Luca Ceresoli
` (17 preceding siblings ...)
2026-10-01 12:42 ` [PATCH RFC v2 18/24] drm/panel: implement .get_next_bridge Luca Ceresoli
@ 2026-10-01 12:42 ` Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 20/24] drm/bridge: ti-sn65dsi83: " Luca Ceresoli
` (4 subsequent siblings)
23 siblings, 0 replies; 25+ messages in thread
From: Luca Ceresoli @ 2026-10-01 12:42 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Inki Dae, Jagan Teki, Marek Szyprowski, Jessica Zhang,
Douglas Anderson, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, dri-devel,
linux-kernel, imx, linux-arm-kernel
This bridge never has a next bridge. Add get_next_callback func to expose
this.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/bridge/display-connector.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/gpu/drm/bridge/display-connector.c b/drivers/gpu/drm/bridge/display-connector.c
index eb3412ec30a0..8b16ad89c7f1 100644
--- a/drivers/gpu/drm/bridge/display-connector.c
+++ b/drivers/gpu/drm/bridge/display-connector.c
@@ -36,6 +36,11 @@ to_display_connector(struct drm_bridge *bridge)
return container_of(bridge, struct display_connector, bridge);
}
+static struct drm_bridge *display_connector_get_next_bridge(struct drm_bridge *bridge)
+{
+ return NULL;
+}
+
static int display_connector_attach(struct drm_bridge *bridge,
struct drm_encoder *encoder,
enum drm_bridge_attach_flags flags)
@@ -214,6 +219,7 @@ static u32 *display_connector_get_input_bus_fmts(struct drm_bridge *bridge,
}
static const struct drm_bridge_funcs display_connector_bridge_funcs = {
+ .get_next_bridge = display_connector_get_next_bridge,
.attach = display_connector_attach,
.destroy = display_connector_destroy,
.detect = display_connector_bridge_detect,
@@ -412,6 +418,7 @@ static int display_connector_probe(struct platform_device *pdev)
conn->bridge.of_node = pdev->dev.of_node;
+ conn->bridge.ops = DRM_BRIDGE_OP_GET_NEXT_BRIDGE;
if (conn->bridge.ddc)
conn->bridge.ops |= DRM_BRIDGE_OP_EDID
| DRM_BRIDGE_OP_DETECT;
--
2.55.0
^ permalink raw reply [flat|nested] 25+ messages in thread* [PATCH RFC v2 20/24] drm/bridge: ti-sn65dsi83: implement .get_next_bridge
2026-10-01 12:42 [PATCH RFC v2 00/24] drm bridge hotplug Luca Ceresoli
` (18 preceding siblings ...)
2026-10-01 12:42 ` [PATCH RFC v2 19/24] drm/bridge: display-connector: " Luca Ceresoli
@ 2026-10-01 12:42 ` Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 21/24] drm/bridge: ti-sn65dsi86: " Luca Ceresoli
` (3 subsequent siblings)
23 siblings, 0 replies; 25+ messages in thread
From: Luca Ceresoli @ 2026-10-01 12:42 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Inki Dae, Jagan Teki, Marek Szyprowski, Jessica Zhang,
Douglas Anderson, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, dri-devel,
linux-kernel, imx, linux-arm-kernel
Add get_next_callback func to expose the next bridge.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/bridge/ti-sn65dsi83.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c
index d32b80e7c374..05c6b1d6b9f1 100644
--- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c
+++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c
@@ -295,6 +295,13 @@ static struct sn65dsi83 *bridge_to_sn65dsi83(struct drm_bridge *bridge)
return container_of(bridge, struct sn65dsi83, bridge);
}
+static struct drm_bridge *sn65dsi83_get_next_bridge(struct drm_bridge *bridge)
+{
+ struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge);
+
+ return ctx->panel_bridge ?: ERR_PTR(-ENODEV);
+}
+
static int sn65dsi83_attach(struct drm_bridge *bridge,
struct drm_encoder *encoder,
enum drm_bridge_attach_flags flags)
@@ -796,6 +803,7 @@ sn65dsi83_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
}
static const struct drm_bridge_funcs sn65dsi83_funcs = {
+ .get_next_bridge = sn65dsi83_get_next_bridge,
.attach = sn65dsi83_attach,
.detach = sn65dsi83_detach,
.atomic_enable = sn65dsi83_atomic_enable,
@@ -1066,6 +1074,7 @@ static int sn65dsi83_probe(struct i2c_client *client)
ctx->bridge.of_node = dev->of_node;
ctx->bridge.pre_enable_prev_first = true;
ctx->bridge.type = DRM_MODE_CONNECTOR_LVDS;
+ ctx->bridge.ops = DRM_BRIDGE_OP_GET_NEXT_BRIDGE;
drm_bridge_add(&ctx->bridge);
ret = sn65dsi83_host_attach(ctx);
--
2.55.0
^ permalink raw reply [flat|nested] 25+ messages in thread* [PATCH RFC v2 21/24] drm/bridge: ti-sn65dsi86: implement .get_next_bridge
2026-10-01 12:42 [PATCH RFC v2 00/24] drm bridge hotplug Luca Ceresoli
` (19 preceding siblings ...)
2026-10-01 12:42 ` [PATCH RFC v2 20/24] drm/bridge: ti-sn65dsi83: " Luca Ceresoli
@ 2026-10-01 12:42 ` Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 22/24] drm/bridge: samsung-dsim: " Luca Ceresoli
` (2 subsequent siblings)
23 siblings, 0 replies; 25+ messages in thread
From: Luca Ceresoli @ 2026-10-01 12:42 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Inki Dae, Jagan Teki, Marek Szyprowski, Jessica Zhang,
Douglas Anderson, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, dri-devel,
linux-kernel, imx, linux-arm-kernel
Add get_next_callback func to expose the next bridge.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/bridge/ti-sn65dsi86.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
index 369c13183232..7196097f68aa 100644
--- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c
+++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
@@ -768,6 +768,13 @@ static int ti_sn_attach_host(struct auxiliary_device *adev, struct ti_sn65dsi86
return devm_mipi_dsi_attach(&adev->dev, dsi);
}
+static struct drm_bridge *sn65dsi86_get_next_bridge(struct drm_bridge *bridge)
+{
+ struct ti_sn65dsi86 *pdata = bridge_to_ti_sn65dsi86(bridge);
+
+ return pdata->next_bridge ?: ERR_PTR(-ENODEV);
+}
+
static int ti_sn_bridge_attach(struct drm_bridge *bridge,
struct drm_encoder *encoder,
enum drm_bridge_attach_flags flags)
@@ -1358,6 +1365,7 @@ static void ti_sn_bridge_hpd_disable(struct drm_bridge *bridge)
}
static const struct drm_bridge_funcs ti_sn_bridge_funcs = {
+ .get_next_bridge = sn65dsi86_get_next_bridge,
.attach = ti_sn_bridge_attach,
.detach = ti_sn_bridge_detach,
.mode_valid = ti_sn_bridge_mode_valid,
@@ -1496,8 +1504,9 @@ static int ti_sn_bridge_probe(struct auxiliary_device *adev,
pdata->bridge.type = pdata->next_bridge->type == DRM_MODE_CONNECTOR_DisplayPort
? DRM_MODE_CONNECTOR_DisplayPort : DRM_MODE_CONNECTOR_eDP;
+ pdata->bridge.ops = DRM_BRIDGE_OP_GET_NEXT_BRIDGE;
if (pdata->bridge.type == DRM_MODE_CONNECTOR_DisplayPort) {
- pdata->bridge.ops = DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_DETECT;
+ pdata->bridge.ops |= DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_DETECT;
if (client->irq)
pdata->bridge.ops |= DRM_BRIDGE_OP_HPD;
/*
--
2.55.0
^ permalink raw reply [flat|nested] 25+ messages in thread* [PATCH RFC v2 22/24] drm/bridge: samsung-dsim: implement .get_next_bridge
2026-10-01 12:42 [PATCH RFC v2 00/24] drm bridge hotplug Luca Ceresoli
` (20 preceding siblings ...)
2026-10-01 12:42 ` [PATCH RFC v2 21/24] drm/bridge: ti-sn65dsi86: " Luca Ceresoli
@ 2026-10-01 12:42 ` Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 23/24] drm: drm_hotplug_helper: new helper to implement bridge hotplug Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 24/24] drm/mxsfb/lcdif: enable " Luca Ceresoli
23 siblings, 0 replies; 25+ messages in thread
From: Luca Ceresoli @ 2026-10-01 12:42 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Inki Dae, Jagan Teki, Marek Szyprowski, Jessica Zhang,
Douglas Anderson, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, dri-devel,
linux-kernel, imx, linux-arm-kernel
Add get_next_callback func to expose the next bridge.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/bridge/samsung-dsim.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c
index 6c48404fd60a..2da3a13260b9 100644
--- a/drivers/gpu/drm/bridge/samsung-dsim.c
+++ b/drivers/gpu/drm/bridge/samsung-dsim.c
@@ -1821,6 +1821,13 @@ static void samsung_dsim_mode_set(struct drm_bridge *bridge,
drm_mode_copy(&dsi->mode, adjusted_mode);
}
+static struct drm_bridge *samsung_dsim_get_next_bridge(struct drm_bridge *bridge)
+{
+ struct samsung_dsim *dsi = bridge_to_dsi(bridge);
+
+ return dsi->bridge.next_bridge ?: ERR_PTR(-ENODEV);
+}
+
static int samsung_dsim_attach(struct drm_bridge *bridge,
struct drm_encoder *encoder,
enum drm_bridge_attach_flags flags)
@@ -1835,6 +1842,7 @@ static int samsung_dsim_attach(struct drm_bridge *bridge,
}
static const struct drm_bridge_funcs samsung_dsim_bridge_funcs = {
+ .get_next_bridge = samsung_dsim_get_next_bridge,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_create_state = drm_atomic_helper_bridge_create_state,
@@ -2200,6 +2208,7 @@ int samsung_dsim_probe(struct platform_device *pdev)
dsi->bridge.of_node = dev->of_node;
dsi->bridge.type = DRM_MODE_CONNECTOR_DSI;
+ dsi->bridge.ops = DRM_BRIDGE_OP_GET_NEXT_BRIDGE;
/* DE_LOW: i.MX8M Mini/Nano LCDIF-DSIM glue logic inverts HS/VS/DE */
if (dsi->plat_data->hw_type == DSIM_TYPE_IMX8MM)
--
2.55.0
^ permalink raw reply [flat|nested] 25+ messages in thread* [PATCH RFC v2 23/24] drm: drm_hotplug_helper: new helper to implement bridge hotplug
2026-10-01 12:42 [PATCH RFC v2 00/24] drm bridge hotplug Luca Ceresoli
` (21 preceding siblings ...)
2026-10-01 12:42 ` [PATCH RFC v2 22/24] drm/bridge: samsung-dsim: " Luca Ceresoli
@ 2026-10-01 12:42 ` Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 24/24] drm/mxsfb/lcdif: enable " Luca Ceresoli
23 siblings, 0 replies; 25+ messages in thread
From: Luca Ceresoli @ 2026-10-01 12:42 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Inki Dae, Jagan Teki, Marek Szyprowski, Jessica Zhang,
Douglas Anderson, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, dri-devel,
linux-kernel, imx, linux-arm-kernel
Add a new helper to support cards implementing hotpluggable drm_bridges.
drmm_hotplug_helper_init() registers to get notified of relevant events and
react by creating a bridge (if the pipeline is complete in the hardware)
and destroying it on bridge removal.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
MAINTAINERS | 8 +
drivers/gpu/drm/display/Kconfig | 6 +
drivers/gpu/drm/display/Makefile | 2 +
drivers/gpu/drm/display/drm_hotplug_helper.c | 243 +++++++++++++++++++++++++++
include/drm/drm_hotplug_helper.h | 13 ++
5 files changed, 272 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index c5ae9f2f408a..611790c175c4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8019,6 +8019,14 @@ F: Documentation/devicetree/bindings/npu/rockchip,rk3588-rknn-core.yaml
F: drivers/accel/rocket/
F: include/uapi/drm/rocket_accel.h
+DRM BRIDGE HOTPLUG HELPER
+M: Luca Ceresoli <luca.ceresoli@bootlin.com>
+S: Maintained
+T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
+F: Documentation/devicetree/bindings/display/bridge/
+F: drivers/gpu/drm/display/drm_hotplug_helper.c
+F: include/drm/drm_hotplug_helper.h
+
DRM COMPUTE ACCELERATORS DRIVERS AND FRAMEWORK
M: Oded Gabbay <ogabbay@kernel.org>
L: dri-devel@lists.freedesktop.org
diff --git a/drivers/gpu/drm/display/Kconfig b/drivers/gpu/drm/display/Kconfig
index df09cf9a8ca1..f1a6ffcd4c63 100644
--- a/drivers/gpu/drm/display/Kconfig
+++ b/drivers/gpu/drm/display/Kconfig
@@ -22,6 +22,12 @@ config DRM_BRIDGE_CONNECTOR
help
DRM connector implementation terminating DRM bridge chains.
+config DRM_HOTPLUG_HELPER
+ bool
+ select DRM_BRIDGE_CONNECTOR
+ help
+ Helper code to implement a card supporting hotpluggable bridges.
+
config DRM_DISPLAY_DP_AUX_CEC
bool "Enable DisplayPort CEC-Tunneling-over-AUX HDMI support"
select DRM_DISPLAY_DP_HELPER
diff --git a/drivers/gpu/drm/display/Makefile b/drivers/gpu/drm/display/Makefile
index 0ff4a1ad0222..ce6dbd65833b 100644
--- a/drivers/gpu/drm/display/Makefile
+++ b/drivers/gpu/drm/display/Makefile
@@ -5,6 +5,8 @@ obj-$(CONFIG_DRM_DISPLAY_DP_AUX_BUS) += drm_dp_aux_bus.o
drm_display_helper-y := drm_display_helper_mod.o
drm_display_helper-$(CONFIG_DRM_BRIDGE_CONNECTOR) += \
drm_bridge_connector.o
+drm_display_helper-$(CONFIG_DRM_HOTPLUG_HELPER) += \
+ drm_hotplug_helper.o
drm_display_helper-$(CONFIG_DRM_DISPLAY_DP_HELPER) += \
drm_dp_dual_mode_helper.o \
drm_dp_helper.o \
diff --git a/drivers/gpu/drm/display/drm_hotplug_helper.c b/drivers/gpu/drm/display/drm_hotplug_helper.c
new file mode 100644
index 000000000000..1b89b14b52aa
--- /dev/null
+++ b/drivers/gpu/drm/display/drm_hotplug_helper.c
@@ -0,0 +1,243 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2026 GE HealthCare
+ * Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
+ */
+
+#include <drm/drm_bridge.h>
+#include <drm/drm_bridge_connector.h>
+#include <drm/drm_event_notifier.h>
+#include <drm/drm_hotplug_helper.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
+
+struct drm_hotplug_helper {
+ /**
+ * @drm: The DRM device we belong to
+ */
+ struct drm_device *drm;
+ /**
+ * @encoder:
+ *
+ * The encoder at the start of the bridges chain.
+ */
+ struct drm_encoder *encoder;
+ /**
+ * @drm_event_nb: notifier to receive DRM hotplug-related events
+ */
+ struct notifier_block drm_event_nb;
+ /**
+ * @connector: the drm_connector added/removed on plug/unplug
+ */
+ struct drm_connector *connector;
+ /**
+ * @connector_mutex: Protect @connector from concurrent creation and
+ * destruction
+ */
+ struct mutex connector_mutex;
+};
+
+static bool drm_hotplug_helper_pipeline_is_complete(struct drm_hotplug_helper *hotplug_helper)
+{
+ struct drm_bridge *last_bridge __free(drm_bridge_put) =
+ drm_bridge_chain_get_last_bridge(hotplug_helper->encoder);
+
+ /* We expect at least one bridge */
+ if (!last_bridge) {
+ drm_dbg_driver(hotplug_helper->drm, "no bridges in pipeline (yet)\n");
+ return false;
+ }
+
+ struct drm_bridge *next_bridge __free(drm_bridge_put) =
+ drm_bridge_get_next(last_bridge);
+
+ /* No next bridge expected, pipeline is complete */
+ if (!next_bridge) {
+ drm_dbg_driver(hotplug_helper->drm, "pipeline complete\n");
+ return true;
+ }
+
+ /* Next bridge expected but not there now, pipeline incomplete */
+ if (next_bridge == ERR_PTR(-ENODEV)) {
+ drm_dbg_driver(hotplug_helper->drm, "pipeline not (yet) complete\n");
+ return false;
+ }
+
+ /* Unexpected error */
+ if (IS_ERR(next_bridge))
+ drm_warn(hotplug_helper->drm, "%s error %pe\n", __func__, next_bridge);
+
+ /* next_bridge is valid, but not (yet|anymore) in chain */
+ return false;
+}
+
+/**
+ * drm_hotplug_helper_connector_add - add the drm_connector
+ * @hotplug_helper: drm_hotplug_helper to add the drm_connector to
+ *
+ * Returns 0 on success or a negative error otherwise.
+ */
+static int drm_hotplug_helper_connector_add(struct drm_hotplug_helper *hotplug_helper)
+{
+ struct drm_connector *connector;
+
+ guard(mutex)(&hotplug_helper->connector_mutex);
+
+ if (drm_WARN_ON(hotplug_helper->drm, hotplug_helper->connector))
+ return -EBUSY;
+
+ connector = drm_bridge_connector_dynamic_add(hotplug_helper->drm,
+ hotplug_helper->encoder);
+ if (IS_ERR(connector))
+ return PTR_ERR(connector);
+
+ hotplug_helper->connector = connector;
+
+ return 0;
+}
+
+static void drm_hotplug_helper_connector_remove(struct drm_hotplug_helper *hotplug_helper)
+{
+ guard(mutex)(&hotplug_helper->connector_mutex);
+
+ if (drm_WARN_ON(hotplug_helper->drm, !hotplug_helper->connector))
+ return;
+
+ drm_bridge_connector_dynamic_remove(hotplug_helper->connector);
+ hotplug_helper->connector = NULL;
+}
+
+/*
+ * Propagate the attach chain and possibly add a drm_bridge_connector after
+ * a new drm_bridge is hot-plugged.
+ *
+ * The connector is added only if the pipeline is now complete. This could
+ * not be the case for various reasons:
+ *
+ * - the new bridge is just unrelated to our encoder
+ * - the new bridge is not be the next one in the pipeline
+ * - the new bridge is the next in the pipeline but the pipeline is not yet
+ * complete
+ *
+ * All these cases are normal, not an error.
+ */
+static void drm_hotplug_helper_try_complete(struct drm_hotplug_helper *hotplug_helper)
+{
+ int err;
+
+ /*
+ * drm_connector already present, the new bridge must be for
+ * another card
+ */
+ if (hotplug_helper->connector)
+ return;
+
+ /* Propagate the attach call chain to newly hotplugged bridge(s) */
+ struct drm_bridge *last_bridge __free(drm_bridge_put) =
+ drm_bridge_chain_get_last_bridge(hotplug_helper->encoder);
+ err = last_bridge->funcs->attach(last_bridge, hotplug_helper->encoder,
+ DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+ if (err)
+ return;
+
+ /* Add the connector if the pipeline is now complete */
+ if (drm_hotplug_helper_pipeline_is_complete(hotplug_helper))
+ drm_hotplug_helper_connector_add(hotplug_helper);
+}
+
+static int drm_hotplug_helper_handle_event(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct drm_hotplug_helper *hotplug_helper =
+ container_of(nb, struct drm_hotplug_helper, drm_event_nb);
+
+ switch (event) {
+ case DRM_MIPI_DSI_ATTACHED:
+ /* One or more bridges hot-plugged, try adding the drm_bridge_connector */
+ drm_hotplug_helper_try_complete(hotplug_helper);
+ break;
+ case DRM_BRIDGE_DETACHED:
+ {
+ /*
+ * A bridge was unplugged, remove the drm_bridge_connector
+ * if it's part of the same pipeline
+ */
+ struct drm_bridge *bridge = (struct drm_bridge *)data;
+
+ if (hotplug_helper->connector &&
+ bridge->encoder == hotplug_helper->encoder)
+ drm_hotplug_helper_connector_remove(hotplug_helper);
+ break;
+ }
+ default:
+ }
+
+ return NOTIFY_DONE;
+}
+
+static void drm_hotplug_helper_fini(struct drm_device *dev, void *res)
+{
+ struct drm_hotplug_helper *hotplug_helper = (struct drm_hotplug_helper *)res;
+
+ drm_hotplug_helper_connector_remove(hotplug_helper);
+}
+
+static void drm_hotplug_helper_notifier_unregister(struct drm_device *dev, void *res)
+{
+ struct notifier_block *nb = (struct notifier_block *)res;
+
+ drm_event_notifier_unregister(nb);
+}
+
+/**
+ * drmm_hotplug_helper_init - Initialise the hotplug helper for an encoder
+ * @drm: the DRM device
+ * @encoder: the encoder where the bridge chain starts
+ *
+ * Register to receive hotplug-related events and react to them:
+ * - when a new bridge appears, check if the pipeline is now complete in
+ * the hardware, and if it is add a drm_bridge_connector which will add a
+ * drm_connector
+ * - when a bridge dispears, remove the drm_bridge_connector which will
+ * remove the drm_connector
+ *
+ * Returns a pointer to the new &drm_hotplug_helper on success, or a
+ * negative error pointer otherwise.
+ */
+struct drm_hotplug_helper *drmm_hotplug_helper_init(struct drm_device *drm,
+ struct drm_encoder *encoder)
+{
+ struct drm_hotplug_helper *hotplug_helper;
+ int ret;
+
+ hotplug_helper = drmm_kzalloc(drm, sizeof(*hotplug_helper), GFP_KERNEL);
+ if (!hotplug_helper)
+ return ERR_PTR(-ENOMEM);
+
+ mutex_init(&hotplug_helper->connector_mutex);
+ hotplug_helper->drm = drm;
+ hotplug_helper->encoder = encoder;
+ hotplug_helper->drm_event_nb.notifier_call = drm_hotplug_helper_handle_event;
+
+ if (drm_hotplug_helper_pipeline_is_complete(hotplug_helper)) {
+ ret = drm_hotplug_helper_connector_add(hotplug_helper);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
+ ret = drmm_add_action_or_reset(drm, drm_hotplug_helper_fini, hotplug_helper);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = drm_event_notifier_register(&hotplug_helper->drm_event_nb);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = drmm_add_action_or_reset(drm, drm_hotplug_helper_notifier_unregister,
+ &hotplug_helper->drm_event_nb);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(drmm_hotplug_helper_init);
diff --git a/include/drm/drm_hotplug_helper.h b/include/drm/drm_hotplug_helper.h
new file mode 100644
index 000000000000..26779a0b6554
--- /dev/null
+++ b/include/drm/drm_hotplug_helper.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2026 GE HealthCare
+ * Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
+ */
+
+#ifndef __DRM_HOTPLUG_HELPER_H__
+#define __DRM_HOTPLUG_HELPER_H__
+
+struct drm_hotplug_helper *drmm_hotplug_helper_init(struct drm_device *drm,
+ struct drm_encoder *encoder);
+
+#endif /* __DRM_HOTPLUG_HELPER_H__ */
--
2.55.0
^ permalink raw reply [flat|nested] 25+ messages in thread* [PATCH RFC v2 24/24] drm/mxsfb/lcdif: enable bridge hotplug
2026-10-01 12:42 [PATCH RFC v2 00/24] drm bridge hotplug Luca Ceresoli
` (22 preceding siblings ...)
2026-10-01 12:42 ` [PATCH RFC v2 23/24] drm: drm_hotplug_helper: new helper to implement bridge hotplug Luca Ceresoli
@ 2026-10-01 12:42 ` Luca Ceresoli
23 siblings, 0 replies; 25+ messages in thread
From: Luca Ceresoli @ 2026-10-01 12:42 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
David Airlie, Simona Vetter, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Inki Dae, Jagan Teki, Marek Szyprowski, Jessica Zhang,
Douglas Anderson, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, dri-devel,
linux-kernel, imx, linux-arm-kernel
Switch to the new drm_hotplug_helper() to handle bridge hotplugging.
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/gpu/drm/mxsfb/Kconfig | 2 +-
drivers/gpu/drm/mxsfb/lcdif_drv.c | 12 ++++++------
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/mxsfb/Kconfig b/drivers/gpu/drm/mxsfb/Kconfig
index 31db7a824a93..ada9a9334ea4 100644
--- a/drivers/gpu/drm/mxsfb/Kconfig
+++ b/drivers/gpu/drm/mxsfb/Kconfig
@@ -34,7 +34,7 @@ config DRM_IMX_LCDIF
select DRM_PANEL
select DRM_PANEL_BRIDGE
select DRM_DISPLAY_HELPER
- select DRM_BRIDGE_CONNECTOR
+ select DRM_HOTPLUG_HELPER
help
Choose this option if you have an LCDIFv3 LCD controller.
Those devices are found in various i.MX SoC (i.MX8MP,
diff --git a/drivers/gpu/drm/mxsfb/lcdif_drv.c b/drivers/gpu/drm/mxsfb/lcdif_drv.c
index e2173c4d6fc2..3d8904a48d9a 100644
--- a/drivers/gpu/drm/mxsfb/lcdif_drv.c
+++ b/drivers/gpu/drm/mxsfb/lcdif_drv.c
@@ -18,7 +18,7 @@
#include <drm/clients/drm_client_setup.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
-#include <drm/drm_bridge_connector.h>
+#include <drm/drm_hotplug_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_encoder.h>
#include <drm/drm_fbdev_dma.h>
@@ -58,7 +58,7 @@ static int lcdif_attach_bridge(struct lcdif_drm_private *lcdif)
struct of_endpoint of_ep;
struct drm_bridge *bridge;
struct drm_encoder *encoder;
- struct drm_connector *connector;
+ struct drm_hotplug_helper *hotplug_helper;
int ret;
if (!of_device_is_available(remote))
@@ -94,10 +94,10 @@ static int lcdif_attach_bridge(struct lcdif_drm_private *lcdif)
"Failed to attach bridge for endpoint%u\n",
of_ep.id);
- connector = drm_bridge_connector_init(lcdif->drm, encoder);
- if (IS_ERR(connector))
- return dev_err_probe(dev, PTR_ERR(connector),
- "Failed to init bridge_connector for endpoint%u\n",
+ hotplug_helper = drmm_hotplug_helper_init(lcdif->drm, encoder);
+ if (IS_ERR(hotplug_helper))
+ return dev_err_probe(dev, PTR_ERR(hotplug_helper),
+ "Failed to init hotplug_helper for endpoint%u\n",
of_ep.id);
}
--
2.55.0
^ permalink raw reply [flat|nested] 25+ messages in thread