In order to enable drivers to read their initial state from the
hardware, each KMS object needs a hook to fill a pre-allocated state
from the hardware registers.

Introduce the atomic_sro_readout_state hook in every KMS object funcs
vtable: drm_crtc_funcs, drm_plane_funcs, drm_connector_funcs,
drm_bridge_funcs, and drm_private_state_funcs.

Also introduce the drm_mode_config_funcs.atomic_sro_readout_state and
drm_mode_config_helper_funcs.atomic_sro_build_state hooks, which are the
top-level entry points for drivers and helpers respectively.

Finally, add drm_atomic_sro_can_readout() to verify that all objects on
a device implement the readout hook, and wire it into
drm_atomic_sro_device_can_readout().

Reviewed-by: Maarten Lankhorst <[email protected]>
Signed-off-by: Maxime Ripard <[email protected]>
---
 drivers/gpu/drm/drm_atomic_state_readout.c | 64 ++++++++++++++++++++++++++++++
 include/drm/drm_atomic.h                   | 30 ++++++++++++++
 include/drm/drm_bridge.h                   | 36 +++++++++++++++++
 include/drm/drm_connector.h                | 31 +++++++++++++++
 include/drm/drm_crtc.h                     | 31 +++++++++++++++
 include/drm/drm_mode_config.h              | 22 ++++++++++
 include/drm/drm_modeset_helper_vtables.h   | 31 +++++++++++++++
 include/drm/drm_plane.h                    | 31 +++++++++++++++
 8 files changed, 276 insertions(+)

diff --git a/drivers/gpu/drm/drm_atomic_state_readout.c 
b/drivers/gpu/drm/drm_atomic_state_readout.c
index e93b0e4f0839..de40e7e2231c 100644
--- a/drivers/gpu/drm/drm_atomic_state_readout.c
+++ b/drivers/gpu/drm/drm_atomic_state_readout.c
@@ -7,4 +7,5 @@
 #include <drm/drm_crtc.h>
 #include <drm/drm_drv.h>
+#include <drm/drm_modeset_helper_vtables.h>
 #include <drm/drm_plane.h>
 #include <drm/drm_print.h>
@@ -27,4 +28,61 @@ MODULE_PARM_DESC(atomic_readout,
                 "Enable Hardware State Readout (0 = disabled, 1 = enabled, 2 = 
ignore missing compares, 3 = ignore missing readouts and compares, default = 
1)");
 
+static bool drm_atomic_sro_can_readout(struct drm_device *dev)
+{
+       struct drm_crtc *crtc;
+       struct drm_plane *plane;
+       struct drm_connector *connector;
+       struct drm_private_obj *privobj;
+       struct drm_connector_list_iter conn_iter;
+
+       if (!dev->mode_config.helper_private ||
+           !dev->mode_config.helper_private->atomic_sro_build_state)
+               return false;
+
+       if (atomic_readout == DRM_ATOMIC_READOUT_SKIP_MISSING_READOUT)
+               return true;
+
+       if (!dev->mode_config.funcs->atomic_sro_readout_state)
+               return false;
+
+       drm_for_each_privobj(privobj, dev) {
+               if (!privobj->funcs->atomic_sro_readout_state) {
+                       drm_dbg_atomic(dev,
+                                      "Private object %s missing readout 
callback",
+                                      privobj->name);
+                       return false;
+               }
+       }
+
+       drm_for_each_plane(plane, dev) {
+               if (!plane->funcs->atomic_sro_readout_state) {
+                       drm_dbg_atomic(dev, "Plane %s missing readout callback",
+                                      plane->name);
+                       return false;
+               }
+       }
+
+       drm_for_each_crtc(crtc, dev) {
+               if (!crtc->funcs->atomic_sro_readout_state) {
+                       drm_dbg_atomic(dev, "CRTC %s missing readout callback",
+                                      crtc->name);
+                       return false;
+               }
+       }
+
+       drm_connector_list_iter_begin(dev, &conn_iter);
+       drm_for_each_connector_iter(connector, &conn_iter) {
+               if (!connector->funcs->atomic_sro_readout_state) {
+                       drm_dbg_atomic(dev, "Connector %s missing readout 
callback",
+                                      connector->name);
+                       drm_connector_list_iter_end(&conn_iter);
+                       return false;
+               }
+       }
+       drm_connector_list_iter_end(&conn_iter);
+
+       return true;
+}
+
 /**
  * drm_atomic_sro_device_can_readout - check if a device supports hardware 
state readout
@@ -41,4 +99,6 @@ MODULE_PARM_DESC(atomic_readout,
 bool drm_atomic_sro_device_can_readout(struct drm_device *dev)
 {
+       bool ret;
+
        if (!drm_drv_uses_atomic_modeset(dev))
                return false;
@@ -47,4 +107,8 @@ bool drm_atomic_sro_device_can_readout(struct drm_device 
*dev)
                return false;
 
+       ret = drm_atomic_sro_can_readout(dev);
+       if (!ret)
+               return false;
+
        return true;
 }
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
index 87f85a77e5f3..42923f960b30 100644
--- a/include/drm/drm_atomic.h
+++ b/include/drm/drm_atomic.h
@@ -315,4 +315,34 @@ struct drm_private_state_funcs {
                                     struct drm_private_state *state);
 
+       /**
+        * @atomic_sro_readout_state:
+        *
+        * Initializes @obj_state to reflect the current hardware state
+        * of the private object.
+        *
+        * It is meant to be used by drivers that want to implement
+        * flicker-free boot (also called fastboot by i915) and allows
+        * to initialize the atomic state from the hardware state left
+        * by the firmware.
+        *
+        * It is used at initialization time, so drivers must make sure
+        * that the power state is sensible when accessing the hardware.
+        *
+        * 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.
+        *
+        * This hook is mandatory for drivers implementing SRO, but can
+        * be left unassigned otherwise.
+        *
+        * RETURNS:
+        *
+        * 0 on success, a negative error code otherwise.
+        */
+       int (*atomic_sro_readout_state)(struct drm_private_obj *obj,
+                                       struct drm_atomic_sro_state *state,
+                                       struct drm_private_state *obj_state);
+
        /**
         * @atomic_print_state:
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index a22a0bdf3251..ef044ad27d2c 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -281,4 +281,40 @@ struct drm_bridge_funcs {
                                    struct drm_atomic_commit *state);
 
+       /**
+        * @atomic_sro_readout_state:
+        *
+        * Initializes @bridge_state to reflect the current hardware
+        * state of the bridge.
+        *
+        * It is meant to be used by drivers that want to implement
+        * flicker-free boot (also called fastboot by i915) and allows
+        * to initialize the atomic state from the hardware state left
+        * by the firmware.
+        *
+        * It is used at initialization time, so drivers must make sure
+        * that the power state is sensible when accessing the hardware.
+        *
+        * The @crtc_state and @conn_state parameters point to the CRTC
+        * and connector states for the pipeline this bridge is part
+        * of.
+        *
+        * 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.
+        *
+        * This hook is mandatory for drivers implementing SRO, but can
+        * be left unassigned otherwise.
+        *
+        * RETURNS:
+        *
+        * 0 on success, a negative error code otherwise.
+        */
+       int (*atomic_sro_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);
+
        /**
         * @atomic_duplicate_state:
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index f656ed8324d3..f70b3aff259d 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -36,4 +36,5 @@
 #include <uapi/drm/drm_mode.h>
 
+struct drm_atomic_sro_state;
 struct drm_connector_helper_funcs;
 struct drm_modeset_acquire_ctx;
@@ -1814,4 +1815,34 @@ struct drm_connector_funcs {
        struct drm_connector_state *(*atomic_create_state)(struct drm_connector 
*connector);
 
+       /**
+        * @atomic_sro_readout_state:
+        *
+        * Initializes @conn_state to reflect the current hardware
+        * state of the connector.
+        *
+        * It is meant to be used by drivers that want to implement
+        * flicker-free boot (also called fastboot by i915) and allows
+        * to initialize the atomic state from the hardware state left
+        * by the firmware.
+        *
+        * It is used at initialization time, so drivers must make sure
+        * that the power state is sensible when accessing the hardware.
+        *
+        * 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.
+        *
+        * This hook is mandatory for drivers implementing SRO, but can
+        * be left unassigned otherwise.
+        *
+        * RETURNS:
+        *
+        * 0 on success, a negative error code otherwise.
+        */
+       int (*atomic_sro_readout_state)(struct drm_connector *connector,
+                                       struct drm_atomic_sro_state *state,
+                                       struct drm_connector_state *conn_state);
+
        /**
         * @atomic_duplicate_state:
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index be5dca538d87..52365638b06f 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -36,4 +36,5 @@
 #include <drm/drm_mode_config.h>
 
+struct drm_atomic_sro_state;
 struct drm_connector;
 struct drm_device;
@@ -643,4 +644,34 @@ struct drm_crtc_funcs {
        struct drm_crtc_state *(*atomic_create_state)(struct drm_crtc *crtc);
 
+       /**
+        * @atomic_sro_readout_state:
+        *
+        * Initializes @crtc_state to reflect the current hardware
+        * state of the CRTC.
+        *
+        * It is meant to be used by drivers that want to implement
+        * flicker-free boot (also called fastboot by i915) and allows
+        * to initialize the atomic state from the hardware state left
+        * by the firmware.
+        *
+        * It is used at initialization time, so drivers must make sure
+        * that the power state is sensible when accessing the hardware.
+        *
+        * 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.
+        *
+        * This hook is mandatory for drivers implementing SRO, but can
+        * be left unassigned otherwise.
+        *
+        * RETURNS:
+        *
+        * 0 on success, a negative error code otherwise.
+        */
+       int (*atomic_sro_readout_state)(struct drm_crtc *crtc,
+                                       struct drm_atomic_sro_state *state,
+                                       struct drm_crtc_state *crtc_state);
+
        /**
         * @atomic_duplicate_state:
diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h
index 084517beb924..dd555f8727ad 100644
--- a/include/drm/drm_mode_config.h
+++ b/include/drm/drm_mode_config.h
@@ -322,4 +322,26 @@ struct drm_mode_config_funcs {
         */
        void (*atomic_state_free)(struct drm_atomic_commit *state);
+
+       /**
+        * @atomic_sro_readout_state:
+        *
+        * This optional hook is called at initialization time to read
+        * out the hardware state and initialize the DRM objects' atomic
+        * states from it.
+        *
+        * When implemented, the framework will prefer hardware state
+        * readout over creating pristine default states, enabling the
+        * first modeset to be skipped if the firmware already set up
+        * the display (flicker-free boot).
+        *
+        * If the readout fails, for example because the hardware was
+        * programmed with a configuration the driver doesn't support,
+        * the state initialization will fall back to the regular path.
+        *
+        * RETURNS:
+        *
+        * 0 on success, a negative error code otherwise.
+        */
+        int (*atomic_sro_readout_state)(struct drm_device *dev);
 };
 
diff --git a/include/drm/drm_modeset_helper_vtables.h 
b/include/drm/drm_modeset_helper_vtables.h
index 60b30cc26940..6858f0052637 100644
--- a/include/drm/drm_modeset_helper_vtables.h
+++ b/include/drm/drm_modeset_helper_vtables.h
@@ -1598,4 +1598,35 @@ struct drm_mode_config_helper_funcs {
         */
        int (*atomic_commit_setup)(struct drm_atomic_commit *state);
+
+       /**
+        * @atomic_sro_build_state:
+        *
+        * Builds a &struct drm_atomic_sro_state from the current hardware
+        * state by calling the atomic_sro_readout_state hooks on the KMS
+        * objects to read out.
+        *
+        * This hook is called both at initialization time, to create the
+        * initial state from what the firmware programmed, and after
+        * blocking commits, to read back the hardware state and compare it
+        * to what was committed.
+        *
+        * When @commit is NULL, the state of every object in the device is
+        * read out, and the caller must hold all the modeset locks, as
+        * drm_mode_config_create_initial_state() does.
+        *
+        * When @commit is not NULL, only the objects that have a state in
+        * @commit are read out. The caller must hold the locks of the
+        * objects in @commit through its acquire context, and the readout
+        * hooks must not access the state of any object outside of the
+        * commit, since its locks aren't held.
+        *
+        * RETURNS:
+        *
+        * A &struct drm_atomic_sro_state on success, an error pointer
+        * otherwise.
+        */
+       struct drm_atomic_sro_state *
+       (*atomic_sro_build_state)(struct drm_device *dev,
+                                 struct drm_atomic_commit *commit);
 };
 
diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h
index 410d7b02b77a..d16dc057adf6 100644
--- a/include/drm/drm_plane.h
+++ b/include/drm/drm_plane.h
@@ -35,4 +35,5 @@
 enum drm_panic_type;
 
+struct drm_atomic_sro_state;
 struct drm_crtc;
 struct drm_plane_size_hint;
@@ -395,4 +396,34 @@ struct drm_plane_funcs {
        struct drm_plane_state *(*atomic_create_state)(struct drm_plane *plane);
 
+       /**
+        * @atomic_sro_readout_state:
+        *
+        * Initializes @plane_state to reflect the current hardware
+        * state of the plane.
+        *
+        * It is meant to be used by drivers that want to implement
+        * flicker-free boot (also called fastboot by i915) and allows
+        * to initialize the atomic state from the hardware state left
+        * by the firmware.
+        *
+        * It is used at initialization time, so drivers must make sure
+        * that the power state is sensible when accessing the hardware.
+        *
+        * 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.
+        *
+        * This hook is mandatory for drivers implementing SRO, but can
+        * be left unassigned otherwise.
+        *
+        * RETURNS:
+        *
+        * 0 on success, a negative error code otherwise.
+        */
+       int (*atomic_sro_readout_state)(struct drm_plane *plane,
+                                       struct drm_atomic_sro_state *state,
+                                       struct drm_plane_state *plane_state);
+
        /**
         * @atomic_duplicate_state:

-- 
2.55.0

Reply via email to