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.

Reviewed-by: Maarten Lankhorst <[email protected]>
Signed-off-by: Maxime Ripard <[email protected]>
---
 drivers/gpu/drm/drm_bridge.c | 89 ++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 86 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index b2d77824e4be..5977157d8e67 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -31,4 +31,5 @@
 
 #include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_atomic_state_readout.h>
 #include <drm/drm_bridge.h>
 #include <drm/drm_debugfs.h>
@@ -562,4 +563,71 @@ drm_bridge_atomic_create_priv_state(struct drm_private_obj 
*obj)
 }
 
+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) {
+                       drm_connector_list_iter_end(&conn_iter);
+                       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;
+
+       /*
+        * If the bridge's encoder has no routed connector or no
+        * active CRTC, the pipeline is disabled and there is nothing
+        * to read out. Keep the pristine default state created by
+        * &drm_mode_config_helper_funcs.atomic_sro_build_state.
+        */
+       conn_state = find_connector_state_for_encoder(state, encoder);
+       if (!conn_state)
+               return 0;
+
+       if (!conn_state->crtc)
+               return 0;
+
+       crtc_state = drm_atomic_sro_get_crtc_state(state, conn_state->crtc);
+       if (!crtc_state)
+               return 0;
+
+       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,
@@ -589,4 +657,12 @@ static const struct drm_private_state_funcs 
drm_bridge_priv_state_funcs = {
 };
 
+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
@@ -600,5 +676,7 @@ static const struct drm_private_state_funcs 
drm_bridge_priv_state_funcs = {
 bool drm_private_obj_is_bridge(struct drm_private_obj *obj)
 {
-       return obj && obj->funcs == &drm_bridge_priv_state_funcs;
+       return obj &&
+              (obj->funcs == &drm_bridge_priv_state_funcs ||
+               obj->funcs == &drm_bridge_priv_state_funcs_with_sro);
 }
 EXPORT_SYMBOL(drm_private_obj_is_bridge);
@@ -673,6 +751,11 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct 
drm_bridge *bridge,
        }
 
-       drm_atomic_private_obj_init(bridge->dev, &bridge->base, bridge->name,
-                                   &drm_bridge_priv_state_funcs);
+       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;

-- 
2.55.0

Reply via email to