[PATCH v2 24/28] drm/bridge_connector: Implement hw readout for connector
From: Maxime Ripard
Date: Thu Apr 23 2026 - 06:28:52 EST
drm_bridge_connector allows to create a generic connector from a list of
bridges.
However, it's a somewhat virtual connector, and relies on the bridges to
implement its various capabilities.
What we actually want though is for the last bridge implementing
hardware readout to fill the connector state from its own state.
Thus, implement a new op for bridge_connector to allow just
that.
Signed-off-by: Maxime Ripard <mripard@xxxxxxxxxx>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 31 ++++++++++++++++++++++++++
include/drm/drm_bridge.h | 27 ++++++++++++++++++++++
2 files changed, 58 insertions(+)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index 4b310fe505b4..43f6de5c1295 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -8,10 +8,11 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/property.h>
#include <linux/slab.h>
+#include <drm/drm_atomic_sro_helper.h>
#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_bridge_connector.h>
#include <drm/drm_connector.h>
#include <drm/drm_device.h>
@@ -64,10 +65,18 @@ struct drm_bridge_connector {
* @encoder:
*
* The encoder at the start of the bridges chain.
*/
struct drm_encoder *encoder;
+ /**
+ * @bridge_connector_hw_readout:
+ *
+ * The last bridge in the chain (closest to the connector) that
+ * provides hardware state readout support, if any (see
+ * &DRM_BRIDGE_OP_CONNECTOR_HW_READOUT).
+ */
+ struct drm_bridge *bridge_connector_hw_readout;
/**
* @bridge_edid:
*
* The last bridge in the chain (closest to the connector) that provides
* EDID read support, if any (see &DRM_BRIDGE_OP_EDID).
@@ -281,15 +290,33 @@ drm_bridge_connector_create_state(struct drm_connector *connector)
conn_state);
return conn_state;
}
+static int
+drm_bridge_connector_readout_state(struct drm_connector *connector,
+ struct drm_atomic_sro_state *state,
+ struct drm_connector_state *conn_state)
+{
+ struct drm_bridge_connector *bridge_connector =
+ to_drm_bridge_connector(connector);
+ struct drm_bridge *readout =
+ bridge_connector->bridge_connector_hw_readout;
+
+ if (readout)
+ readout->funcs->atomic_sro_connector_readout(readout, state, conn_state);
+
+ return 0;
+}
+
static const struct drm_connector_funcs drm_bridge_connector_funcs = {
.detect = drm_bridge_connector_detect,
.force = drm_bridge_connector_force,
.fill_modes = drm_helper_probe_single_connector_modes,
.atomic_create_state = drm_bridge_connector_create_state,
+ .atomic_sro_readout_state = drm_bridge_connector_readout_state,
+ .atomic_sro_compare_state = drm_atomic_helper_connector_compare_state,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.debugfs_init = drm_bridge_connector_debugfs_init,
.oob_hotplug_event = drm_bridge_connector_oob_hotplug_event,
};
@@ -831,10 +858,14 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
if (!bridge->interlace_allowed)
connector->interlace_allowed = false;
if (!bridge->ycbcr_420_allowed)
connector->ycbcr_420_allowed = false;
+ if (bridge->ops & DRM_BRIDGE_OP_CONNECTOR_HW_READOUT) {
+ drm_bridge_put(bridge_connector->bridge_connector_hw_readout);
+ bridge_connector->bridge_connector_hw_readout = drm_bridge_get(bridge);
+ }
/*
* Ensure the last bridge declares OP_EDID or OP_MODES or both.
*/
if (bridge->ops & DRM_BRIDGE_OP_EDID || bridge->ops & DRM_BRIDGE_OP_MODES) {
drm_bridge_put(bridge_connector->bridge_edid);
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 36d558a5cd4d..17c863da5d6d 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -1006,10 +1006,30 @@ struct drm_bridge_funcs {
*/
int (*dp_audio_mute_stream)(struct drm_bridge *bridge,
struct drm_connector *connector,
bool enable, int direction);
+ /**
+ * @atomic_sro_connector_readout:
+ *
+ * This optional hook initializes the &struct drm_connector_state
+ * based on hardware state.
+ *
+ * It is implemented by bridges that set the
+ * %DRM_BRIDGE_OP_CONNECTOR_HW_READOUT flag in their
+ * &drm_bridge.ops. When using drm_bridge_connector, the last
+ * bridge in the chain with this flag set will have its hook
+ * called to fill the connector state.
+ *
+ * RETURNS:
+ *
+ * 0 on success, a negative error code otherwise.
+ */
+ int (*atomic_sro_connector_readout)(struct drm_bridge *bridge,
+ struct drm_atomic_sro_state *state,
+ struct drm_connector_state *conn_state);
+
/**
* @debugfs_init:
*
* Allows bridges to create bridge-specific debugfs files.
*/
@@ -1146,10 +1166,17 @@ enum drm_bridge_ops {
* @DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME: The bridge supports
* &drm_bridge_funcs->hdmi_write_spd_infoframe and
* &drm_bridge_funcs->hdmi_clear_spd_infoframe callbacks.
*/
DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME = BIT(10),
+ /**
+ * @DRM_BRIDGE_OP_CONNECTOR_HW_READOUT: The bridge supports the
+ * &drm_bridge_funcs.atomic_sro_connector_readout callback to
+ * fill the connector state from the bridge's own hardware state
+ * during state readout.
+ */
+ DRM_BRIDGE_OP_CONNECTOR_HW_READOUT = BIT(11),
};
/**
* struct drm_bridge - central DRM bridge control structure
*/
--
2.53.0