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.

Reviewed-by: Maarten Lankhorst <[email protected]>
Signed-off-by: Maxime Ripard <[email protected]>
---
 drivers/gpu/drm/display/drm_bridge_connector.c | 36 ++++++++++++++++++++++++++
 include/drm/drm_bridge.h                       | 32 +++++++++++++++++++++++
 2 files changed, 68 insertions(+)

diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c 
b/drivers/gpu/drm/display/drm_bridge_connector.c
index 2202e6044444..13a0faa115e0 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -12,4 +12,5 @@
 
 #include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_atomic_state_readout_helper.h>
 #include <drm/drm_bridge.h>
 #include <drm/drm_bridge_connector.h>
@@ -68,4 +69,12 @@ struct drm_bridge_connector {
         */
        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:
@@ -245,4 +254,24 @@ drm_bridge_connector_create_state(struct drm_connector 
*connector)
 }
 
+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;
+       int ret;
+
+       if (readout) {
+               ret = readout->funcs->atomic_sro_connector_readout(readout, 
state, conn_state);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
 static enum drm_connector_color_format
 drm_bridge_connector_color_format(const struct drm_connector_state *conn_state)
@@ -271,4 +300,6 @@ static const struct drm_connector_funcs 
drm_bridge_connector_funcs = {
        .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,
@@ -828,4 +859,5 @@ static void drm_bridge_connector_put_bridges(struct 
drm_device *dev, void *data)
        struct drm_bridge_connector *bridge_connector = (struct 
drm_bridge_connector *)data;
 
+       drm_bridge_put(bridge_connector->bridge_connector_hw_readout);
        drm_bridge_put(bridge_connector->bridge_edid);
        drm_bridge_put(bridge_connector->bridge_hpd);
@@ -897,4 +929,8 @@ struct drm_connector *drm_bridge_connector_init(struct 
drm_device *drm,
                        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.
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 436dfffb25ad..b73f3424ed62 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -943,4 +943,29 @@ struct drm_bridge_funcs {
                                    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.
+        *
+        * Drivers are expected to return a negative error code if the
+        * hardware was programmed with a configuration they don't
+        * support. The framework will then abort the readout and fall
+        * back to the regular initialization path.
+        *
+        * 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:
@@ -1083,4 +1108,11 @@ enum drm_bridge_ops {
         */
        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),
 };
 

-- 
2.55.0

Reply via email to