[PATCH v2 15/28] drm/bridge: Handle bridges with hardware state readout
From: Maxime Ripard
Date: Thu Apr 23 2026 - 06:16:23 EST
Bridges use drm_private_obj for their atomic state, but their readout
needs special handling compared to other private objects.
Bridge drivers need the CRTC and connector states for their pipeline
to properly read out their own state. Since bridge registration does
not guarantee ordering, the readout must traverse each encoder's
bridge chain to ensure each bridge can query the state of preceding
bridges.
Add a drm_bridge_atomic_readout_priv_state() wrapper that looks up
the connector and CRTC states for the bridge's encoder, then forwards
to the bridge's atomic_sro_readout_state hook. Create a separate
drm_private_state_funcs vtable for bridges that support SRO so that
drm_atomic_sro_device_can_readout() can properly discriminate bridges
with and without SRO support.
Signed-off-by: Maxime Ripard <mripard@xxxxxxxxxx>
---
drivers/gpu/drm/drm_bridge.c | 81 +++++++++++++++++++++++++++++++++++++++++---
1 file changed, 77 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index fba440bddcb3..08226af6b82a 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -27,10 +27,11 @@
#include <linux/media-bus-format.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/srcu.h>
+#include <drm/drm_atomic_sro.h>
#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_debugfs.h>
#include <drm/drm_edid.h>
#include <drm/drm_encoder.h>
@@ -506,10 +507,66 @@ drm_bridge_atomic_create_priv_state(struct drm_private_obj *obj)
return ERR_CAST(state);
return &state->base;
}
+static struct drm_connector_state *
+find_connector_state_for_encoder(struct drm_atomic_sro_state *state,
+ struct drm_encoder *encoder)
+{
+ struct drm_connector_list_iter conn_iter;
+ struct drm_connector *connector;
+
+ drm_connector_list_iter_begin(drm_atomic_sro_state_get_device(state),
+ &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
+ struct drm_connector_state *conn_state =
+ drm_atomic_sro_get_connector_state(state, connector);
+
+ if (WARN_ON(!conn_state))
+ continue;
+
+ if (encoder == conn_state->best_encoder)
+ return conn_state;
+ }
+ drm_connector_list_iter_end(&conn_iter);
+
+ return NULL;
+}
+
+static int
+drm_bridge_atomic_readout_priv_state(struct drm_private_obj *obj,
+ struct drm_atomic_sro_state *state,
+ struct drm_private_state *priv_state)
+{
+ struct drm_bridge *bridge = drm_priv_to_bridge(obj);
+ struct drm_encoder *encoder = bridge->encoder;
+ const struct drm_bridge_funcs *bridge_funcs = bridge->funcs;
+ struct drm_crtc_state *crtc_state;
+ struct drm_bridge_state *bridge_state;
+ struct drm_connector_state *conn_state;
+ int ret;
+
+ conn_state = find_connector_state_for_encoder(state, encoder);
+ if (!conn_state)
+ return -EINVAL;
+
+ crtc_state = drm_atomic_sro_get_crtc_state(state, conn_state->crtc);
+ if (!crtc_state)
+ return -EINVAL;
+
+ bridge_state = drm_priv_to_bridge_state(priv_state);
+ if (bridge_funcs->atomic_sro_readout_state) {
+ ret = bridge_funcs->atomic_sro_readout_state(
+ bridge, state, bridge_state, crtc_state, conn_state);
+ if (WARN_ON(ret))
+ return ret;
+ }
+
+ return 0;
+}
+
static void
drm_bridge_atomic_print_priv_state(struct drm_printer *p,
const struct drm_private_state *s)
{
const struct drm_bridge_state *state = drm_priv_to_bridge_state(s);
@@ -533,10 +590,18 @@ static const struct drm_private_state_funcs drm_bridge_priv_state_funcs = {
.atomic_duplicate_state = drm_bridge_atomic_duplicate_priv_state,
.atomic_destroy_state = drm_bridge_atomic_destroy_priv_state,
.atomic_print_state = drm_bridge_atomic_print_priv_state,
};
+static const struct drm_private_state_funcs drm_bridge_priv_state_funcs_with_sro = {
+ .atomic_sro_readout_state = drm_bridge_atomic_readout_priv_state,
+ .atomic_create_state = drm_bridge_atomic_create_priv_state,
+ .atomic_duplicate_state = drm_bridge_atomic_duplicate_priv_state,
+ .atomic_destroy_state = drm_bridge_atomic_destroy_priv_state,
+ .atomic_print_state = drm_bridge_atomic_print_priv_state,
+};
+
/**
* drm_private_obj_is_bridge - check if a private object backs a bridge
* @obj: private object to check
*
* RETURNS:
@@ -544,11 +609,13 @@ static const struct drm_private_state_funcs drm_bridge_priv_state_funcs = {
* True if @obj is the &drm_private_obj embedded in a &struct drm_bridge,
* false otherwise.
*/
bool drm_private_obj_is_bridge(struct drm_private_obj *obj)
{
- return obj->funcs && obj->funcs == &drm_bridge_priv_state_funcs;
+ return obj->funcs &&
+ (obj->funcs == &drm_bridge_priv_state_funcs ||
+ obj->funcs == &drm_bridge_priv_state_funcs_with_sro);
}
EXPORT_SYMBOL(drm_private_obj_is_bridge);
static bool drm_bridge_is_atomic(struct drm_bridge *bridge)
{
@@ -622,13 +689,19 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
ret = bridge->funcs->attach(bridge, encoder, flags);
if (ret < 0)
goto err_reset_bridge;
}
- if (drm_bridge_is_atomic(bridge))
- drm_atomic_private_obj_init(bridge->dev, &bridge->base, bridge->name,
- &drm_bridge_priv_state_funcs);
+ if (drm_bridge_is_atomic(bridge)) {
+ if (bridge->funcs &&
+ bridge->funcs->atomic_sro_readout_state)
+ drm_atomic_private_obj_init(bridge->dev, &bridge->base, bridge->name,
+ &drm_bridge_priv_state_funcs_with_sro);
+ else
+ drm_atomic_private_obj_init(bridge->dev, &bridge->base, bridge->name,
+ &drm_bridge_priv_state_funcs);
+ }
return 0;
err_reset_bridge:
bridge->dev = NULL;
--
2.53.0