Subject: [PATCH 00/37] drm bridge hotplug

From: Luca Ceresoli <[email-masked]>
Date: Tue, 19 May 2026 12:37:17 +0200

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.

TL;DR:

 * This new approach is totally different from the one proposed in the
   past [0], and the code is an almost completely rewrite
 * If you already know the use case, feel free to skip to the "Design"
   section below

Use case
========

This series targets as its first use case a professional product (GE SUNH)
that is composed of a "main" part, with the main SoC and able to work
autonomously with limited features, and an optional "add-on" that enables
more features by adding more hardware peripherals.

The hotplug connector has a MIPI DSI bus. The addon has a DSI-to-LVDS
bridge and an LVDS panel attached to it. Different addon models can have
different components. As a consequence, a DRM bridge must be added and
removed at runtime without tearing down the whole card. This is currently
not possible, and this series enables it.

DRM already supports pipelines whose display can be removed, but all the
components preceding it (all the display controller and any bridges) are
assumed to be fixed and cannot be plugged, removed or modified at
runtime. Additionally, dynamic drm_connectors are supported for DP MST.

This picture summarizes the DRM structure implemented by this series:

 .------------------------.
 |   DISPLAY CONTROLLER   |
 | .---------.   .------. |
 | | ENCODER |<--| CRTC | |
 | '---------'   '------' |
 '------|-----------------'
        |              MACHANICAL
        |DSI            HOTPLUG
        V              CONNECTOR
   .---------.        .--.    .-.        .---------.         .-------.
   | 0 to N  |        | _|   _| |        | 1 to N  |         |       |
   | BRIDGES |--DSI-->||_   |_  |--DSI-->| BRIDGES |--LVDS-->| PANEL |
   |         |        |  |    | |        |         |         |       |
   '---------'        '--'    '-'        '---------'         '-------'

 [--- fixed components --]  [----------- removable add-on -----------]

The video bus is MIPI DSI in the example and in the implementation provided
by this series, but the implementation is meant to allow generalization to
other video busses without native hotplug support, such as parallel video
and LVDS. Addons which terminate with a HDMI/DP connector are possible too.

== Design

The old approach [0] was based on a hotplug-bridge driver, connected
between the last fixed bridge and the first removable bridge.

That approach has been rejected, and this new approach is based on a
central place to handle DRM hotplug, not a specific driver.

The drm_bridge_connector is nowadays the recommended way to implement DRM
connectors when a chain of bridges is used. It takes care of adding the
drm_connector when the pipeline is composed by an arbitrarily long chain of
bridges, which it scans to properly implement the drm_connector
operations. 

As such the drm_bridge_connector looked like the ideal component to
implement DRM bridge hotplug. 

This series augments the drm_bridge_connector code to be able to create and
destroy the drm_connector reacting on hot(un)plug events.

== Series description

This series has many preparatory patches: to make the drm_bridge_connector
code allocate the drm_connector dynamically and to prepare various other
parts of the involved DRM code.

In current code the last bridge in the encoder chain is always assumed to
complete the pipeline. There is no way for a bridge to tell "I'm currently
the last bridge in the encoder chain, but I need an next bridge to
work". This series has some patches to allow such a behaviour:

 * bridges can now report whether they conclude the pipeline or not (I
   propose the "tail bridge" naming to tell this)
 * returning -EPROBE_DEFER on bridge attach is not considered a hard error
   anymore, it's normal when the next bridge is needed but not yet present

Compared to the hotplug-bridge approach, now bridge drivers need a bit of
adaptation in order to support that the following bridge is
hotpluggable. So there are a few changes to the samsung-dsim bridge driver,
which in the hardware I'm working on is the last fixed bridge.

Finally, as the current drm_bridge_connector API is unable to support
hotplug, a new API is added to enable the new features. This makes existing
drivers not affected by the changes. The last patch shows to changes needed
to an encoder driver to switch to the new API enabling hotplug support.

== Series layout

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

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

 2. bridge-connector: split the long drm_bridge_connector_init() in various
    parts in preparation to be called in different ways

     * drm/display: bridge-connector: rename variable for consistency
     * drm/display: bridge-connector: store the drm_device pointer
     * drm/display: bridge-connector: split code creating the connector to a subfunction
     * drm/display: bridge-connector: use a drm_bridge_connector internally, not a drm_connector
     * drm/display: bridge-connector: extract drm_bridge_connector_get_bridges()
     * drm/display: bridge-connector: return int from drm_bridge_connector_get_bridges()
     * drm/display: bridge-connector: extract drm_bridge_connector_init_hdmi_audio_cec()
     * drm/display: bridge-connector: return int from drm_bridge_connector_init_hdmi_audio_cec()
     * drm/display: bridge-connector: return int from drm_bridge_connector_add_connector()

 3. bridge-connector: create the drm_connector dynamically

     * drm/display: bridge-connector: hoist error management to common code
     * drm/display: bridge-connector: move drm_bridge_connector_put_bridges() definition eariler
     * drm/display: bridge-connector: add non-drmm variant of drm_bridge_connector_put_bridges()
     * drm/display: bridge-connector: allocate the connector dynamically
     * drm/display: bridge-connector: move per-connector fields to the dynamic connector
     * drm/display: bridge-connector: protect dynconn creation and destruction with a mutex

 4. samsung-dsim: prepare driver to work when the following bridge is hotpluggable

     * drm/bridge: samsung-dsim: remove the panel_bridge on host_detach
     * drm/bridge: samsung-dsim: move drm_bridge_add() call to probe
     * drm/bridge: samsung-dsim: attach: return -EPROBE_DEFER is next bridge not yet available

 5. Misc preparation work
 
     * drm/bridge: initialize chain_node list head on allocation
     * drm/bridge: initialize chain_node list head on detach and attach errors

 6. drm_bridge: stop pipeline when a bridge is removed

     * 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

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

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

 8. Allow common code to know when the pipeline is complete

     * drm/bridge: add drm_bridge_is_tail() to know whether a bridge completes the pipeline
     * drm/bridge: panel: implement .is_tail
     * drm/bridge: display-connector: implement .is_tail
     * drm/bridge: samsung-dsim: implement .is_tail
     * drm/bridge: ti-sn65dsi83: implement .is_tail

 9. Allow probing incomplete pipelines

     * drm/bridge: drm_bridge_attach(): don't fail on -EPROBE_DEFER

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

     * drm/display: bridge-connector: handle bridge hotplug
     * drm/mxsfb/lcdif: enable bridge hotplug

== Grand plan

This is part of the work to support hotplug of DRM bridges. The grand plan
was 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.   drm_of_find_panel_or_bridge, *_of_get_bridge
       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. … protect private_obj removal from list
    C. ✔ 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/[email-masked]/#t

== Dependencies

This series depends on:

 * "drm/atomic: drm_atomic_private_obj_fini: protect private_obj removal
    from list"
   - https://lore.kernel.org/lkml/20[email-masked]/
   - Reason: avoid sporadic deadlock

Signed-off-by: Luca Ceresoli <[email-masked]>
---
Luca Ceresoli (37):
      drm/connector: split drmm_connector_hdmi_init() in 3 parts
      drm/connector: add drm_connector_hdmi_dynamic_init()
      drm/display: bridge-connector: rename variable for consistency
      drm/display: bridge-connector: store the drm_device pointer
      drm/display: bridge-connector: split code creating the connector to a subfunction
      drm/display: bridge-connector: use a drm_bridge_connector internally, not a drm_connector
      drm/display: bridge-connector: extract drm_bridge_connector_get_bridges()
      drm/display: bridge-connector: return int from drm_bridge_connector_get_bridges()
      drm/display: bridge-connector: extract drm_bridge_connector_init_hdmi_audio_cec()
      drm/display: bridge-connector: return int from drm_bridge_connector_init_hdmi_audio_cec()
      drm/display: bridge-connector: return int from drm_bridge_connector_add_connector()
      drm/display: bridge-connector: hoist error management to common code
      drm/display: bridge-connector: move drm_bridge_connector_put_bridges() definition eariler
      drm/display: bridge-connector: add non-drmm variant of drm_bridge_connector_put_bridges()
      drm/display: bridge-connector: allocate the connector dynamically
      drm/display: bridge-connector: move per-connector fields to the dynamic connector
      drm/display: bridge-connector: protect dynconn creation and destruction with a mutex
      drm/bridge: samsung-dsim: remove the panel_bridge on host_detach
      drm/bridge: samsung-dsim: move drm_bridge_add() call to probe
      drm/bridge: samsung-dsim: attach: return -EPROBE_DEFER is next bridge not yet available
      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: event-notifier: add mechanism to notify about hotplug events
      drm/bridge: notify about detached bridges
      drm/mipi-dsi: turn DRM_MIPI_DSI into a tristate
      drm/mipi-dsi: notify about DSI attach
      drm/bridge: add drm_bridge_is_tail() to know whether a bridge completes the pipeline
      drm/bridge: panel: implement .is_tail
      drm/bridge: display-connector: implement .is_tail
      drm/bridge: samsung-dsim: implement .is_tail
      drm/bridge: ti-sn65dsi83: implement .is_tail
      drm/bridge: drm_bridge_attach(): don't fail on -EPROBE_DEFER
      drm/display: bridge-connector: handle bridge hotplug
      drm/mxsfb/lcdif: enable bridge hotplug

 drivers/gpu/drm/Kconfig                        |   7 +-
 drivers/gpu/drm/Makefile                       |   2 +
 drivers/gpu/drm/bridge/display-connector.c     |   7 +
 drivers/gpu/drm/bridge/panel.c                 |   8 +-
 drivers/gpu/drm/bridge/samsung-dsim.c          |  36 +-
 drivers/gpu/drm/bridge/ti-sn65dsi83.c          |   7 +
 drivers/gpu/drm/display/drm_bridge_connector.c | 835 +++++++++++++++++--------
 drivers/gpu/drm/drm_atomic.c                   | 115 ++++
 drivers/gpu/drm/drm_atomic_helper.c            |  76 +--
 drivers/gpu/drm/drm_bridge.c                   |  47 +-
 drivers/gpu/drm/drm_connector.c                | 155 +++--
 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/mxsfb/lcdif_drv.c              |   8 +-
 include/drm/bridge/samsung-dsim.h              |   2 +
 include/drm/drm_atomic.h                       |   3 +
 include/drm/drm_bridge.h                       |  19 +
 include/drm/drm_bridge_connector.h             |   2 +
 include/drm/drm_connector.h                    |   9 +
 include/drm/drm_encoder.h                      |   1 +
 include/drm/drm_event_notifier.h               |  36 ++
 22 files changed, 1078 insertions(+), 396 deletions(-)
---
base-commit: 289b9a790d52a6865db3006f07922a9191faa54e
change-id: 20260515-drm-bridge-hotplug-46265d3a2f85

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