The SRO infrastructure needs a state container to collect the readout
states for all KMS objects. Unlike drm_atomic_commit which tracks old
and new states for atomic commits, this container only holds a single
state per object: the one read out from the hardware.

Create struct drm_atomic_sro_state with arrays for planes, CRTCs,
connectors, and private objects, along with accessors to get and set
individual object states within it.

Signed-off-by: Maxime Ripard <[email protected]>
---
 drivers/gpu/drm/Makefile         |   1 +
 drivers/gpu/drm/drm_atomic_sro.c | 424 +++++++++++++++++++++++++++++++++++++++
 include/drm/drm_atomic_sro.h     |  51 +++++
 3 files changed, 476 insertions(+)

diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index e97faabcd783..64705ac96bd1 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -32,10 +32,11 @@ endif
 # Enable -Werror in CI and development
 subdir-ccflags-$(CONFIG_DRM_WERROR) += -Werror
 
 drm-y := \
        drm_atomic.o \
+       drm_atomic_sro.o \
        drm_atomic_uapi.o \
        drm_auth.o \
        drm_blend.o \
        drm_bridge.o \
        drm_cache.o \
diff --git a/drivers/gpu/drm/drm_atomic_sro.c b/drivers/gpu/drm/drm_atomic_sro.c
new file mode 100644
index 000000000000..177b97d451f5
--- /dev/null
+++ b/drivers/gpu/drm/drm_atomic_sro.c
@@ -0,0 +1,424 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_sro.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_plane.h>
+#include <drm/drm_print.h>
+#include <linux/module.h>
+
+#include "drm_internal.h"
+#include "drm_crtc_internal.h"
+
+struct __drm_atomic_sro_plane {
+       struct drm_plane *ptr;
+       struct drm_plane_state *state;
+};
+
+struct __drm_atomic_sro_crtc {
+       struct drm_crtc *ptr;
+       struct drm_crtc_state *state;
+};
+
+struct __drm_atomic_sro_connector {
+       struct drm_connector *ptr;
+       struct drm_connector_state *state;
+};
+
+struct __drm_atomic_sro_private_obj {
+       struct drm_private_obj *ptr;
+       struct drm_private_state *state;
+};
+
+struct drm_atomic_sro_state {
+       struct drm_device *dev;
+       struct __drm_atomic_sro_plane *planes;
+       struct __drm_atomic_sro_crtc *crtcs;
+       struct __drm_atomic_sro_connector *connectors;
+       struct __drm_atomic_sro_private_obj *private_objs;
+       unsigned int num_private_objs;
+};
+
+static unsigned int count_private_obj(struct drm_device *dev)
+{
+       struct drm_mode_config *config = &dev->mode_config;
+       struct drm_private_obj *obj;
+       unsigned int count = 0;
+
+       list_for_each_entry(obj, &config->privobj_list, head)
+               count++;
+
+       return count;
+}
+
+/**
+ * drm_atomic_sro_state_get_device - get the DRM device from an SRO state
+ * @state: SRO state
+ *
+ * RETURNS:
+ *
+ * The &struct drm_device associated with the SRO state.
+ */
+struct drm_device *
+drm_atomic_sro_state_get_device(struct drm_atomic_sro_state *state)
+{
+       return state->dev;
+}
+
+static int drm_atomic_sro_state_init(struct drm_device *dev,
+                                    struct drm_atomic_sro_state *state)
+{
+       state->planes =
+               kzalloc_objs(*state->planes, dev->mode_config.num_total_plane);
+       if (!state->planes)
+               return -ENOMEM;
+
+       state->crtcs = kzalloc_objs(*state->crtcs, dev->mode_config.num_crtc);
+       if (!state->crtcs)
+               return -ENOMEM;
+
+       state->connectors = kzalloc_objs(*state->connectors,
+                                        dev->mode_config.num_connector);
+       if (!state->connectors)
+               return -ENOMEM;
+
+       state->private_objs =
+               kzalloc_objs(*state->private_objs, count_private_obj(dev));
+       if (!state->private_objs)
+               return -ENOMEM;
+       state->num_private_objs = 0;
+
+       drm_dev_get(dev);
+       state->dev = dev;
+
+       return 0;
+}
+
+/**
+ * drm_atomic_sro_state_alloc - allocate an SRO state container
+ * @dev: DRM device to allocate the state for
+ *
+ * Allocates and initializes a &struct drm_atomic_sro_state that can hold
+ * readout states for all KMS objects on @dev.
+ *
+ * The returned state must be freed with drm_atomic_sro_state_free().
+ *
+ * RETURNS:
+ *
+ * A new &struct drm_atomic_sro_state on success, an error pointer on
+ * failure.
+ */
+struct drm_atomic_sro_state *drm_atomic_sro_state_alloc(struct drm_device *dev)
+{
+       struct drm_atomic_sro_state *state;
+       int ret;
+
+       state = kzalloc_obj(*state);
+       if (!state)
+               return ERR_PTR(-EINVAL);
+
+       ret = drm_atomic_sro_state_init(dev, state);
+       if (ret)
+               return ERR_PTR(ret);
+
+       return state;
+}
+EXPORT_SYMBOL(drm_atomic_sro_state_alloc);
+
+/**
+ * drm_atomic_sro_state_free - free an SRO state container
+ * @state: SRO state to free
+ *
+ * Frees a &struct drm_atomic_sro_state previously allocated with
+ * drm_atomic_sro_state_alloc(). Any states that have not been
+ * installed via drm_atomic_sro_install_state() are also freed.
+ */
+void drm_atomic_sro_state_free(struct drm_atomic_sro_state *state)
+{
+       struct drm_device *dev = state->dev;
+       unsigned int i;
+
+       for (i = 0; i < state->num_private_objs; i++) {
+               struct drm_private_obj *obj = state->private_objs[i].ptr;
+               struct drm_private_state *obj_state = 
state->private_objs[i].state;
+
+               if (!obj || !obj_state)
+                       continue;
+
+               obj->funcs->atomic_destroy_state(obj, obj_state);
+               state->private_objs[i].state = NULL;
+               state->private_objs[i].ptr = NULL;
+       }
+
+       kfree(state->private_objs);
+
+       for (i = 0; i < state->dev->mode_config.num_connector; i++) {
+               struct drm_connector *conn = state->connectors[i].ptr;
+               struct drm_connector_state *conn_state =
+                       state->connectors[i].state;
+
+               if (!conn || !conn_state)
+                       continue;
+
+               conn->funcs->atomic_destroy_state(conn, conn_state);
+               state->connectors[i].state = NULL;
+               state->connectors[i].ptr = NULL;
+               drm_connector_put(conn);
+       }
+
+       kfree(state->connectors);
+
+       for (i = 0; i < state->dev->mode_config.num_crtc; i++) {
+               struct drm_crtc *crtc = state->crtcs[i].ptr;
+               struct drm_crtc_state *crtc_state =
+                       state->crtcs[i].state;
+
+               if (!crtc || !crtc_state)
+                       continue;
+
+               crtc->funcs->atomic_destroy_state(crtc, crtc_state);
+               state->crtcs[i].state = NULL;
+               state->crtcs[i].ptr = NULL;
+       }
+
+       kfree(state->crtcs);
+
+       for (i = 0; i < state->dev->mode_config.num_total_plane; i++) {
+               struct drm_plane *plane = state->planes[i].ptr;
+               struct drm_plane_state *plane_state =
+                       state->planes[i].state;
+
+               if (!plane || !plane_state)
+                       continue;
+
+               plane->funcs->atomic_destroy_state(plane, plane_state);
+               state->planes[i].state = NULL;
+               state->planes[i].ptr = NULL;
+       }
+
+       kfree(state->planes);
+       kfree(state);
+
+       drm_dev_put(dev);
+}
+EXPORT_SYMBOL(drm_atomic_sro_state_free);
+
+/**
+ * drm_atomic_sro_state_print - prints drm atomic SRO state
+ * @state: SRO state to print
+ * @p: drm printer
+ *
+ * This function prints the SRO state snapshot using the drm printer which is
+ * passed to it. This snapshot can be used for debugging purposes.
+ */
+void drm_atomic_sro_state_print(const struct drm_atomic_sro_state *state,
+                               struct drm_printer *p)
+{
+       struct drm_mode_config *config = &state->dev->mode_config;
+       int i;
+
+       if (!p) {
+               drm_err(state->dev, "invalid drm printer\n");
+               return;
+       }
+
+       drm_dbg_atomic(state->dev, "Printing readout state %p\n", state);
+
+       for (i = 0; i < config->num_total_plane; i++) {
+               struct drm_plane_state *plane_state = state->planes[i].state;
+
+               drm_atomic_plane_print_state(p, plane_state);
+       }
+
+       for (i = 0; i < config->num_crtc; i++) {
+               struct drm_crtc_state *crtc_state = state->crtcs[i].state;
+
+               drm_atomic_crtc_print_state(p, crtc_state);
+       }
+
+       for (i = 0; i < config->num_connector; i++) {
+               struct drm_connector_state *conn_state = 
state->connectors[i].state;
+
+               drm_atomic_connector_print_state(p, conn_state);
+       }
+
+       for (i = 0; i < state->num_private_objs; i++) {
+               struct drm_private_state *obj_state = 
state->private_objs[i].state;
+
+               drm_atomic_private_obj_print_state(p, obj_state);
+       }
+}
+EXPORT_SYMBOL(drm_atomic_sro_state_print);
+
+/**
+ * drm_atomic_sro_get_plane_state - get the plane state from an SRO state
+ * @state: SRO state container
+ * @plane: plane to get the state for
+ *
+ * RETURNS:
+ *
+ * The &struct drm_plane_state for @plane, or NULL if not yet read out.
+ */
+struct drm_plane_state *
+drm_atomic_sro_get_plane_state(struct drm_atomic_sro_state *state,
+                              struct drm_plane *plane)
+{
+       unsigned int index = drm_plane_index(plane);
+
+       return state->planes[index].state;
+};
+EXPORT_SYMBOL(drm_atomic_sro_get_plane_state);
+
+/**
+ * drm_atomic_sro_set_plane_state - store a plane state into an SRO state
+ * @state: SRO state container
+ * @plane: plane the state belongs to
+ * @plane_state: plane state to store
+ */
+void drm_atomic_sro_set_plane_state(struct drm_atomic_sro_state *state,
+                                   struct drm_plane *plane,
+                                   struct drm_plane_state *plane_state)
+{
+       unsigned int index = drm_plane_index(plane);
+
+       state->planes[index].ptr = plane;
+       state->planes[index].state = plane_state;
+
+       drm_dbg_atomic(plane->dev,
+                      "Added [PLANE:%d:%s] %p state to readout state %p\n",
+                      plane->base.id, plane->name, plane_state, state);
+}
+EXPORT_SYMBOL(drm_atomic_sro_set_plane_state);
+
+/**
+ * drm_atomic_sro_get_crtc_state - get the CRTC state from an SRO state
+ * @state: SRO state container
+ * @crtc: CRTC to get the state for
+ *
+ * RETURNS:
+ *
+ * The &struct drm_crtc_state for @crtc, or NULL if not yet read out.
+ */
+struct drm_crtc_state *
+drm_atomic_sro_get_crtc_state(struct drm_atomic_sro_state *state,
+                             struct drm_crtc *crtc)
+{
+       unsigned int index = drm_crtc_index(crtc);
+
+       return state->crtcs[index].state;
+};
+EXPORT_SYMBOL(drm_atomic_sro_get_crtc_state);
+
+/**
+ * drm_atomic_sro_set_crtc_state - store a CRTC state into an SRO state
+ * @state: SRO state container
+ * @crtc: CRTC the state belongs to
+ * @crtc_state: CRTC state to store
+ */
+void drm_atomic_sro_set_crtc_state(struct drm_atomic_sro_state *state,
+                                  struct drm_crtc *crtc,
+                                  struct drm_crtc_state *crtc_state)
+{
+       unsigned int index = drm_crtc_index(crtc);
+
+       state->crtcs[index].ptr = crtc;
+       state->crtcs[index].state = crtc_state;
+
+       drm_dbg_atomic(state->dev,
+                      "Added [CRTC:%d:%s] %p state to readout state %p\n",
+                      crtc->base.id, crtc->name, crtc_state, state);
+}
+EXPORT_SYMBOL(drm_atomic_sro_set_crtc_state);
+
+/**
+ * drm_atomic_sro_get_connector_state - get the connector state from an SRO 
state
+ * @state: SRO state container
+ * @connector: connector to get the state for
+ *
+ * RETURNS:
+ *
+ * The &struct drm_connector_state for @connector, or NULL if not yet
+ * read out.
+ */
+struct drm_connector_state *
+drm_atomic_sro_get_connector_state(struct drm_atomic_sro_state *state,
+                                  struct drm_connector *connector)
+{
+       unsigned int index = drm_connector_index(connector);
+
+       return state->connectors[index].state;
+};
+EXPORT_SYMBOL(drm_atomic_sro_get_connector_state);
+
+/**
+ * drm_atomic_sro_set_connector_state - store a connector state into an SRO 
state
+ * @state: SRO state container
+ * @conn: connector the state belongs to
+ * @conn_state: connector state to store
+ *
+ * Takes a reference on @conn which is released when the state is
+ * installed or freed.
+ */
+void drm_atomic_sro_set_connector_state(struct drm_atomic_sro_state *state,
+                                       struct drm_connector *conn,
+                                       struct drm_connector_state *conn_state)
+{
+       unsigned int index = drm_connector_index(conn);
+
+       drm_connector_get(conn);
+       state->connectors[index].ptr = conn;
+       state->connectors[index].state = conn_state;
+
+       drm_dbg_atomic(conn->dev,
+                      "Added [CONNECTOR:%d:%s] %p state to readout state %p\n",
+                      conn->base.id, conn->name, conn_state, state);
+}
+EXPORT_SYMBOL(drm_atomic_sro_set_connector_state);
+
+/**
+ * drm_atomic_sro_get_private_obj_state - get a private object state from an 
SRO state
+ * @state: SRO state container
+ * @obj: private object to get the state for
+ *
+ * RETURNS:
+ *
+ * The &struct drm_private_state for @obj, or NULL if not yet read out.
+ */
+struct drm_private_state *
+drm_atomic_sro_get_private_obj_state(struct drm_atomic_sro_state *state,
+                                    struct drm_private_obj *obj)
+{
+       unsigned int i;
+
+       for (i = 0; i < state->num_private_objs; i++)
+               if (obj == state->private_objs[i].ptr)
+                       return state->private_objs[i].state;
+
+       return NULL;
+}
+EXPORT_SYMBOL(drm_atomic_sro_get_private_obj_state);
+
+/**
+ * drm_atomic_sro_set_private_obj_state - store a private object state into an 
SRO state
+ * @state: SRO state container
+ * @obj: private object the state belongs to
+ * @obj_state: private object state to store
+ */
+void drm_atomic_sro_set_private_obj_state(struct drm_atomic_sro_state *state,
+                                         struct drm_private_obj *obj,
+                                         struct drm_private_state *obj_state)
+{
+       unsigned int index = state->num_private_objs;
+
+       state->private_objs[index].ptr = obj;
+       state->private_objs[index].state = obj_state;
+       state->num_private_objs += 1;
+
+       drm_dbg_atomic(state->dev,
+                      "Added new private object %p state %p to readout state 
%p\n", obj,
+                      obj_state, state);
+}
+EXPORT_SYMBOL(drm_atomic_sro_set_private_obj_state);
diff --git a/include/drm/drm_atomic_sro.h b/include/drm/drm_atomic_sro.h
new file mode 100644
index 000000000000..5a9333a05796
--- /dev/null
+++ b/include/drm/drm_atomic_sro.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef DRM_ATOMIC_SRO_H_
+#define DRM_ATOMIC_SRO_H_
+
+struct drm_atomic_sro_state;
+struct drm_connector;
+struct drm_connector_state;
+struct drm_crtc;
+struct drm_crtc_state;
+struct drm_device;
+struct drm_plane;
+struct drm_plane_state;
+struct drm_printer;
+struct drm_private_obj;
+struct drm_private_state;
+
+struct drm_atomic_sro_state *drm_atomic_sro_state_alloc(struct drm_device 
*dev);
+void drm_atomic_sro_state_free(struct drm_atomic_sro_state *state);
+void drm_atomic_sro_state_print(const struct drm_atomic_sro_state *state,
+                               struct drm_printer *p);
+
+struct drm_device *
+drm_atomic_sro_state_get_device(struct drm_atomic_sro_state *state);
+
+struct drm_plane_state *
+drm_atomic_sro_get_plane_state(struct drm_atomic_sro_state *state,
+                              struct drm_plane *plane);
+void drm_atomic_sro_set_plane_state(struct drm_atomic_sro_state *state,
+                                   struct drm_plane *plane,
+                                   struct drm_plane_state *plane_state);
+struct drm_crtc_state *
+drm_atomic_sro_get_crtc_state(struct drm_atomic_sro_state *state,
+                             struct drm_crtc *crtc);
+void drm_atomic_sro_set_crtc_state(struct drm_atomic_sro_state *state,
+                                  struct drm_crtc *crtc,
+                                  struct drm_crtc_state *crtc_state);
+struct drm_connector_state *
+drm_atomic_sro_get_connector_state(struct drm_atomic_sro_state *state,
+                                  struct drm_connector *connector);
+void drm_atomic_sro_set_connector_state(struct drm_atomic_sro_state *state,
+                                       struct drm_connector *conn,
+                                       struct drm_connector_state *conn_state);
+struct drm_private_state *
+drm_atomic_sro_get_private_obj_state(struct drm_atomic_sro_state *state,
+                                    struct drm_private_obj *private_obj);
+void drm_atomic_sro_set_private_obj_state(struct drm_atomic_sro_state *state,
+                                         struct drm_private_obj *obj,
+                                         struct drm_private_state *obj_state);
+
+#endif /* DRM_ATOMIC_SRO_H_ */

-- 
2.54.0

Reply via email to