dri-devel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH RFC v2 00/24] drm bridge hotplug
@ 2026-10-01 12:42 Luca Ceresoli
  2026-10-01 12:42 ` [PATCH RFC v2 01/24] drm/connector: split drmm_connector_hdmi_init() in 3 parts Luca Ceresoli
                   ` (23 more replies)
  0 siblings, 24 replies; 41+ 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

Hello,

this series adds support for Linux-based devices with a DRM pipeline whose
final components, including one or more bridges, can be hot-plugged and
hot-unplugged

For more about the use case see the v1 cover letter:
https://lore.kernel.org/lkml/20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com/

For reviewers with limited time for review
==========================================

These are the patches with the core changes and likely needing review and
discussion (most relevant first):

 * 23: the new drm_hotplug_helper (the main patch)
 * 24: an example usage
 * 5-6: the main changes to the bridge-connector
 * 17-22: the new get_next_bridge bridge func

The following can be skipped at this iteration, changes have been requested
in the v1 discussion but not yet implemented, and these patches are not
crucial:

 * 10-12: still needs rewording
 * 13-16: need docs, still todo

Design
======

The drm_bridge_connector is nowadays the recommended way to implement DRM
connectors when a chain of bridges is used.

This series proposes a small helper (drm_hotplug_helper) which drivers can
use to make their encoder able to:

 * receive hotplug-relevant events
 * add a drm_bridge_connector when a new bridge is added making the
   pipeline complete in the hardware
 * remove the drm_bridge_connector when a bridge is removed
 
Series layout
=============

 A. Add a dynamic variant of drmm_connector_hdmi_init()
    (needed for the bridge-connector to allocate the connector dynamically)

      1 drm/connector: split drmm_connector_hdmi_init() in 3 parts
      2 drm/connector: add drm_connector_hdmi_dynamic_init()

 B. bridge-connector: use a dynamic drm_connector

      3 drm/display: bridge-connector: split code allocation from initialization
      4 drm/display: bridge-connector: hoist error management to common code
      5 drm/display: bridge-connector: use a dynamic connector
      6 drm/display: bridge-connector: add APIs to add/remove the connector dynamically

 C. Misc preparation work

      7 drm/bridge: samsung-dsim: move drm_bridge_add() call to probe
      8 drm/bridge: initialize chain_node list head on allocation
      9 drm/bridge: initialize chain_node list head on detach and attach errors

 D. drm_bridge: stop pipeline when a bridge is removed

     10 drm/encoder: add drm_encoder_cleanup_from()
     11 drm/atomic: move drm_atomic_helper_disable_all() and drm_atomic_helper_shutdown() from drm_atomic_helper to drm_atomic
     12 drm/bridge: shutdown and cleanup on bridge unplug

 E. Add notifier mechanism to let common code (the bridge-connector)
    take actions on hotplug events

     13 drm/mipi-dsi: turn DRM_MIPI_DSI into a tristate
     14 drm: event-notifier: add mechanism to notify about hotplug events
     15 drm/bridge: notify about detached bridges
     16 drm/mipi-dsi: notify about DSI attach

 F. Let bridges return their next bridge
    (allows to know when the pipeline is complete)

     17 drm/bridge: add drm_bridge_get_next() and supporting func
     18 drm/panel: implement .get_next_bridge
     19 drm/bridge: display-connector: implement .get_next_bridge
     20 drm/bridge: ti-sn65dsi83: implement .get_next_bridge
     21 drm/bridge: ti-sn65dsi86: implement .get_next_bridge
     22 drm/bridge: samsung-dsim: implement .get_next_bridge

 G. Implement bridge hotplug in bridge-connector, enable it in a driver

     23 drm: drm_hotplug_helper: new helper to implement bridge hotplug
     24 drm/mxsfb/lcdif: enable bridge hotplug

== Grand plan

This is part of the work to support hotplug of DRM bridges. The grand plan
was initially discussed in [0].

Here's the work breakdown (➜ marks the current series):

 1. … add refcounting to DRM bridges struct drm_bridge,
      based on devm_drm_bridge_alloc()
    A. ✔ add new alloc API and refcounting (v6.16)
    B. ✔ convert all bridge drivers to new API (v6.17)
    C. ✔ kunit tests (v6.17)
    D. ✔ add get/put to drm_bridge_add/remove() + attach/detach()
         and warn on old allocation pattern (v6.17)
    E. … add get/put on drm_bridge accessors
       1. ✔ drm_bridge_chain_get_first_bridge(), add cleanup action (v6.18)
       2. ✔ drm_bridge_get_prev_bridge() (v6.18)
       3. ✔ drm_bridge_get_next_bridge() (v6.19)
       4. ✔ drm_for_each_bridge_in_chain() (v6.19)
       5. ✔ drm_bridge_connector_init (v6.19)
       6. ✔ protect encoder bridge chain with a mutex (v7.2)
       7. ✔ of_drm_find_bridge
          a. ✔ add of_drm_get_bridge() (v7.0),
               convert basic direct users (v7.0-v7.1)
          b. ✔ convert direct of_drm_get_bridge() users, part 2 (v7.0)
          c. ✔ convert direct of_drm_get_bridge() users, part 3 (v7.0)
          d. ✔ convert direct of_drm_get_bridge() users, part 4 (v7.1-v7.2)
          e. ✔ bridge-only drm_of_find_panel_or_bridge() users (v7.2)
       8. … panel_bridge lifetime
          a. ✔ cleanup DRM_PANEL in bridge drivers (v7.4)
	  b. ✔ embed a drm_bridge in every drm_panel (v7.4)
	  c. … remove deprecated *_of_get_bridge(): non-OF drivers
	  d.   remove deprecated *_of_get_bridge(): OF drivers
       9. ✔ enforce drm_bridge_add before drm_bridge_attach (v6.19)
    F. ✔ debugfs improvements
       1. ✔ add top-level 'bridges' file (v6.16)
       2. ✔ show refcount and list lingering bridges (v6.19)
 2. ✔ handle gracefully atomic updates during bridge removal
    A. ✔ Add drm_bridge_enter/exit() to protect device resources (v7.0)
    B. ✔ Add drm_bridge_clear_and_put() (v7.1)
 3. … DSI host-device driver interaction
 4. ✔ removing the need for the "always-disconnected" connector
 5. ✔ Migrate i.MX LCDIF driver to bridge-connector (v7.2)
 6. ➜ DRM bridge hotplug
    A. ➜ Bridge hotplug management in the DRM core
       1. ✔ bridge-connector: attach encoder to the connector (v7.2)
       2. ➜ drm bridge hotplug
    B.   Device tree description

[0] https://lore.kernel.org/lkml/20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com/#t
   

---
Changes in v2:
- Rewrote: added new drm_hotplug_helper, dropped most changes to drm_bridge_connector
- Added get_next_bridge func, dropped is_tail func
- Rebased on drm-misc-next, which required a few reworks
- Fix dynconn mutex locking
- Fix sashiko-reported bugs
- Drop "drm/display: bridge-connector: store the drm_device pointer", not
  strongly needed, and adapt the remaining patches
- Simplify handle_hpd code types
- Lots of other smaller improvements
- Link to v1: https://patch.msgid.link/20260519-drm-bridge-hotplug-v1-0-45e2bdb3dfb4@bootlin.com

---
Luca Ceresoli (24):
      drm/connector: split drmm_connector_hdmi_init() in 3 parts
      drm/connector: add drm_connector_hdmi_dynamic_init()
      drm/display: bridge-connector: split code allocation from initialization
      drm/display: bridge-connector: hoist error management to common code
      drm/display: bridge-connector: use a dynamic connector
      drm/display: bridge-connector: add APIs to add/remove the connector dynamically
      drm/bridge: samsung-dsim: move drm_bridge_add() call to probe
      drm/bridge: initialize chain_node list head on allocation
      drm/bridge: initialize chain_node list head on detach and attach errors
      drm/encoder: add drm_encoder_cleanup_from()
      drm/atomic: move drm_atomic_helper_disable_all() and drm_atomic_helper_shutdown() from drm_atomic_helper to drm_atomic
      drm/bridge: shutdown and cleanup on bridge unplug
      drm/mipi-dsi: turn DRM_MIPI_DSI into a tristate
      drm: event-notifier: add mechanism to notify about hotplug events
      drm/bridge: notify about detached bridges
      drm/mipi-dsi: notify about DSI attach
      drm/bridge: add drm_bridge_get_next() and supporting func
      drm/panel: implement .get_next_bridge
      drm/bridge: display-connector: implement .get_next_bridge
      drm/bridge: ti-sn65dsi83: implement .get_next_bridge
      drm/bridge: ti-sn65dsi86: implement .get_next_bridge
      drm/bridge: samsung-dsim: implement .get_next_bridge
      drm: drm_hotplug_helper: new helper to implement bridge hotplug
      drm/mxsfb/lcdif: enable bridge hotplug

 MAINTAINERS                                    |   8 +
 drivers/gpu/drm/Kconfig                        |   7 +-
 drivers/gpu/drm/Makefile                       |   2 +
 drivers/gpu/drm/bridge/display-connector.c     |   7 +
 drivers/gpu/drm/bridge/samsung-dsim.c          |  24 ++-
 drivers/gpu/drm/bridge/ti-sn65dsi83.c          |   9 +
 drivers/gpu/drm/bridge/ti-sn65dsi86.c          |  11 +-
 drivers/gpu/drm/display/Kconfig                |   6 +
 drivers/gpu/drm/display/Makefile               |   2 +
 drivers/gpu/drm/display/drm_bridge_connector.c | 172 +++++++++++------
 drivers/gpu/drm/display/drm_hotplug_helper.c   | 243 +++++++++++++++++++++++++
 drivers/gpu/drm/drm_atomic.c                   | 115 ++++++++++++
 drivers/gpu/drm/drm_atomic_helper.c            |  76 +-------
 drivers/gpu/drm/drm_bridge.c                   |  45 ++++-
 drivers/gpu/drm/drm_connector.c                | 130 +++++++++----
 drivers/gpu/drm/drm_encoder.c                  |  38 ++++
 drivers/gpu/drm/drm_event_notifier.c           |  58 ++++++
 drivers/gpu/drm/drm_mipi_dsi.c                 |   3 +
 drivers/gpu/drm/drm_panel.c                    |   8 +-
 drivers/gpu/drm/mxsfb/Kconfig                  |   2 +-
 drivers/gpu/drm/mxsfb/lcdif_drv.c              |  12 +-
 include/drm/drm_atomic.h                       |   3 +
 include/drm/drm_bridge.h                       |  26 +++
 include/drm/drm_bridge_connector.h             |   4 +
 include/drm/drm_connector.h                    |   6 +
 include/drm/drm_encoder.h                      |   1 +
 include/drm/drm_event_notifier.h               |  38 ++++
 include/drm/drm_hotplug_helper.h               |  13 ++
 28 files changed, 894 insertions(+), 175 deletions(-)
---
base-commit: e051645b3b4be7c8f9f77596e62f554cd52b008b
change-id: 20260515-drm-bridge-hotplug-46265d3a2f85

Best regards,
--  
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [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; 41+ 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 related	[flat|nested] 41+ 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:48   ` sashiko-bot
  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, 1 reply; 41+ 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 related	[flat|nested] 41+ 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; 41+ 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 related	[flat|nested] 41+ 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; 41+ 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 related	[flat|nested] 41+ 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:56   ` sashiko-bot
  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, 1 reply; 41+ 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 related	[flat|nested] 41+ 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:56   ` sashiko-bot
  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, 1 reply; 41+ 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 related	[flat|nested] 41+ 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 13:01   ` sashiko-bot
  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, 1 reply; 41+ 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 related	[flat|nested] 41+ 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; 41+ 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 related	[flat|nested] 41+ 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; 41+ 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 related	[flat|nested] 41+ 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 13:05   ` sashiko-bot
  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, 1 reply; 41+ 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 related	[flat|nested] 41+ 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 13:03   ` sashiko-bot
  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, 1 reply; 41+ 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 related	[flat|nested] 41+ 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 13:14   ` sashiko-bot
  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, 1 reply; 41+ 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 related	[flat|nested] 41+ 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 13:17   ` sashiko-bot
  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, 1 reply; 41+ 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 related	[flat|nested] 41+ 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 13:12   ` sashiko-bot
  2026-10-01 12:42 ` [PATCH RFC v2 15/24] drm/bridge: notify about detached bridges Luca Ceresoli
                   ` (9 subsequent siblings)
  23 siblings, 1 reply; 41+ 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 related	[flat|nested] 41+ 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; 41+ 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 related	[flat|nested] 41+ 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 13:15   ` sashiko-bot
  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, 1 reply; 41+ 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 related	[flat|nested] 41+ 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 13:20   ` sashiko-bot
  2026-10-01 12:42 ` [PATCH RFC v2 18/24] drm/panel: implement .get_next_bridge Luca Ceresoli
                   ` (6 subsequent siblings)
  23 siblings, 1 reply; 41+ 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 related	[flat|nested] 41+ 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; 41+ 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 related	[flat|nested] 41+ 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; 41+ 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 related	[flat|nested] 41+ 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 13:23   ` sashiko-bot
  2026-10-01 12:42 ` [PATCH RFC v2 21/24] drm/bridge: ti-sn65dsi86: " Luca Ceresoli
                   ` (3 subsequent siblings)
  23 siblings, 1 reply; 41+ 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 related	[flat|nested] 41+ 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 13:25   ` sashiko-bot
  2026-10-01 12:42 ` [PATCH RFC v2 22/24] drm/bridge: samsung-dsim: " Luca Ceresoli
                   ` (2 subsequent siblings)
  23 siblings, 1 reply; 41+ 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 related	[flat|nested] 41+ 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 13:27   ` sashiko-bot
  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, 1 reply; 41+ 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 related	[flat|nested] 41+ 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 13:31   ` sashiko-bot
  2026-10-01 12:42 ` [PATCH RFC v2 24/24] drm/mxsfb/lcdif: enable " Luca Ceresoli
  23 siblings, 1 reply; 41+ 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 related	[flat|nested] 41+ 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
  2026-10-01 13:31   ` sashiko-bot
  23 siblings, 1 reply; 41+ 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 related	[flat|nested] 41+ messages in thread

* Re: [PATCH RFC v2 02/24] drm/connector: add drm_connector_hdmi_dynamic_init()
  2026-10-01 12:42 ` [PATCH RFC v2 02/24] drm/connector: add drm_connector_hdmi_dynamic_init() Luca Ceresoli
@ 2026-10-01 12:48   ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-10-01 12:48 UTC (permalink / raw)
  To: Luca Ceresoli; +Cc: imx, Frank.Li, dri-devel

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] drm/connector: resource leak on dynamic HDMI init failure due to dead code
- [Low] drm/connector: incorrect kernel-doc function name for drm_connector_hdmi_dynamic_init
--

commit 7682dffb42f83d7a2155ddd8d9ec5ae0bcb7425d
Author: Luca Ceresoli <luca.ceresoli@bootlin.com>

drm/connector: add drm_connector_hdmi_dynamic_init()

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.

> diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
> index f5bd08640d251..f264cab0d184e 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

[Severity: Low]
This isn't a bug, but the kernel-doc header lists the function name as
drmm_connector_hdmi_init instead of drm_connector_hdmi_dynamic_init. Could
this cause kernel-doc validation warnings?

> + * @dev: DRM device
> + * @connector: A pointer to the HDMI connector to init

[ ... ]

> +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);

[Severity: Medium]
Is this second if (ret) check dead code?

Since ret would have caused an early return in the previous check, this
drm_connector_cleanup(connector) will never run. 

Does this leak resources (like the IDA allocation and connector name) if
drm_connector_hdmi_initialize() fails on the next line? Should the cleanup
instead happen on the return path if drm_connector_hdmi_initialize() 
encounters a failure?

> +
> +	return drm_connector_hdmi_initialize(dev, connector, hdmi_funcs);
> +}
> +EXPORT_SYMBOL(drm_connector_hdmi_dynamic_init);

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261001-drm-bridge-hotplug-v2-0-8e34986dcb68@bootlin.com?part=2

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH RFC v2 05/24] drm/display: bridge-connector: use a dynamic connector
  2026-10-01 12:42 ` [PATCH RFC v2 05/24] drm/display: bridge-connector: use a dynamic connector Luca Ceresoli
@ 2026-10-01 12:56   ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-10-01 12:56 UTC (permalink / raw)
  To: Luca Ceresoli; +Cc: dri-devel, Frank.Li, imx

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] drm: NULL pointer dereference due to inverted drmm cleanup order
- [Medium] drm: memory leak on connector initialization error paths
- [Medium] drm: missing IS_ERR check causes invalid pointer dereference
--

commit a2cbdd2f3a09967f7b7cc240d15ab5e45033083e
Author: Luca Ceresoli <luca.ceresoli@bootlin.com>

drm/display: bridge-connector: use a dynamic connector

This commit switches the DRM bridge connector initialization from
drmm-managed functions to dynamic variants. This prepares the driver for
bridge hotplugging, allowing connectors to be removed without removing the
entire card.

> diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
> index 2aff745f01478..1c222e27767d4 100644
> --- a/drivers/gpu/drm/display/drm_bridge_connector.c
> +++ b/drivers/gpu/drm/display/drm_bridge_connector.c
[ ... ]
> @@ -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);
> +

[Severity: Medium]
Does this code assign an error pointer directly to connector->state?

If drm_bridge_connector_create_state() returns ERR_PTR(-ENOMEM), this
will poison the state pointer without validation, potentially leading to an
invalid pointer dereference later.

>  	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);
> +

[Severity: Medium]
Does this code skip manual cleanup on this error path?

Since the switch to dynamic initialization, the connector is referenced and
has to be put if initialization fails (as noted in an earlier comment: "From
now on the connector is referenced and has to be put"). Returning
ERR_PTR(ret) directly skips drm_connector_put() and bridge cleanup,
leaking the initialized dynamic connector and bridge references.

This also seems to occur on earlier error paths in this function, such as
after drm_connector_hdmi_audio_init().

>  	return connector;
>  }
[ ... ]
> @@ -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);
> +

[Severity: High]
Does this inverted drmm cleanup order cause a NULL pointer dereference?

This drmm action is registered after the CEC drmm action (which happens
inside drm_bridge_connector_initialize()). Since drmm actions run in
LIFO order, the connector is destroyed and zeroed out before the CEC
adapter unregisters. The CEC unregister action would then dereference
connector->cec.data, which is already NULL, leading to a crash.

>  	return connector;
>  }

While reviewing this patch, I noticed a pre-existing issue in the HDMI
dynamic initialization path that this patch calls into:

drivers/gpu/drm/drm_connector.c:drm_connector_hdmi_dynamic_init()
{
	...
	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);
}

[Severity: Medium]
This is a pre-existing issue, but does this code contain a dead cleanup path?

The code evaluates if (ret) return ret; followed immediately by
if (ret) drm_connector_cleanup(connector);. The cleanup is dead code.
If the subsequent drm_connector_hdmi_initialize() fails, the successfully
initialized connector is leaked.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261001-drm-bridge-hotplug-v2-0-8e34986dcb68@bootlin.com?part=5

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [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 06/24] drm/display: bridge-connector: add APIs to add/remove the connector dynamically Luca Ceresoli
@ 2026-10-01 12:56   ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-10-01 12:56 UTC (permalink / raw)
  To: Luca Ceresoli; +Cc: Frank.Li, dri-devel, imx

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] drm/display: UAF in CEC unregister due to decoupled lifetimes
- [Medium] drm/display: memory leak in drm_bridge_connector_init() error paths
--

commit 53e1c33d3e43a19a4088655af78ef18e7e7c5200
Author: Luca Ceresoli <luca.ceresoli@bootlin.com>

drm/display: bridge-connector: add APIs to add/remove the connector dynamically

This commit changes bridge connector allocation to use standard non-managed
allocations (kzalloc_obj) instead of drmm_kzalloc(), and introduces
dynamic add/remove functions, to prepare for bridge hotplug.

> diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
> index 1c222e27767d4..2a0065208fb8a 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);
>  }

[Severity: High]
Can this cause a use-after-free during device teardown?

During initialization, the connector may register drmm managed cleanup
actions on the parent drm_device (such as calling
drmm_connector_hdmi_cec_notifier_register() inside
drm_bridge_connector_initialize()).

If a dynamic bridge connector is hot-removed, this function will call
kfree() on bridge_connector while those drmm actions are still queued on
the drm_device.

When the parent drm_device is later torn down, could those managed actions
execute and access the already freed connector memory?

[ ... ]

> @@ -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);
>  

[Severity: Medium]
Since bridge_connector is now allocated with kzalloc_obj() instead of
drmm_kzalloc(), does this leak bridge_connector if the subsequent call to
drm_bridge_connector_initialize() fails and returns an error pointer?

[ ... ]

> @@ -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);
> +}

[Severity: Medium]
Similarly here, if drm_bridge_connector_initialize() returns an error, will
it leak the bridge_connector allocation instead of freeing it?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261001-drm-bridge-hotplug-v2-0-8e34986dcb68@bootlin.com?part=6

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH RFC v2 07/24] drm/bridge: samsung-dsim: move drm_bridge_add() call to probe
  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 13:01   ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-10-01 13:01 UTC (permalink / raw)
  To: Luca Ceresoli; +Cc: Frank.Li, imx, dri-devel

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] drm/bridge: samsung-dsim: UAF in samsung_dsim_attach() due to data race
--

commit c28866fd6b4a2bff1ae13053f73c850e270af34a
Author: Luca Ceresoli <luca.ceresoli@bootlin.com>

drm/bridge: samsung-dsim: move drm_bridge_add() call to probe

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.

[ ... ]

> diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c
> index 480e568dd2c9d..b2de447dc6f43 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);
>  }

[Severity: High]
Could this lockless read of next_bridge introduce a use-after-free
regression due to a time-of-check to time-of-use race condition?

Since drm_bridge_add() is now called in probe(), the upstream encoder can
attach the bridge while the downstream panel is asynchronously probing.
This allows samsung_dsim_attach() to run concurrently with
samsung_dsim_host_detach().

If the panel concurrently unbinds or detaches, samsung_dsim_host_detach()
can clear the downstream bridge locklessly via:

    drm_bridge_clear_and_put(&dsi->bridge.next_bridge);

If this concurrent clearing happens between the check here and the
subsequent internal drm_bridge_get() call within drm_bridge_attach(),
could the kernel execute a kref_get() on a freed bridge pointer?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261001-drm-bridge-hotplug-v2-0-8e34986dcb68@bootlin.com?part=7

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [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 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 13:03   ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-10-01 13:03 UTC (permalink / raw)
  To: Luca Ceresoli; +Cc: dri-devel, imx, Frank.Li

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] drm: outdated helper function names in drm_atomic documentation
--

commit 1f801641406daea3f9369bcd8c55bd29b23f3328
Author: Luca Ceresoli <luca.ceresoli@bootlin.com>

drm/atomic: move drm_atomic_helper_disable_all() and drm_atomic_helper_shutdown() from drm_atomic_helper to drm_atomic

This patch moves drm_atomic_helper_shutdown() and its callee
drm_atomic_helper_disable_all() from the drm_kms_helper module to the drm
module, renaming them to drm_atomic_shutdown() and drm_atomic_disable_all().
The original functions are kept as deprecated wrappers to avoid a module
dependency loop.

> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> index 9bb761eda59f4..ec7a0f369c8b0 100644
> --- a/drivers/gpu/drm/drm_atomic.c
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -2081,6 +2081,121 @@ int __drm_atomic_helper_set_config(struct drm_mode_set *set,

[ ... ]

> + * 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)

[Severity: Low]
Should this "See also" section refer to the newly renamed
drm_atomic_shutdown() instead of the deprecated
drm_atomic_helper_shutdown() wrapper?

[ ... ]

> +/**
> + * 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)

[Severity: Low]
Does this documentation need to be updated to mention it wraps
drm_atomic_disable_all() instead of the deprecated
drm_atomic_helper_disable_all() function?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261001-drm-bridge-hotplug-v2-0-8e34986dcb68@bootlin.com?part=11

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH RFC v2 10/24] drm/encoder: add drm_encoder_cleanup_from()
  2026-10-01 12:42 ` [PATCH RFC v2 10/24] drm/encoder: add drm_encoder_cleanup_from() Luca Ceresoli
@ 2026-10-01 13:05   ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-10-01 13:05 UTC (permalink / raw)
  To: Luca Ceresoli; +Cc: dri-devel, Frank.Li, imx

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] drm: list corruption in privobj_list during dynamic bridge detachment
--

commit 77c9ca5601fd825a1740e548bb2319aadc1fb84a
Author: Luca Ceresoli <luca.ceresoli@bootlin.com>

drm/encoder: add drm_encoder_cleanup_from()

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.

> diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c
> index 0d5dbed06db44..40ece477b3025 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));

[Severity: High]
Does this dynamically detach the bridge without acquiring the
connection_mutex?

If a hardware bridge unplug triggers drm_bridge_remove(), this calls:
drm_encoder_cleanup_from() -> drm_bridge_detach() ->
drm_atomic_private_obj_fini()

In drm_atomic_private_obj_fini(), it executes:
    list_del(&obj->head);
on dev->mode_config.privobj_list without holding connection_mutex or any
other list-protecting lock.

Can this create a fatal race condition with concurrent lockless readers?

Concurrent userspace DRM IOCTLs (like modeset or atomic commits) can invoke
drm_modeset_lock_all_ctx(), which iterates over privobj_list locklessly
using list_for_each_entry(). The lockless iteration would race with the
un-synchronized list_del() triggered here, potentially corrupting the list
traversal for the reader and causing a crash.

> +}
> +EXPORT_SYMBOL(drm_encoder_cleanup_from);
> +

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261001-drm-bridge-hotplug-v2-0-8e34986dcb68@bootlin.com?part=10

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH RFC v2 14/24] drm: event-notifier: add mechanism to notify about hotplug events
  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 13:12   ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-10-01 13:12 UTC (permalink / raw)
  To: Luca Ceresoli; +Cc: dri-devel, imx, Frank.Li

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] drm: wrapping EXPORT_SYMBOL_GPL functions with EXPORT_SYMBOL
- [Low] drm: kernel-doc mismatch for DRM_BRIDGE_DETACHED enum
--

commit dcfd2e822b2355745325f20ab78b53e5e57972ac
Author: Luca Ceresoli <luca.ceresoli@bootlin.com>

drm: event-notifier: add mechanism to notify about hotplug events

In preparation for supporting DRM bridge hotplug, this commit adds an event
notifier. It allows interested parties to be notified about events they need
to react to, such as bridge detach and MIPI device attach.

> diff --git a/drivers/gpu/drm/drm_event_notifier.c b/drivers/gpu/drm/drm_event_notifier.c
> index 0000000000000..76af4dd4cdb02
> --- /dev/null
> +++ b/drivers/gpu/drm/drm_event_notifier.c
[ ... ]
> +/**
> + * 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);

[Severity: Medium]
Does this create a GPL-bypass? The core API blocking_notifier_chain_register()
is exported strictly as EXPORT_SYMBOL_GPL, but wrapping it here and exporting
drm_event_notifier_register() via EXPORT_SYMBOL could allow proprietary modules
to access GPL-only core functionality.

[ ... ]
> +/**
> + * 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);

[Severity: Medium]
Similar to the register function, does wrapping blocking_notifier_chain_unregister()
with EXPORT_SYMBOL inadvertently allow proprietary modules to bypass the license
restriction?

[ ... ]
> +/**
> + * 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);

[Severity: Medium]
Is it intentional to export this via EXPORT_SYMBOL? Since
blocking_notifier_call_chain() is EXPORT_SYMBOL_GPL, this wrapper effectively
bypasses that restriction for loadable modules calling drm_event_notifier_notify().

> diff --git a/include/drm/drm_event_notifier.h b/include/drm/drm_event_notifier.h
> index 0000000000000..2457719d50fe1
> --- /dev/null
> +++ b/include/drm/drm_event_notifier.h
[ ... ]
> +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,

[Severity: Low]
This isn't a bug, but there is a kernel-doc mismatch here in
drm_event_notifier_event. The comment refers to @DRM_BRIDGE_NOTIFY_DETACHED,
but the actual enum value is DRM_BRIDGE_DETACHED. This will cause kernel-doc
parsing warnings.

> +};
> +
> +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_ */

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261001-drm-bridge-hotplug-v2-0-8e34986dcb68@bootlin.com?part=14

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH RFC v2 12/24] drm/bridge: shutdown and cleanup on bridge unplug
  2026-10-01 12:42 ` [PATCH RFC v2 12/24] drm/bridge: shutdown and cleanup on bridge unplug Luca Ceresoli
@ 2026-10-01 13:14   ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-10-01 13:14 UTC (permalink / raw)
  To: Luca Ceresoli; +Cc: imx, dri-devel, Frank.Li

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [Critical] drm/bridge: UAF from dangling encoder pointer in drm_bridge_remove()
- [Critical] drm/bridge: list corruption in drm_encoder_cleanup_from() on concurrent unplug
- [Critical] drm/bridge: data race on privobj_list during hot-unplug detachment
- [High] drm/bridge: overbroad shutdown via drm_atomic_shutdown() on unplug
- [High] drm/bridge: lockless TOCTOU race on encoder pointer in drm_bridge_remove()
--

commit d7e62834c6a405667b2b0039bc07bf8c9f4cd74d
Author: Luca Ceresoli <luca.ceresoli@bootlin.com>

drm/bridge: shutdown and cleanup on bridge unplug

This commit adds logic to shut down the pipeline and detach the removed
bridge (and subsequent ones) from the encoder chain when a bridge is
hot-unplugged. It modifies drm_bridge_remove() to invoke
drm_atomic_shutdown() and drm_encoder_cleanup_from() if the bridge is
attached to an encoder.

> diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
> index 3cf84f2da33ba..468ceed36eb9c 100644
> --- a/drivers/gpu/drm/drm_bridge.c
> +++ b/drivers/gpu/drm/drm_bridge.c
> @@ -507,9 +507,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) {

[Severity: Critical]
Is it possible for bridge->encoder to be a dangling pointer here?

When a display driver unbinds, drm_encoder_cleanup() calls drm_bridge_detach().
The detachment logic clears bridge->dev but fails to clear bridge->encoder:

drivers/gpu/drm/drm_bridge.c:drm_bridge_detach() {
	...
	bridge->dev = NULL;
	drm_bridge_put(bridge);
}

If the encoder memory is then freed, and the bridge driver later unbinds,
drm_bridge_remove() will observe the dangling bridge->encoder pointer and pass
it to drm_encoder_cleanup_from(), which immediately attempts to acquire the
freed encoder's bridge_chain_mutex.


[Severity: High]
Is this lockless read of bridge->encoder susceptible to a TOCTOU race?

If this races with drm_bridge_attach(), it can observe the newly assigned
bridge->encoder before drm_bridge_attach() links the bridge into the encoder
chain under bridge_chain_mutex.

drm_bridge_remove() would then call drm_encoder_cleanup_from(), which acquires
the mutex, sees the bridge is not yet in the list, and skips detachment. The
bridge is then linked into the chain by drm_bridge_attach(), while
drm_bridge_remove() proceeds to destroy bridge->hpd_mutex and drops its
refcount.

> +		drm_atomic_shutdown(bridge->dev);

[Severity: High]
Does this unconditionally disable all display pipelines on the DRM device during
a single bridge unplug?

Looking at drm_atomic_shutdown(), it invokes drm_atomic_disable_all() on the
entire device. For systems with multiple independent displays (e.g., an internal
eDP panel and an external hot-pluggable HDMI bridge), couldn't this blank the
internal display when the external one is unplugged?

> +		drm_encoder_cleanup_from(bridge->encoder, bridge);

[Severity: Critical]
Can this lead to list corruption on concurrent unplugs?

In drm_encoder_cleanup_from(), the code evaluates !list_empty(&bridge->chain_node)
to check if a bridge is attached:

drivers/gpu/drm/drm_encoder.c:drm_encoder_cleanup_from() {
	...
	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);
	...
}

If Thread A removes an upstream bridge, it moves downstream bridges to its local
stack tmplist. If Thread B concurrently removes one of these downstream bridges,
it acquires the mutex and evaluates !list_empty(). Since the node is in Thread
A's tmplist, it evaluates to true.

Thread B then executes list_for_each_entry_safe_from(), which expects to terminate
at &encoder->bridge_chain. Because the node is actually on Thread A's stack
list, the loop never reaches the encoder's list head, wrapping around the stack
LIST_HEAD and interpreting arbitrary memory as struct drm_bridge.


[Severity: Critical]
Does detaching a bridge here introduce a data race on privobj_list?

drm_encoder_cleanup_from() calls drm_bridge_detach(), which calls
drm_atomic_private_obj_fini(). This executes list_del(&obj->head) to remove the
bridge from dev->mode_config.privobj_list.

However, privobj_list is documented as invariant over the lifetime of the device
and doesn't need locks:

include/drm/drm_mode_config.h:struct drm_mode_config {
	...
	/**
	 * @privobj_list:
	 *
	 * List of private objects linked with &drm_private_obj.head. This is
	 * invariant over the lifetime of a device and hence doesn't need any
	 * locks.
	 */
	...
}

Modifying it dynamically on hot-unplug without protective locks (like RCU)
introduces a data race with concurrent atomic operations or mode settings that
iterate the list locklessly.

> +	}
> +
>  	mutex_lock(&bridge_lock);
>  	list_move_tail(&bridge->list, &bridge_lingering_list);
>  	mutex_unlock(&bridge_lock);

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261001-drm-bridge-hotplug-v2-0-8e34986dcb68@bootlin.com?part=12

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH RFC v2 16/24] drm/mipi-dsi: notify about DSI attach
  2026-10-01 12:42 ` [PATCH RFC v2 16/24] drm/mipi-dsi: notify about DSI attach Luca Ceresoli
@ 2026-10-01 13:15   ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-10-01 13:15 UTC (permalink / raw)
  To: Luca Ceresoli; +Cc: imx, Frank.Li, dri-devel

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] drm: commit message contradicts ATTACHED event semantics
--

commit 3106f1037d08f7d82066d0ccd2f170f5facd1bf0
Author: Luca Ceresoli <luca.ceresoli@bootlin.com>

drm/mipi-dsi: notify about DSI attach

This commit introduces an event notification when a DSI device is attached to
its host. This is added in preparation for DRM bridge hotplug support.

>     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.

[Severity: Medium]
Does this commit message contradict the ATTACHED event semantics used in
mipi_dsi_attach() in drivers/gpu/drm/drm_mipi_dsi.c?

The commit message explicitly claims the DRM_MIPI_DSI_ATTACHED event is
intended to trigger actions to "deconfigure the pipeline and unregister the
connector". However, since this event is emitted when a device is attached,
shouldn't it be used to configure the pipeline and register the connector
(which is what the hotplug helper actually does)?

The teardown path is structurally safe because error unwinding (via devres)
calls drm_bridge_remove(), which detaches the bridge from the encoder and
emits DRM_BRIDGE_DETACHED. The hotplug helper correctly listens to
DRM_BRIDGE_DETACHED to unregister the connector, meaning the lifecycle is
perfectly symmetrical without needing a dedicated DRM_MIPI_DSI_DETACHED
event.

Could the commit message be updated to reflect that this event triggers
configuration and registration rather than deconfiguration and
unregistration?

> diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
> index 3ac1dd5ad640a..eaa474da4a519 100644
> --- a/drivers/gpu/drm/drm_mipi_dsi.c
> +++ b/drivers/gpu/drm/drm_mipi_dsi.c
[ ... ]
> @@ -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;
>  }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261001-drm-bridge-hotplug-v2-0-8e34986dcb68@bootlin.com?part=16

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH RFC v2 13/24] drm/mipi-dsi: turn DRM_MIPI_DSI into a tristate
  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 13:17   ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-10-01 13:17 UTC (permalink / raw)
  To: Luca Ceresoli; +Cc: imx, dri-devel, Frank.Li

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] module: drm_mipi_dsi cannot be unloaded and leaks bus registration
- [Low] kbuild: Linker error due to IS_ENABLED(CONFIG_DRM_MIPI_DSI) when built as module
--

commit 33f0d16c5b1d57480468538aae543a48ddad02aa
Author: Luca Ceresoli <luca.ceresoli@bootlin.com>

drm/mipi-dsi: turn DRM_MIPI_DSI into a tristate

DRM_MIPI_DSI is currently a bool, but there's no reason to not be allowed
to build it as a loadable module.

> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index 65d46dfa72661..9bdd9110e754b 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

[Severity: Low]
Will this cause a linker error when CONFIG_DRM=y and CONFIG_DRM_MIPI_DSI=m?

Since IS_ENABLED(CONFIG_DRM_MIPI_DSI) is true for both 'y' and 'm',
built-in code will attempt to call exported symbols from the now-loadable
drm_mipi_dsi module, leading to undefined references during the build.
For example, in drivers/gpu/drm/drm_of.c:

drm_of_get_dsi_bus() {
    ...
    #if IS_ENABLED(CONFIG_DRM_MIPI_DSI)
    ...
    dsi_host = of_find_mipi_dsi_host_by_node(dsi_host_node);
    ...
}

[Severity: Medium]
Does the drm_mipi_dsi module need a module_exit() cleanup function now that
it can be built as a loadable module?

Currently, mipi_dsi_bus_init() in drivers/gpu/drm/drm_mipi_dsi.c registers
the bus during initialization, but there is no corresponding module_exit()
to unregister it:

static int __init mipi_dsi_bus_init(void)
{
	return bus_register(&mipi_dsi_bus_type);
}
postcore_initcall(mipi_dsi_bus_init);

If the module is forcefully unloaded, it will leave a dangling
mipi_dsi_bus_type registration in the driver core.

[Severity: Low]
This is a pre-existing issue, but does changing this to a tristate still
leave an unmet dependency warning when selected by boolean configs?

For example, config DRM_MSM_DSI in drivers/gpu/drm/msm/Kconfig is a bool
that selects DRM_MIPI_DSI:

config DRM_MSM_DSI
	bool "Enable DSI support in MSM DRM driver"
	depends on DRM_MSM
	depends on DRM_MSM_KMS
	select DRM_PANEL
	select DRM_MIPI_DSI

If CONFIG_DRM=m and CONFIG_DRM_MSM_DSI=y, this forces CONFIG_DRM_MIPI_DSI
to 'y', violating its 'depends on DRM' restriction.

>  	depends on DRM
>  
>  config DRM_KMS_HELPER

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261001-drm-bridge-hotplug-v2-0-8e34986dcb68@bootlin.com?part=13

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH RFC v2 17/24] drm/bridge: add drm_bridge_get_next() and supporting func
  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 13:20   ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-10-01 13:20 UTC (permalink / raw)
  To: Luca Ceresoli; +Cc: dri-devel, Frank.Li, imx

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Low] doc: get_next_bridge incorrectly specifies PTR_ERR instead of ERR_PTR
- [Low] kernel-doc: mismatch in enum drm_bridge_ops documentation for DRM_BRIDGE_OP_GET_NEXT_BRIDGE
- [Low] kernel-doc: missing @bridge parameter description in drm_bridge_get_next()
- [Low] typo: spelling errors in commit message and documentation
--

commit 3521ec7ceaf6e88e9c48a699f2fdf587e7cc4f93
Author: Luca Ceresoli <luca.ceresoli@bootlin.com>

drm/bridge: add drm_bridge_get_next() and supporting func

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

> 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

[Severity: Low]
This isn't a bug, but there's a typo ("followong") in the commit message.

> 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

[Severity: Low]
This isn't a bug, but there's a typo ("probed))") in the commit message.

> diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
> index c84129100d20a..97a142ef0348e 100644
> --- a/drivers/gpu/drm/drm_bridge.c
> +++ b/drivers/gpu/drm/drm_bridge.c
> @@ -706,6 +706,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.
> + *

[Severity: Low]
Could we add a description for the @bridge parameter in this kernel-doc
block? Omitting it causes a warning when building kernel documentation.

> + * 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)

[ ... ]

> diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> index 1981d24a700d2..f020e0c2a2684 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

[Severity: Low]
Does this documentation incorrectly specify PTR_ERR instead of ERR_PTR?
Following this kernel-doc would result in returning an integer as a pointer,
causing compilation errors.

> +	 *                     bridge needs to be present for video output,
> +	 *                     but the nextbridge is not currently

[Severity: Low]
This isn't a bug, but there's a typo ("nextbridge") here.

> +	 *                     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);
> +

[ ... ]

> @@ -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

[Severity: Low]
Does this documented name mismatch the actual enum value
DRM_BRIDGE_OP_GET_NEXT_BRIDGE? This mismatch causes an unresolved reference
during kernel-doc generation.

> +	 * &drm_bridge_funcs->get_next_bridge callback.
> +	 */
> +	DRM_BRIDGE_OP_GET_NEXT_BRIDGE = BIT(11),
>  };

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261001-drm-bridge-hotplug-v2-0-8e34986dcb68@bootlin.com?part=17

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH RFC v2 20/24] drm/bridge: ti-sn65dsi83: implement .get_next_bridge
  2026-10-01 12:42 ` [PATCH RFC v2 20/24] drm/bridge: ti-sn65dsi83: " Luca Ceresoli
@ 2026-10-01 13:23   ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-10-01 13:23 UTC (permalink / raw)
  To: Luca Ceresoli; +Cc: imx, dri-devel, Frank.Li

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] drm/bridge: ti-sn65dsi83: missing refcount increment in get_next_bridge
--

commit cea7189b53e4a71deaaf15ff6f56ed70a0d97448
Author: Luca Ceresoli <luca.ceresoli@bootlin.com>

drm/bridge: ti-sn65dsi83: implement .get_next_bridge

This commit adds the get_next_bridge callback to expose the next bridge in
the chain for the ti-sn65dsi83 driver.

> diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c
> index eb5b249cc43c7..76fc0ca8cd196 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);
> +}

[Severity: High]
Does this need to increment the refcount of the returned panel bridge?

The API contract for the get_next_bridge callback in include/drm/drm_bridge.h
states that it must return a bridge with its refcount incremented, as callers
are expected to invoke drm_bridge_put() when they are done.

Since ctx->panel_bridge is returned directly without calling drm_bridge_get(),
could a caller traversing the chain cause a refcount underflow? This could
potentially lead to a use-after-free of the panel bridge object when the
caller drops its reference.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261001-drm-bridge-hotplug-v2-0-8e34986dcb68@bootlin.com?part=20

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH RFC v2 21/24] drm/bridge: ti-sn65dsi86: implement .get_next_bridge
  2026-10-01 12:42 ` [PATCH RFC v2 21/24] drm/bridge: ti-sn65dsi86: " Luca Ceresoli
@ 2026-10-01 13:25   ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-10-01 13:25 UTC (permalink / raw)
  To: Luca Ceresoli; +Cc: Frank.Li, dri-devel, imx

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] drm/bridge: refcount underflow in sn65dsi86_get_next_bridge()
--

commit ed254bdda2bfa941d8761ef842227d71a55ce971
Author: Luca Ceresoli <luca.ceresoli@bootlin.com>

drm/bridge: ti-sn65dsi86: implement .get_next_bridge

This commit adds a get_next_callback function to expose the next bridge in the
ti-sn65dsi86 driver.

> diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
> index 48b83df9aed61..e981d98a39b96 100644
> --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c
> +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
> @@ -743,6 +743,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);

[Severity: High]
Does this callback need to call drm_bridge_get() on the next bridge before
returning it?

The get_next_bridge API expects the returned bridge to have an incremented
reference count. Callers of drm_bridge_get_next() will clean up the returned
bridge using drm_bridge_put(). Because this function returns the pointer
directly without acquiring a reference, the subsequent put operation will
decrement a reference that was never acquired.

Could this lead to a regression involving a use-after-free of the next_bridge
structure during pipeline initialization or hotplug events?

> +}
> +
[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261001-drm-bridge-hotplug-v2-0-8e34986dcb68@bootlin.com?part=21

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH RFC v2 22/24] drm/bridge: samsung-dsim: implement .get_next_bridge
  2026-10-01 12:42 ` [PATCH RFC v2 22/24] drm/bridge: samsung-dsim: " Luca Ceresoli
@ 2026-10-01 13:27   ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-10-01 13:27 UTC (permalink / raw)
  To: Luca Ceresoli; +Cc: dri-devel, Frank.Li, imx

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] drm/bridge: UAF in samsung_dsim_get_next_bridge() due to missing refcount increment
--

commit ff84bc6df44d9112a31ddd636aeba9815e9fc90e
Author: Luca Ceresoli <luca.ceresoli@bootlin.com>

drm/bridge: samsung-dsim: implement .get_next_bridge

This commit adds a get_next_callback function to expose the next bridge in the
samsung-dsim driver.

> diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c
> index b2de447dc6f43..de64d036b89bf 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);

[Severity: High]
Does samsung_dsim_get_next_bridge() in drivers/gpu/drm/bridge/samsung-dsim.c
need to increment the reference count of the returned bridge?

The DRM core API drm_bridge_funcs.get_next_bridge mandates that the returned
bridge pointer must have its reference count incremented (for example via
drm_bridge_get()), as the caller is expected to call drm_bridge_put() when
done.

By returning dsi->bridge.next_bridge directly without taking a new reference,
when callers (such as drm_hotplug_helper_pipeline_is_complete() introduced
later in this series) use drm_bridge_get_next() and automatically drop the
reference using __free(drm_bridge_put), they will drop the reference
originally owned by the samsung_dsim struct.

Later, when the driver tears down and tries to release its own reference via
drm_bridge_clear_and_put(), could this trigger a double-put or
use-after-free condition?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261001-drm-bridge-hotplug-v2-0-8e34986dcb68@bootlin.com?part=22

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH RFC v2 24/24] drm/mxsfb/lcdif: enable bridge hotplug
  2026-10-01 12:42 ` [PATCH RFC v2 24/24] drm/mxsfb/lcdif: enable " Luca Ceresoli
@ 2026-10-01 13:31   ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-10-01 13:31 UTC (permalink / raw)
  To: Luca Ceresoli; +Cc: imx, dri-devel, Frank.Li

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] drm: API violation via double execution of bridge .attach()
- [High] drm: race condition allows concurrent bridge attach execution
- [Medium] drm: spurious warning on teardown of incomplete pipeline
--

commit 58600c33214c844d55e169ec965ac93160d06726
Author: Luca Ceresoli <luca.ceresoli@bootlin.com>

drm/mxsfb/lcdif: enable bridge hotplug
    
Switch to the new drm_hotplug_helper() to handle bridge hotplugging.

> diff --git a/drivers/gpu/drm/mxsfb/lcdif_drv.c b/drivers/gpu/drm/mxsfb/lcdif_drv.c
> index e2173c4d6fc28..3d8904a48d9a9 100644
> --- a/drivers/gpu/drm/mxsfb/lcdif_drv.c
> +++ b/drivers/gpu/drm/mxsfb/lcdif_drv.c
[ ... ]
> @@ -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);

[Severity: Medium]
Will this cause a spurious warning during standard module unload or unbind if
the bridge pipeline is incomplete?

When the device is torn down, the drmm cleanup action
drm_hotplug_helper_fini() unconditionally calls
drm_hotplug_helper_connector_remove(). If the bridge pipeline never completed
(e.g., a downstream bridge was never plugged or probed),
hotplug_helper->connector remains NULL.

In drivers/gpu/drm/display/drm_hotplug_helper.c:
drm_hotplug_helper_connector_remove(), this evaluates to true and triggers a
warning:

	if (drm_WARN_ON(hotplug_helper->drm, !hotplug_helper->connector))
		return;

[Severity: High]
Does using this helper violate the DRM API contract by executing the bridge's
.attach() callback twice?

When a hotplug event occurs, the helper retrieves the last bridge currently in
the chain. Since this bridge is already in the chain, its .attach() callback
was already executed by the drm_bridge_attach() call made earlier in
lcdif_attach_bridge().

However, in drivers/gpu/drm/display/drm_hotplug_helper.c:
drm_hotplug_helper_try_complete(), the helper directly invokes attach again:

	err = last_bridge->funcs->attach(last_bridge, hotplug_helper->encoder,
					 DRM_BRIDGE_ATTACH_NO_CONNECTOR);

Can this lead to memory leaks, double list additions, or crashes in bridge
drivers that do not expect .attach() to be called multiple times?

[Severity: High]
Is there a race condition when processing concurrent hotplug events in the
helper?

In drivers/gpu/drm/display/drm_hotplug_helper.c:
drm_hotplug_helper_try_complete(), there is a state check without holding the
connector_mutex:

	if (hotplug_helper->connector)
		return;

Since hotplug notifier events can be dispatched concurrently via the blocking
notifier chain (which uses a read-write semaphore), two simultaneous events
can both see connector == NULL.

Both threads will then proceed to concurrently execute
last_bridge->funcs->attach(), causing data races. Furthermore, both threads
may proceed to drm_hotplug_helper_connector_add(), where one will hit the
drm_WARN_ON after acquiring the mutex.

> +		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);
>  	}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261001-drm-bridge-hotplug-v2-0-8e34986dcb68@bootlin.com?part=24

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH RFC v2 23/24] drm: drm_hotplug_helper: new helper to implement bridge hotplug
  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 13:31   ` sashiko-bot
  0 siblings, 0 replies; 41+ messages in thread
From: sashiko-bot @ 2026-10-01 13:31 UTC (permalink / raw)
  To: Luca Ceresoli; +Cc: Frank.Li, dri-devel, imx

Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [High] drm: NULL pointer dereferences of last_bridge and optional attach callback
- [High] drm: refcount underflow on bridge returned by drm_bridge_get_next()
- [Medium] drm: drmm_hotplug_helper_init returns NULL on success violating API contract
- [Medium] drm: data race on hotplug_helper->connector outside of connector_mutex
- [Medium] drm: duplicate invocation of attach callback bypassing drm_bridge_attach()
- [Medium] drm: TOCTOU race misses hotplug events due to late notifier registration
- [Low] drm: spurious drm_WARN_ON triggered on normal teardown of incomplete pipeline
--

commit 251bdf083a0bf54162b14a9598f43ad560284a46
Author: Luca Ceresoli <luca.ceresoli@bootlin.com>

drm: drm_hotplug_helper: new helper to implement bridge hotplug

This commit adds a new helper to support cards implementing hotpluggable
DRM bridges. It registers to get notified of relevant events and reacts
by creating a bridge if the pipeline is complete, and destroying it on
bridge removal.

> diff --git a/drivers/gpu/drm/display/drm_hotplug_helper.c b/drivers/gpu/drm/display/drm_hotplug_helper.c
> index 0000000000000..1b89b14b52aa2
> --- /dev/null
> +++ b/drivers/gpu/drm/display/drm_hotplug_helper.c

[ ... ]

> +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);

[Severity: High]
Does this code cause a refcount underflow? The pointer returned by
drm_bridge_get_next() directly returns the pointer from the callback without
incrementing its kref reference count.

When the function returns, drm_bridge_put() executes and decrements the
reference count. Since a reference was never acquired, this leads to an
underflow and a use-after-free vulnerability.

[ ... ]

> +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;

[Severity: Low]
Will this trigger a spurious kernel warning during normal device teardown?

drmm_hotplug_helper_init() registers drm_hotplug_helper_fini() to run
unconditionally on teardown, which calls this function. If a bridge was
never hotplugged or the pipeline was incomplete, hotplug_helper->connector
legitimately remains NULL.

[ ... ]

> +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;

[Severity: Medium]
Can this unprotected read lead to a data race on hotplug_helper->connector?

The connector pointer is documented as protected by the connector_mutex.
Since the DRM event notifier uses a blocking notifier chain, concurrent
events could locklessly observe the connector as NULL before the addition
path completes, potentially leading to duplicate bridge attach attempts.

> +
> +	/* 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);

[Severity: High]
Can this code cause a NULL pointer dereference?

drm_bridge_chain_get_last_bridge() returns NULL if the chain is empty,
which can occur if an unrelated global DRM_MIPI_DSI_ATTACHED event triggers
this on an empty pipeline. Unconditionally dereferencing last_bridge->funcs
would crash.

Additionally, the attach callback in struct drm_bridge_funcs is documented
as optional. Unconditionally dereferencing it will crash for bridges that
omit it.

[Severity: Medium]
Could this also lead to a duplicate invocation of the attach callback?

Because last_bridge is already in the bridge chain, it has already been
attached during initialization. Bypassing the standard drm_bridge_attach()
API and manually calling the internal attach callback on the same bridge
breaks driver state expectations, potentially causing memory leaks or
hardware state corruption.

[ ... ]

> +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);

[Severity: Medium]
Does the gap between evaluating pipeline completeness and registering for
notifications create a time-of-check to time-of-use race?

If a bridge is hotplugged in the exact window between the pipeline check
above and this notifier registration, the DRM_MIPI_DSI_ATTACHED event is
missed, leaving the pipeline permanently incomplete.

> +	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;

[Severity: Medium]
Does returning 0 violate the API contract?

The function signature and kernel-doc state it returns a struct
drm_hotplug_helper pointer on success, or an error pointer on failure.
The success path executes return 0, returning a NULL pointer. Callers
correctly checking IS_ERR() will assume success and any subsequent
dereference of the returned pointer will crash.

> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261001-drm-bridge-hotplug-v2-0-8e34986dcb68@bootlin.com?part=23

^ permalink raw reply	[flat|nested] 41+ messages in thread

end of thread, other threads:[~2026-10-01 13:31 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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:48   ` sashiko-bot
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 ` [PATCH RFC v2 04/24] drm/display: bridge-connector: hoist error management to common code Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 05/24] drm/display: bridge-connector: use a dynamic connector Luca Ceresoli
2026-10-01 12:56   ` sashiko-bot
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:56   ` sashiko-bot
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 13:01   ` sashiko-bot
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 ` [PATCH RFC v2 09/24] drm/bridge: initialize chain_node list head on detach and attach errors Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 10/24] drm/encoder: add drm_encoder_cleanup_from() Luca Ceresoli
2026-10-01 13:05   ` sashiko-bot
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 13:03   ` sashiko-bot
2026-10-01 12:42 ` [PATCH RFC v2 12/24] drm/bridge: shutdown and cleanup on bridge unplug Luca Ceresoli
2026-10-01 13:14   ` sashiko-bot
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 13:17   ` sashiko-bot
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 13:12   ` sashiko-bot
2026-10-01 12:42 ` [PATCH RFC v2 15/24] drm/bridge: notify about detached bridges Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 16/24] drm/mipi-dsi: notify about DSI attach Luca Ceresoli
2026-10-01 13:15   ` sashiko-bot
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 13:20   ` sashiko-bot
2026-10-01 12:42 ` [PATCH RFC v2 18/24] drm/panel: implement .get_next_bridge Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 19/24] drm/bridge: display-connector: " Luca Ceresoli
2026-10-01 12:42 ` [PATCH RFC v2 20/24] drm/bridge: ti-sn65dsi83: " Luca Ceresoli
2026-10-01 13:23   ` sashiko-bot
2026-10-01 12:42 ` [PATCH RFC v2 21/24] drm/bridge: ti-sn65dsi86: " Luca Ceresoli
2026-10-01 13:25   ` sashiko-bot
2026-10-01 12:42 ` [PATCH RFC v2 22/24] drm/bridge: samsung-dsim: " Luca Ceresoli
2026-10-01 13:27   ` sashiko-bot
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 13:31   ` sashiko-bot
2026-10-01 12:42 ` [PATCH RFC v2 24/24] drm/mxsfb/lcdif: enable " Luca Ceresoli
2026-10-01 13:31   ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox