The display-connector bridge is the last bridge in the chain on
platforms using drm_bridge_connector, and is responsible for the
physical connector interface.

Implement the atomic_sro_readout_state hook and wire up the default
drm_atomic_helper_bridge_compare_state() as the compare hook.

Signed-off-by: Maxime Ripard <[email protected]>
---
 drivers/gpu/drm/bridge/display-connector.c | 64 ++++++++++++++++++++++++++++++
 1 file changed, 64 insertions(+)

diff --git a/drivers/gpu/drm/bridge/display-connector.c 
b/drivers/gpu/drm/bridge/display-connector.c
index eb3412ec30a0..7f285b7c1e84 100644
--- a/drivers/gpu/drm/bridge/display-connector.c
+++ b/drivers/gpu/drm/bridge/display-connector.c
@@ -12,11 +12,14 @@
 #include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/regulator/consumer.h>
 #include <linux/workqueue.h>
 
+#include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_atomic_sro.h>
+#include <drm/drm_atomic_sro_helper.h>
 #include <drm/drm_bridge.h>
 #include <drm/drm_edid.h>
 
 struct display_connector {
        struct drm_bridge       bridge;
@@ -211,10 +214,69 @@ static u32 *display_connector_get_input_bus_fmts(struct 
drm_bridge *bridge,
        return prev_bridge->funcs->atomic_get_input_bus_fmts(prev_bridge, 
prev_bridge_state,
                                                             crtc_state, 
conn_state, output_fmt,
                                                             num_input_fmts);
 }
 
+/*
+ * Reconstruct the bridge bus configuration from the previous bridge's
+ * readout state and the connector's display_info.
+ *
+ * During normal atomic check, drm_atomic_bridge_chain_select_bus_fmts()
+ * and drm_atomic_bridge_propagate_bus_flags() walk the bridge chain to
+ * negotiate formats and propagate flags. During SRO readout, those
+ * functions do not run, so each bridge must reconstruct its own state.
+ *
+ * The SRO infrastructure traverses bridges from first to last, so the
+ * previous bridge's state is already available. The input bus
+ * configuration is taken from the previous bridge's output. The output
+ * bus format comes from the connector's display_info, since this bridge
+ * is the last in the chain and acts as a passthrough to the connector.
+ * Bus flags are also taken from display_info, matching the default
+ * propagation behavior of drm_atomic_bridge_propagate_bus_flags().
+ */
+static int
+display_connector_readout_state(struct drm_bridge *bridge,
+                               struct drm_atomic_sro_state *state,
+                               struct drm_bridge_state *bridge_state,
+                               struct drm_crtc_state *crtc_state,
+                               struct drm_connector_state *conn_state)
+{
+       struct drm_display_info *di = &conn_state->connector->display_info;
+       struct drm_bridge *prev_bridge __free(drm_bridge_put) =
+               drm_bridge_get_prev_bridge(bridge);
+
+       if (prev_bridge) {
+               struct drm_private_state *prev_priv_state =
+                       drm_atomic_sro_get_private_obj_state(state,
+                                                            
&prev_bridge->base);
+               struct drm_bridge_state *prev_bridge_state;
+
+               if (!prev_priv_state)
+                       return -ENODEV;
+
+               prev_bridge_state =
+                       drm_priv_to_bridge_state(prev_priv_state);
+
+               bridge_state->input_bus_cfg.format =
+                       prev_bridge_state->output_bus_cfg.format;
+               bridge_state->input_bus_cfg.flags =
+                       prev_bridge_state->output_bus_cfg.flags;
+       } else {
+               bridge_state->input_bus_cfg.format = MEDIA_BUS_FMT_FIXED;
+               bridge_state->input_bus_cfg.flags = di->bus_flags;
+       }
+
+       if (di->num_bus_formats && di->bus_formats)
+               bridge_state->output_bus_cfg.format = di->bus_formats[0];
+       else
+               bridge_state->output_bus_cfg.format = MEDIA_BUS_FMT_FIXED;
+
+       bridge_state->output_bus_cfg.flags = di->bus_flags;
+
+       return 0;
+}
+
 static const struct drm_bridge_funcs display_connector_bridge_funcs = {
        .attach = display_connector_attach,
        .destroy = display_connector_destroy,
        .detect = display_connector_bridge_detect,
        .hpd_enable = display_connector_hpd_enable,
@@ -223,10 +285,12 @@ static const struct drm_bridge_funcs 
display_connector_bridge_funcs = {
        .atomic_get_output_bus_fmts = display_connector_get_output_bus_fmts,
        .atomic_get_input_bus_fmts = display_connector_get_input_bus_fmts,
        .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,
+       .atomic_sro_readout_state       = display_connector_readout_state,
+       .atomic_sro_compare_state       = 
drm_atomic_helper_bridge_compare_state,
 };
 
 static irqreturn_t display_connector_hpd_irq(int irq, void *arg)
 {
        struct display_connector *conn = arg;

-- 
2.55.0

Reply via email to