All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 00/15] drm/vkms: Allow to configure device
@ 2025-02-11 11:08 José Expósito
  2025-02-11 11:08 ` [PATCH v2 01/15] drm/vkms: Fix use after free and double free on init error José Expósito
                   ` (14 more replies)
  0 siblings, 15 replies; 32+ messages in thread
From: José Expósito @ 2025-02-11 11:08 UTC (permalink / raw)
  To: louis.chauvet
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel,
	José Expósito

Hi everyone,

In preparation for ConfigFS support, a flexible way to configure VKMS device(s)
is required.
This series adds the required APIs to create a configuration, the code changes
required to apply it and KUnit test validating the changes.

Louis Chauvet and I are working on ConfigFS support. In this series I tried to
merge his changes [1] with mine [2].
I kept his Signed-off-by to reflect that, even if I show up as the author of
some/most of the patches, this was a joint effort.

I'll send the the ConfigFS code [3] and its IGT tests [4] code this or next week.
Meanwhile, the IGT tests also exercise this series and can be used for additional
test coverage.

Best wishes,
José Expósito

[1] https://patchwork.kernel.org/project/dri-devel/cover/20250121-google-remove-crtc-index-from-parameter-v3-0-cac00a3c3544@bootlin.com/
[2] https://patchwork.kernel.org/project/dri-devel/cover/20240813105134.17439-1-jose.exposito89@gmail.com/
[3] https://github.com/JoseExposito/linux/commits/patch-vkms-configfs/
[4] https://gitlab.freedesktop.org/jexposit/igt-gpu-tools/-/commits/vkms-configfs

Changes in v2:

  - Applied review comments by Louis Chauvet (thanks!!):
    - Added Co-developped-by tags where required
    - Split the first patch in 2: vkms_connector.h/c creation and struct
      vkms_connector creation
    - Fixes a user after free/double free bug. The bug is already present
      in drm-misc-next and the fix could be merged independently
    - Implemented for_each macros for the config
    - Other naming fixes
  - Link to v1: https://lore.kernel.org/all/20250129110059.12199-1-jose.exposito89@gmail.com/

José Expósito (14):
  drm/vkms: Fix use after free and double free on init error
  drm/vkms: Extract vkms_connector header
  drm/vkms: Create vkms_connector struct
  drm/vkms: Add KUnit test scaffolding
  drm/vkms: Extract vkms_config header
  drm/vkms: Move default_config creation to its own function
  drm/vkms: Set device name from vkms_config
  drm/vkms: Allow to configure multiple planes
  drm/vkms: Allow to configure multiple CRTCs
  drm/vkms: Allow to attach planes and CRTCs
  drm/vkms: Allow to configure multiple encoders
  drm/vkms: Allow to attach encoders and CRTCs
  drm/vkms: Allow to configure multiple connectors
  drm/vkms: Allow to attach connectors and encoders

Louis Chauvet (1):
  drm/vkms: Add a validation function for VKMS configuration

 .clang-format                                 |   7 +
 drivers/gpu/drm/vkms/Kconfig                  |  15 +
 drivers/gpu/drm/vkms/Makefile                 |   5 +-
 drivers/gpu/drm/vkms/tests/.kunitconfig       |   4 +
 drivers/gpu/drm/vkms/tests/Makefile           |   3 +
 drivers/gpu/drm/vkms/tests/vkms_config_test.c | 826 ++++++++++++++++++
 drivers/gpu/drm/vkms/vkms_config.c            | 606 +++++++++++++
 drivers/gpu/drm/vkms/vkms_config.h            | 429 +++++++++
 drivers/gpu/drm/vkms/vkms_connector.c         |  61 ++
 drivers/gpu/drm/vkms/vkms_connector.h         |  26 +
 drivers/gpu/drm/vkms/vkms_drv.c               |  60 +-
 drivers/gpu/drm/vkms/vkms_drv.h               |  19 +-
 drivers/gpu/drm/vkms/vkms_output.c            | 176 ++--
 13 files changed, 2089 insertions(+), 148 deletions(-)
 create mode 100644 drivers/gpu/drm/vkms/tests/.kunitconfig
 create mode 100644 drivers/gpu/drm/vkms/tests/Makefile
 create mode 100644 drivers/gpu/drm/vkms/tests/vkms_config_test.c
 create mode 100644 drivers/gpu/drm/vkms/vkms_config.c
 create mode 100644 drivers/gpu/drm/vkms/vkms_config.h
 create mode 100644 drivers/gpu/drm/vkms/vkms_connector.c
 create mode 100644 drivers/gpu/drm/vkms/vkms_connector.h

-- 
2.48.1


^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH v2 01/15] drm/vkms: Fix use after free and double free on init error
  2025-02-11 11:08 [PATCH v2 00/15] drm/vkms: Allow to configure device José Expósito
@ 2025-02-11 11:08 ` José Expósito
  2025-02-11 14:33   ` Thomas Zimmermann
  2025-02-11 11:08 ` [PATCH v2 02/15] drm/vkms: Extract vkms_connector header José Expósito
                   ` (13 subsequent siblings)
  14 siblings, 1 reply; 32+ messages in thread
From: José Expósito @ 2025-02-11 11:08 UTC (permalink / raw)
  To: louis.chauvet
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel,
	José Expósito

If the driver initialization fails, the vkms_exit() function might
access an uninitialized or freed default_config pointer and it might
double free it.

Fix both possible errors by initializing default_config only when the
driver initialization succeeded.

Reported-by: Louis Chauvet <louis.chauvet@bootlin.com>
Link: https://lore.kernel.org/all/Z5uDHcCmAwiTsGte@louis-chauvet-laptop/
Fixes: 2df7af93fdad ("drm/vkms: Add vkms_config type")
Signed-off-by: José Expósito <jose.exposito89@gmail.com>
---
 drivers/gpu/drm/vkms/vkms_drv.c | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c
index 7c142bfc3bd9..b6de91134a22 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.c
+++ b/drivers/gpu/drm/vkms/vkms_drv.c
@@ -235,17 +235,19 @@ static int __init vkms_init(void)
 	if (!config)
 		return -ENOMEM;
 
-	default_config = config;
-
 	config->cursor = enable_cursor;
 	config->writeback = enable_writeback;
 	config->overlay = enable_overlay;
 
 	ret = vkms_create(config);
-	if (ret)
+	if (ret) {
 		kfree(config);
+		return ret;
+	}
 
-	return ret;
+	default_config = config;
+
+	return 0;
 }
 
 static void vkms_destroy(struct vkms_config *config)
@@ -269,9 +271,10 @@ static void vkms_destroy(struct vkms_config *config)
 
 static void __exit vkms_exit(void)
 {
-	if (default_config->dev)
-		vkms_destroy(default_config);
+	if (!default_config)
+		return;
 
+	vkms_destroy(default_config);
 	kfree(default_config);
 }
 
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 02/15] drm/vkms: Extract vkms_connector header
  2025-02-11 11:08 [PATCH v2 00/15] drm/vkms: Allow to configure device José Expósito
  2025-02-11 11:08 ` [PATCH v2 01/15] drm/vkms: Fix use after free and double free on init error José Expósito
@ 2025-02-11 11:08 ` José Expósito
  2025-02-11 11:09 ` [PATCH v2 03/15] drm/vkms: Create vkms_connector struct José Expósito
                   ` (12 subsequent siblings)
  14 siblings, 0 replies; 32+ messages in thread
From: José Expósito @ 2025-02-11 11:08 UTC (permalink / raw)
  To: louis.chauvet
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel,
	José Expósito

Up until now, the logic to manage connectors was in vkms_output.c.

Since more options will be added to connectors in the future, extract
the code to its own file.

Refactor, no functional changes.

Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
Signed-off-by: José Expósito <jose.exposito89@gmail.com>
---
 drivers/gpu/drm/vkms/Makefile         |  3 +-
 drivers/gpu/drm/vkms/vkms_connector.c | 50 +++++++++++++++++++++++++++
 drivers/gpu/drm/vkms/vkms_connector.h | 17 +++++++++
 drivers/gpu/drm/vkms/vkms_output.c    | 41 +++-------------------
 4 files changed, 73 insertions(+), 38 deletions(-)
 create mode 100644 drivers/gpu/drm/vkms/vkms_connector.c
 create mode 100644 drivers/gpu/drm/vkms/vkms_connector.h

diff --git a/drivers/gpu/drm/vkms/Makefile b/drivers/gpu/drm/vkms/Makefile
index 1b28a6a32948..6b0615c424f2 100644
--- a/drivers/gpu/drm/vkms/Makefile
+++ b/drivers/gpu/drm/vkms/Makefile
@@ -6,6 +6,7 @@ vkms-y := \
 	vkms_formats.o \
 	vkms_crtc.o \
 	vkms_composer.o \
-	vkms_writeback.o
+	vkms_writeback.o \
+	vkms_connector.o
 
 obj-$(CONFIG_DRM_VKMS) += vkms.o
diff --git a/drivers/gpu/drm/vkms/vkms_connector.c b/drivers/gpu/drm/vkms/vkms_connector.c
new file mode 100644
index 000000000000..fc97f265dea6
--- /dev/null
+++ b/drivers/gpu/drm/vkms/vkms_connector.c
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_probe_helper.h>
+
+#include "vkms_connector.h"
+
+static const struct drm_connector_funcs vkms_connector_funcs = {
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int vkms_conn_get_modes(struct drm_connector *connector)
+{
+	int count;
+
+	/* Use the default modes list from DRM */
+	count = drm_add_modes_noedid(connector, XRES_MAX, YRES_MAX);
+	drm_set_preferred_mode(connector, XRES_DEF, YRES_DEF);
+
+	return count;
+}
+
+static const struct drm_connector_helper_funcs vkms_conn_helper_funcs = {
+	.get_modes    = vkms_conn_get_modes,
+};
+
+struct drm_connector *vkms_connector_init(struct vkms_device *vkmsdev)
+{
+	struct drm_device *dev = &vkmsdev->drm;
+	struct drm_connector *connector;
+	int ret;
+
+	connector = drmm_kzalloc(dev, sizeof(*connector), GFP_KERNEL);
+	if (!connector)
+		return ERR_PTR(-ENOMEM);
+
+	ret = drmm_connector_init(dev, connector, &vkms_connector_funcs,
+				  DRM_MODE_CONNECTOR_VIRTUAL, NULL);
+	if (ret)
+		return ERR_PTR(ret);
+
+	drm_connector_helper_add(connector, &vkms_conn_helper_funcs);
+
+	return connector;
+}
diff --git a/drivers/gpu/drm/vkms/vkms_connector.h b/drivers/gpu/drm/vkms/vkms_connector.h
new file mode 100644
index 000000000000..beb5ebe09155
--- /dev/null
+++ b/drivers/gpu/drm/vkms/vkms_connector.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef _VKMS_CONNECTOR_H_
+#define _VKMS_CONNECTOR_H_
+
+#include "vkms_drv.h"
+
+/**
+ * vkms_connector_init() - Initialize a connector
+ * @vkmsdev: VKMS device containing the connector
+ *
+ * Returns:
+ * The connector or an error on failure.
+ */
+struct drm_connector *vkms_connector_init(struct vkms_device *vkmsdev);
+
+#endif /* _VKMS_CONNECTOR_H_ */
diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
index 22f0d678af3a..b01c3e9289d0 100644
--- a/drivers/gpu/drm/vkms/vkms_output.c
+++ b/drivers/gpu/drm/vkms/vkms_output.c
@@ -1,32 +1,8 @@
 // SPDX-License-Identifier: GPL-2.0+
 
+#include "vkms_connector.h"
 #include "vkms_drv.h"
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_edid.h>
 #include <drm/drm_managed.h>
-#include <drm/drm_probe_helper.h>
-
-static const struct drm_connector_funcs vkms_connector_funcs = {
-	.fill_modes = drm_helper_probe_single_connector_modes,
-	.reset = drm_atomic_helper_connector_reset,
-	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
-	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-};
-
-static int vkms_conn_get_modes(struct drm_connector *connector)
-{
-	int count;
-
-	/* Use the default modes list from DRM */
-	count = drm_add_modes_noedid(connector, XRES_MAX, YRES_MAX);
-	drm_set_preferred_mode(connector, XRES_DEF, YRES_DEF);
-
-	return count;
-}
-
-static const struct drm_connector_helper_funcs vkms_conn_helper_funcs = {
-	.get_modes    = vkms_conn_get_modes,
-};
 
 int vkms_output_init(struct vkms_device *vkmsdev)
 {
@@ -73,21 +49,12 @@ int vkms_output_init(struct vkms_device *vkmsdev)
 		}
 	}
 
-	connector = drmm_kzalloc(dev, sizeof(*connector), GFP_KERNEL);
-	if (!connector) {
-		DRM_ERROR("Failed to allocate connector\n");
-		return -ENOMEM;
-	}
-
-	ret = drmm_connector_init(dev, connector, &vkms_connector_funcs,
-				  DRM_MODE_CONNECTOR_VIRTUAL, NULL);
-	if (ret) {
+	connector = vkms_connector_init(vkmsdev);
+	if (IS_ERR(connector)) {
 		DRM_ERROR("Failed to init connector\n");
-		return ret;
+		return PTR_ERR(connector);
 	}
 
-	drm_connector_helper_add(connector, &vkms_conn_helper_funcs);
-
 	encoder = drmm_kzalloc(dev, sizeof(*encoder), GFP_KERNEL);
 	if (!encoder) {
 		DRM_ERROR("Failed to allocate encoder\n");
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 03/15] drm/vkms: Create vkms_connector struct
  2025-02-11 11:08 [PATCH v2 00/15] drm/vkms: Allow to configure device José Expósito
  2025-02-11 11:08 ` [PATCH v2 01/15] drm/vkms: Fix use after free and double free on init error José Expósito
  2025-02-11 11:08 ` [PATCH v2 02/15] drm/vkms: Extract vkms_connector header José Expósito
@ 2025-02-11 11:09 ` José Expósito
  2025-02-11 11:09 ` [PATCH v2 04/15] drm/vkms: Add KUnit test scaffolding José Expósito
                   ` (11 subsequent siblings)
  14 siblings, 0 replies; 32+ messages in thread
From: José Expósito @ 2025-02-11 11:09 UTC (permalink / raw)
  To: louis.chauvet
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel,
	José Expósito

Create a structure wrapping the drm_connector.

Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
Signed-off-by: José Expósito <jose.exposito89@gmail.com>
---
 drivers/gpu/drm/vkms/vkms_connector.c |  8 ++++----
 drivers/gpu/drm/vkms/vkms_connector.h | 11 ++++++++++-
 drivers/gpu/drm/vkms/vkms_output.c    |  4 ++--
 3 files changed, 16 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/vkms/vkms_connector.c b/drivers/gpu/drm/vkms/vkms_connector.c
index fc97f265dea6..ab8b52a84151 100644
--- a/drivers/gpu/drm/vkms/vkms_connector.c
+++ b/drivers/gpu/drm/vkms/vkms_connector.c
@@ -29,22 +29,22 @@ static const struct drm_connector_helper_funcs vkms_conn_helper_funcs = {
 	.get_modes    = vkms_conn_get_modes,
 };
 
-struct drm_connector *vkms_connector_init(struct vkms_device *vkmsdev)
+struct vkms_connector *vkms_connector_init(struct vkms_device *vkmsdev)
 {
 	struct drm_device *dev = &vkmsdev->drm;
-	struct drm_connector *connector;
+	struct vkms_connector *connector;
 	int ret;
 
 	connector = drmm_kzalloc(dev, sizeof(*connector), GFP_KERNEL);
 	if (!connector)
 		return ERR_PTR(-ENOMEM);
 
-	ret = drmm_connector_init(dev, connector, &vkms_connector_funcs,
+	ret = drmm_connector_init(dev, &connector->base, &vkms_connector_funcs,
 				  DRM_MODE_CONNECTOR_VIRTUAL, NULL);
 	if (ret)
 		return ERR_PTR(ret);
 
-	drm_connector_helper_add(connector, &vkms_conn_helper_funcs);
+	drm_connector_helper_add(&connector->base, &vkms_conn_helper_funcs);
 
 	return connector;
 }
diff --git a/drivers/gpu/drm/vkms/vkms_connector.h b/drivers/gpu/drm/vkms/vkms_connector.h
index beb5ebe09155..c9149c1b7af0 100644
--- a/drivers/gpu/drm/vkms/vkms_connector.h
+++ b/drivers/gpu/drm/vkms/vkms_connector.h
@@ -5,6 +5,15 @@
 
 #include "vkms_drv.h"
 
+/**
+ * struct vkms_connector - VKMS custom type wrapping around the DRM connector
+ *
+ * @drm: Base DRM connector
+ */
+struct vkms_connector {
+	struct drm_connector base;
+};
+
 /**
  * vkms_connector_init() - Initialize a connector
  * @vkmsdev: VKMS device containing the connector
@@ -12,6 +21,6 @@
  * Returns:
  * The connector or an error on failure.
  */
-struct drm_connector *vkms_connector_init(struct vkms_device *vkmsdev);
+struct vkms_connector *vkms_connector_init(struct vkms_device *vkmsdev);
 
 #endif /* _VKMS_CONNECTOR_H_ */
diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
index b01c3e9289d0..4b5abe159add 100644
--- a/drivers/gpu/drm/vkms/vkms_output.c
+++ b/drivers/gpu/drm/vkms/vkms_output.c
@@ -7,7 +7,7 @@
 int vkms_output_init(struct vkms_device *vkmsdev)
 {
 	struct drm_device *dev = &vkmsdev->drm;
-	struct drm_connector *connector;
+	struct vkms_connector *connector;
 	struct drm_encoder *encoder;
 	struct vkms_output *output;
 	struct vkms_plane *primary, *overlay, *cursor = NULL;
@@ -69,7 +69,7 @@ int vkms_output_init(struct vkms_device *vkmsdev)
 	encoder->possible_crtcs = drm_crtc_mask(&output->crtc);
 
 	/* Attach the encoder and the connector */
-	ret = drm_connector_attach_encoder(connector, encoder);
+	ret = drm_connector_attach_encoder(&connector->base, encoder);
 	if (ret) {
 		DRM_ERROR("Failed to attach connector to encoder\n");
 		return ret;
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 04/15] drm/vkms: Add KUnit test scaffolding
  2025-02-11 11:08 [PATCH v2 00/15] drm/vkms: Allow to configure device José Expósito
                   ` (2 preceding siblings ...)
  2025-02-11 11:09 ` [PATCH v2 03/15] drm/vkms: Create vkms_connector struct José Expósito
@ 2025-02-11 11:09 ` José Expósito
  2025-02-13 13:59   ` Louis Chauvet
  2025-02-11 11:09 ` [PATCH v2 05/15] drm/vkms: Extract vkms_config header José Expósito
                   ` (10 subsequent siblings)
  14 siblings, 1 reply; 32+ messages in thread
From: José Expósito @ 2025-02-11 11:09 UTC (permalink / raw)
  To: louis.chauvet
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel,
	José Expósito, Arthur Grillo

Add the required boilerplate to start creating KUnit test.

To run the tests:

    $ ./tools/testing/kunit/kunit.py run \
      --kunitconfig=drivers/gpu/drm/vkms/tests

Co-developed-by: Arthur Grillo <arthurgrillo@riseup.net>
Signed-off-by: Arthur Grillo <arthurgrillo@riseup.net>
Co-developed-by: Louis Chauvet <louis.chauvet@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
Signed-off-by: José Expósito <jose.exposito89@gmail.com>
---
 drivers/gpu/drm/vkms/Kconfig                  | 15 +++++++++++++++
 drivers/gpu/drm/vkms/Makefile                 |  1 +
 drivers/gpu/drm/vkms/tests/.kunitconfig       |  4 ++++
 drivers/gpu/drm/vkms/tests/Makefile           |  3 +++
 drivers/gpu/drm/vkms/tests/vkms_config_test.c | 19 +++++++++++++++++++
 5 files changed, 42 insertions(+)
 create mode 100644 drivers/gpu/drm/vkms/tests/.kunitconfig
 create mode 100644 drivers/gpu/drm/vkms/tests/Makefile
 create mode 100644 drivers/gpu/drm/vkms/tests/vkms_config_test.c

diff --git a/drivers/gpu/drm/vkms/Kconfig b/drivers/gpu/drm/vkms/Kconfig
index 9def079f685b..d4665e913de7 100644
--- a/drivers/gpu/drm/vkms/Kconfig
+++ b/drivers/gpu/drm/vkms/Kconfig
@@ -14,3 +14,18 @@ config DRM_VKMS
 	  a VKMS.
 
 	  If M is selected the module will be called vkms.
+
+config DRM_VKMS_KUNIT_TESTS
+	tristate "KUnit tests for VKMS." if !KUNIT_ALL_TESTS
+	depends on DRM_VKMS && KUNIT
+	default KUNIT_ALL_TESTS
+	help
+	  This builds unit tests for VKMS. This option is not useful for
+	  distributions or general kernels, but only for kernel
+	  developers working on VKMS.
+
+	  For more information on KUnit and unit tests in general,
+	  please refer to the KUnit documentation in
+	  Documentation/dev-tools/kunit/.
+
+	  If in doubt, say "N".
diff --git a/drivers/gpu/drm/vkms/Makefile b/drivers/gpu/drm/vkms/Makefile
index 6b0615c424f2..45ef4b89379e 100644
--- a/drivers/gpu/drm/vkms/Makefile
+++ b/drivers/gpu/drm/vkms/Makefile
@@ -10,3 +10,4 @@ vkms-y := \
 	vkms_connector.o
 
 obj-$(CONFIG_DRM_VKMS) += vkms.o
+obj-$(CONFIG_DRM_VKMS_KUNIT_TESTS) += tests/
diff --git a/drivers/gpu/drm/vkms/tests/.kunitconfig b/drivers/gpu/drm/vkms/tests/.kunitconfig
new file mode 100644
index 000000000000..70e378228cbd
--- /dev/null
+++ b/drivers/gpu/drm/vkms/tests/.kunitconfig
@@ -0,0 +1,4 @@
+CONFIG_KUNIT=y
+CONFIG_DRM=y
+CONFIG_DRM_VKMS=y
+CONFIG_DRM_VKMS_KUNIT_TESTS=y
diff --git a/drivers/gpu/drm/vkms/tests/Makefile b/drivers/gpu/drm/vkms/tests/Makefile
new file mode 100644
index 000000000000..b78371d08aa9
--- /dev/null
+++ b/drivers/gpu/drm/vkms/tests/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_DRM_VKMS_KUNIT_TESTS) += vkms_config_test.o
diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
new file mode 100644
index 000000000000..1177e62e19cb
--- /dev/null
+++ b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <kunit/test.h>
+
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
+
+static struct kunit_case vkms_config_test_cases[] = {
+	{}
+};
+
+static struct kunit_suite vkms_config_test_suite = {
+	.name = "vkms-config",
+	.test_cases = vkms_config_test_cases,
+};
+
+kunit_test_suite(vkms_config_test_suite);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Kunit test for vkms config utility");
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 05/15] drm/vkms: Extract vkms_config header
  2025-02-11 11:08 [PATCH v2 00/15] drm/vkms: Allow to configure device José Expósito
                   ` (3 preceding siblings ...)
  2025-02-11 11:09 ` [PATCH v2 04/15] drm/vkms: Add KUnit test scaffolding José Expósito
@ 2025-02-11 11:09 ` José Expósito
  2025-02-13 13:59   ` Louis Chauvet
  2025-02-11 11:09 ` [PATCH v2 06/15] drm/vkms: Move default_config creation to its own function José Expósito
                   ` (9 subsequent siblings)
  14 siblings, 1 reply; 32+ messages in thread
From: José Expósito @ 2025-02-11 11:09 UTC (permalink / raw)
  To: louis.chauvet
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel,
	José Expósito

Creating a new vkms_config structure will be more complex once we
start adding more options.

Extract the vkms_config structure to its own header and source files
and add functions to create and delete a vkms_config and to initialize
debugfs.

Refactor, no functional changes.

Co-developed-by: Louis Chauvet <louis.chauvet@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
Signed-off-by: José Expósito <jose.exposito89@gmail.com>
---
 drivers/gpu/drm/vkms/Makefile                 |  3 +-
 drivers/gpu/drm/vkms/tests/vkms_config_test.c | 13 +++++
 drivers/gpu/drm/vkms/vkms_config.c            | 47 +++++++++++++++++++
 drivers/gpu/drm/vkms/vkms_config.h            | 47 +++++++++++++++++++
 drivers/gpu/drm/vkms/vkms_drv.c               | 34 +++-----------
 drivers/gpu/drm/vkms/vkms_drv.h               | 15 +-----
 drivers/gpu/drm/vkms/vkms_output.c            |  1 +
 7 files changed, 118 insertions(+), 42 deletions(-)
 create mode 100644 drivers/gpu/drm/vkms/vkms_config.c
 create mode 100644 drivers/gpu/drm/vkms/vkms_config.h

diff --git a/drivers/gpu/drm/vkms/Makefile b/drivers/gpu/drm/vkms/Makefile
index 45ef4b89379e..9bf6b8f94daf 100644
--- a/drivers/gpu/drm/vkms/Makefile
+++ b/drivers/gpu/drm/vkms/Makefile
@@ -7,7 +7,8 @@ vkms-y := \
 	vkms_crtc.o \
 	vkms_composer.o \
 	vkms_writeback.o \
-	vkms_connector.o
+	vkms_connector.o \
+	vkms_config.o
 
 obj-$(CONFIG_DRM_VKMS) += vkms.o
 obj-$(CONFIG_DRM_VKMS_KUNIT_TESTS) += tests/
diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
index 1177e62e19cb..a7060504f3dc 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_config_test.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
@@ -2,9 +2,22 @@
 
 #include <kunit/test.h>
 
+#include "../vkms_config.h"
+
 MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
 
+static void vkms_config_test_empty_config(struct kunit *test)
+{
+	struct vkms_config *config;
+
+	config = vkms_config_create();
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
+
+	vkms_config_destroy(config);
+}
+
 static struct kunit_case vkms_config_test_cases[] = {
+	KUNIT_CASE(vkms_config_test_empty_config),
 	{}
 };
 
diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
new file mode 100644
index 000000000000..152b2ecd6aef
--- /dev/null
+++ b/drivers/gpu/drm/vkms/vkms_config.c
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <linux/slab.h>
+
+#include <drm/drm_print.h>
+#include <drm/drm_debugfs.h>
+
+#include "vkms_config.h"
+
+struct vkms_config *vkms_config_create(void)
+{
+	struct vkms_config *config;
+
+	config = kzalloc(sizeof(*config), GFP_KERNEL);
+	if (!config)
+		return ERR_PTR(-ENOMEM);
+
+	return config;
+}
+
+void vkms_config_destroy(struct vkms_config *config)
+{
+	kfree(config);
+}
+
+static int vkms_config_show(struct seq_file *m, void *data)
+{
+	struct drm_debugfs_entry *entry = m->private;
+	struct drm_device *dev = entry->dev;
+	struct vkms_device *vkmsdev = drm_device_to_vkms_device(dev);
+
+	seq_printf(m, "writeback=%d\n", vkmsdev->config->writeback);
+	seq_printf(m, "cursor=%d\n", vkmsdev->config->cursor);
+	seq_printf(m, "overlay=%d\n", vkmsdev->config->overlay);
+
+	return 0;
+}
+
+static const struct drm_debugfs_info vkms_config_debugfs_list[] = {
+	{ "vkms_config", vkms_config_show, 0 },
+};
+
+void vkms_config_register_debugfs(struct vkms_device *vkms_device)
+{
+	drm_debugfs_add_files(&vkms_device->drm, vkms_config_debugfs_list,
+			      ARRAY_SIZE(vkms_config_debugfs_list));
+}
diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
new file mode 100644
index 000000000000..ced10f56a812
--- /dev/null
+++ b/drivers/gpu/drm/vkms/vkms_config.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef _VKMS_CONFIG_H_
+#define _VKMS_CONFIG_H_
+
+#include <linux/types.h>
+
+#include "vkms_drv.h"
+
+/**
+ * struct vkms_config - General configuration for VKMS driver
+ *
+ * @writeback: If true, a writeback buffer can be attached to the CRTC
+ * @cursor: If true, a cursor plane is created in the VKMS device
+ * @overlay: If true, NUM_OVERLAY_PLANES will be created for the VKMS device
+ * @dev: Used to store the current VKMS device. Only set when the device is instantiated.
+ */
+struct vkms_config {
+	bool writeback;
+	bool cursor;
+	bool overlay;
+	struct vkms_device *dev;
+};
+
+/**
+ * vkms_config_create() - Create a new VKMS configuration
+ *
+ * Returns:
+ * The new vkms_config or an error. Call vkms_config_destroy() to free the
+ * returned configuration.
+ */
+struct vkms_config *vkms_config_create(void);
+
+/**
+ * vkms_config_destroy() - Free a VKMS configuration
+ * @config: vkms_config to free
+ */
+void vkms_config_destroy(struct vkms_config *config);
+
+/**
+ * vkms_config_register_debugfs() - Register a debugfs file to show the device's
+ * configuration
+ * @vkms_device: Device to register
+ */
+void vkms_config_register_debugfs(struct vkms_device *vkms_device);
+
+#endif /* _VKMS_CONFIG_H_ */
diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c
index b6de91134a22..37de0658e6ee 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.c
+++ b/drivers/gpu/drm/vkms/vkms_drv.c
@@ -27,11 +27,9 @@
 #include <drm/drm_gem_shmem_helper.h>
 #include <drm/drm_vblank.h>
 
+#include "vkms_config.h"
 #include "vkms_drv.h"
 
-#include <drm/drm_print.h>
-#include <drm/drm_debugfs.h>
-
 #define DRIVER_NAME	"vkms"
 #define DRIVER_DESC	"Virtual Kernel Mode Setting"
 #define DRIVER_MAJOR	1
@@ -81,23 +79,6 @@ static void vkms_atomic_commit_tail(struct drm_atomic_state *old_state)
 	drm_atomic_helper_cleanup_planes(dev, old_state);
 }
 
-static int vkms_config_show(struct seq_file *m, void *data)
-{
-	struct drm_debugfs_entry *entry = m->private;
-	struct drm_device *dev = entry->dev;
-	struct vkms_device *vkmsdev = drm_device_to_vkms_device(dev);
-
-	seq_printf(m, "writeback=%d\n", vkmsdev->config->writeback);
-	seq_printf(m, "cursor=%d\n", vkmsdev->config->cursor);
-	seq_printf(m, "overlay=%d\n", vkmsdev->config->overlay);
-
-	return 0;
-}
-
-static const struct drm_debugfs_info vkms_config_debugfs_list[] = {
-	{ "vkms_config", vkms_config_show, 0 },
-};
-
 static const struct drm_driver vkms_driver = {
 	.driver_features	= DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM,
 	.fops			= &vkms_driver_fops,
@@ -208,8 +189,7 @@ static int vkms_create(struct vkms_config *config)
 	if (ret)
 		goto out_devres;
 
-	drm_debugfs_add_files(&vkms_device->drm, vkms_config_debugfs_list,
-			      ARRAY_SIZE(vkms_config_debugfs_list));
+	vkms_config_register_debugfs(vkms_device);
 
 	ret = drm_dev_register(&vkms_device->drm, 0);
 	if (ret)
@@ -231,9 +211,9 @@ static int __init vkms_init(void)
 	int ret;
 	struct vkms_config *config;
 
-	config = kmalloc(sizeof(*config), GFP_KERNEL);
-	if (!config)
-		return -ENOMEM;
+	config = vkms_config_create();
+	if (IS_ERR(config))
+		return PTR_ERR(config);
 
 	config->cursor = enable_cursor;
 	config->writeback = enable_writeback;
@@ -241,7 +221,7 @@ static int __init vkms_init(void)
 
 	ret = vkms_create(config);
 	if (ret) {
-		kfree(config);
+		vkms_config_destroy(config);
 		return ret;
 	}
 
@@ -275,7 +255,7 @@ static void __exit vkms_exit(void)
 		return;
 
 	vkms_destroy(default_config);
-	kfree(default_config);
+	vkms_config_destroy(default_config);
 }
 
 module_init(vkms_init);
diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h
index abbb652be2b5..af7081c940d6 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.h
+++ b/drivers/gpu/drm/vkms/vkms_drv.h
@@ -189,20 +189,7 @@ struct vkms_output {
 	spinlock_t composer_lock;
 };
 
-/**
- * struct vkms_config - General configuration for VKMS driver
- *
- * @writeback: If true, a writeback buffer can be attached to the CRTC
- * @cursor: If true, a cursor plane is created in the VKMS device
- * @overlay: If true, NUM_OVERLAY_PLANES will be created for the VKMS device
- * @dev: Used to store the current VKMS device. Only set when the device is instantiated.
- */
-struct vkms_config {
-	bool writeback;
-	bool cursor;
-	bool overlay;
-	struct vkms_device *dev;
-};
+struct vkms_config;
 
 /**
  * struct vkms_device - Description of a VKMS device
diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
index 4b5abe159add..068a7f87ecec 100644
--- a/drivers/gpu/drm/vkms/vkms_output.c
+++ b/drivers/gpu/drm/vkms/vkms_output.c
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 
+#include "vkms_config.h"
 #include "vkms_connector.h"
 #include "vkms_drv.h"
 #include <drm/drm_managed.h>
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 06/15] drm/vkms: Move default_config creation to its own function
  2025-02-11 11:08 [PATCH v2 00/15] drm/vkms: Allow to configure device José Expósito
                   ` (4 preceding siblings ...)
  2025-02-11 11:09 ` [PATCH v2 05/15] drm/vkms: Extract vkms_config header José Expósito
@ 2025-02-11 11:09 ` José Expósito
  2025-02-13 13:59   ` Louis Chauvet
  2025-02-11 11:09 ` [PATCH v2 07/15] drm/vkms: Set device name from vkms_config José Expósito
                   ` (8 subsequent siblings)
  14 siblings, 1 reply; 32+ messages in thread
From: José Expósito @ 2025-02-11 11:09 UTC (permalink / raw)
  To: louis.chauvet
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel,
	José Expósito

Extract the initialization of the default configuration to a function.
Refactor, no functional changes.

Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
Co-developed-by: Louis Chauvet <louis.chauvet@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
Signed-off-by: José Expósito <jose.exposito89@gmail.com>
---
 drivers/gpu/drm/vkms/tests/vkms_config_test.c | 38 +++++++++++++++++++
 drivers/gpu/drm/vkms/vkms_config.c            | 17 +++++++++
 drivers/gpu/drm/vkms/vkms_config.h            | 14 +++++++
 drivers/gpu/drm/vkms/vkms_drv.c               |  6 +--
 4 files changed, 70 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
index a7060504f3dc..d8644a1e3e18 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_config_test.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
@@ -6,6 +6,12 @@
 
 MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
 
+struct default_config_case {
+	bool enable_cursor;
+	bool enable_writeback;
+	bool enable_overlay;
+};
+
 static void vkms_config_test_empty_config(struct kunit *test)
 {
 	struct vkms_config *config;
@@ -16,8 +22,40 @@ static void vkms_config_test_empty_config(struct kunit *test)
 	vkms_config_destroy(config);
 }
 
+static struct default_config_case default_config_cases[] = {
+	{ false, false, false },
+	{ true, false, false },
+	{ true, true, false },
+	{ true, false, true },
+	{ false, true, false },
+	{ false, true, true },
+	{ false, false, true },
+	{ true, true, true },
+};
+
+KUNIT_ARRAY_PARAM(default_config, default_config_cases, NULL);
+
+static void vkms_config_test_default_config(struct kunit *test)
+{
+	const struct default_config_case *params = test->param_value;
+	struct vkms_config *config;
+
+	config = vkms_config_default_create(params->enable_cursor,
+					    params->enable_writeback,
+					    params->enable_overlay);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
+
+	KUNIT_EXPECT_EQ(test, config->cursor, params->enable_cursor);
+	KUNIT_EXPECT_EQ(test, config->writeback, params->enable_writeback);
+	KUNIT_EXPECT_EQ(test, config->overlay, params->enable_overlay);
+
+	vkms_config_destroy(config);
+}
+
 static struct kunit_case vkms_config_test_cases[] = {
 	KUNIT_CASE(vkms_config_test_empty_config),
+	KUNIT_CASE_PARAM(vkms_config_test_default_config,
+			 default_config_gen_params),
 	{}
 };
 
diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
index 152b2ecd6aef..30ce4b23553f 100644
--- a/drivers/gpu/drm/vkms/vkms_config.c
+++ b/drivers/gpu/drm/vkms/vkms_config.c
@@ -18,6 +18,23 @@ struct vkms_config *vkms_config_create(void)
 	return config;
 }
 
+struct vkms_config *vkms_config_default_create(bool enable_cursor,
+					       bool enable_writeback,
+					       bool enable_overlay)
+{
+	struct vkms_config *config;
+
+	config = vkms_config_create();
+	if (IS_ERR(config))
+		return config;
+
+	config->cursor = enable_cursor;
+	config->writeback = enable_writeback;
+	config->overlay = enable_overlay;
+
+	return config;
+}
+
 void vkms_config_destroy(struct vkms_config *config)
 {
 	kfree(config);
diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
index ced10f56a812..d0868750826a 100644
--- a/drivers/gpu/drm/vkms/vkms_config.h
+++ b/drivers/gpu/drm/vkms/vkms_config.h
@@ -31,6 +31,20 @@ struct vkms_config {
  */
 struct vkms_config *vkms_config_create(void);
 
+/**
+ * vkms_config_default_create() - Create the configuration for the default device
+ * @enable_cursor: Create or not a cursor plane
+ * @enable_writeback: Create or not a writeback connector
+ * @enable_overlay: Create or not overlay planes
+ *
+ * Returns:
+ * The default vkms_config or an error. Call vkms_config_destroy() to free the
+ * returned configuration.
+ */
+struct vkms_config *vkms_config_default_create(bool enable_cursor,
+					       bool enable_writeback,
+					       bool enable_overlay);
+
 /**
  * vkms_config_destroy() - Free a VKMS configuration
  * @config: vkms_config to free
diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c
index 37de0658e6ee..582d5825f42b 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.c
+++ b/drivers/gpu/drm/vkms/vkms_drv.c
@@ -211,14 +211,10 @@ static int __init vkms_init(void)
 	int ret;
 	struct vkms_config *config;
 
-	config = vkms_config_create();
+	config = vkms_config_default_create(enable_cursor, enable_writeback, enable_overlay);
 	if (IS_ERR(config))
 		return PTR_ERR(config);
 
-	config->cursor = enable_cursor;
-	config->writeback = enable_writeback;
-	config->overlay = enable_overlay;
-
 	ret = vkms_create(config);
 	if (ret) {
 		vkms_config_destroy(config);
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 07/15] drm/vkms: Set device name from vkms_config
  2025-02-11 11:08 [PATCH v2 00/15] drm/vkms: Allow to configure device José Expósito
                   ` (5 preceding siblings ...)
  2025-02-11 11:09 ` [PATCH v2 06/15] drm/vkms: Move default_config creation to its own function José Expósito
@ 2025-02-11 11:09 ` José Expósito
  2025-02-11 11:09 ` [PATCH v2 08/15] drm/vkms: Add a validation function for VKMS configuration José Expósito
                   ` (7 subsequent siblings)
  14 siblings, 0 replies; 32+ messages in thread
From: José Expósito @ 2025-02-11 11:09 UTC (permalink / raw)
  To: louis.chauvet
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel,
	José Expósito

In order to be able to create multiple devices, the device name needs to
be unique.

Allow to set it in the VKMS configuration.

Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
Signed-off-by: José Expósito <jose.exposito89@gmail.com>
---
 drivers/gpu/drm/vkms/tests/vkms_config_test.c |  7 ++++++-
 drivers/gpu/drm/vkms/vkms_config.c            | 14 ++++++++++++--
 drivers/gpu/drm/vkms/vkms_config.h            | 18 +++++++++++++++++-
 drivers/gpu/drm/vkms/vkms_drv.c               |  4 +++-
 drivers/gpu/drm/vkms/vkms_drv.h               |  2 ++
 5 files changed, 40 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
index d8644a1e3e18..92798590051b 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_config_test.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
@@ -15,10 +15,15 @@ struct default_config_case {
 static void vkms_config_test_empty_config(struct kunit *test)
 {
 	struct vkms_config *config;
+	const char *dev_name = "test";
 
-	config = vkms_config_create();
+	config = vkms_config_create(dev_name);
 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
 
+	/* The dev_name string and the config have different lifetimes */
+	dev_name = NULL;
+	KUNIT_EXPECT_STREQ(test, vkms_config_get_device_name(config), "test");
+
 	vkms_config_destroy(config);
 }
 
diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
index 30ce4b23553f..11b0e539920b 100644
--- a/drivers/gpu/drm/vkms/vkms_config.c
+++ b/drivers/gpu/drm/vkms/vkms_config.c
@@ -7,7 +7,7 @@
 
 #include "vkms_config.h"
 
-struct vkms_config *vkms_config_create(void)
+struct vkms_config *vkms_config_create(const char *dev_name)
 {
 	struct vkms_config *config;
 
@@ -15,6 +15,12 @@ struct vkms_config *vkms_config_create(void)
 	if (!config)
 		return ERR_PTR(-ENOMEM);
 
+	config->dev_name = kstrdup_const(dev_name, GFP_KERNEL);
+	if (!config->dev_name) {
+		kfree(config);
+		return ERR_PTR(-ENOMEM);
+	}
+
 	return config;
 }
 
@@ -24,7 +30,7 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
 {
 	struct vkms_config *config;
 
-	config = vkms_config_create();
+	config = vkms_config_create(DEFAULT_DEVICE_NAME);
 	if (IS_ERR(config))
 		return config;
 
@@ -37,6 +43,7 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
 
 void vkms_config_destroy(struct vkms_config *config)
 {
+	kfree_const(config->dev_name);
 	kfree(config);
 }
 
@@ -45,7 +52,10 @@ static int vkms_config_show(struct seq_file *m, void *data)
 	struct drm_debugfs_entry *entry = m->private;
 	struct drm_device *dev = entry->dev;
 	struct vkms_device *vkmsdev = drm_device_to_vkms_device(dev);
+	const char *dev_name;
 
+	dev_name = vkms_config_get_device_name((struct vkms_config *)vkmsdev->config);
+	seq_printf(m, "dev_name=%s\n", dev_name);
 	seq_printf(m, "writeback=%d\n", vkmsdev->config->writeback);
 	seq_printf(m, "cursor=%d\n", vkmsdev->config->cursor);
 	seq_printf(m, "overlay=%d\n", vkmsdev->config->overlay);
diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
index d0868750826a..fcaa909fb2e0 100644
--- a/drivers/gpu/drm/vkms/vkms_config.h
+++ b/drivers/gpu/drm/vkms/vkms_config.h
@@ -10,12 +10,14 @@
 /**
  * struct vkms_config - General configuration for VKMS driver
  *
+ * @dev_name: Name of the device
  * @writeback: If true, a writeback buffer can be attached to the CRTC
  * @cursor: If true, a cursor plane is created in the VKMS device
  * @overlay: If true, NUM_OVERLAY_PLANES will be created for the VKMS device
  * @dev: Used to store the current VKMS device. Only set when the device is instantiated.
  */
 struct vkms_config {
+	const char *dev_name;
 	bool writeback;
 	bool cursor;
 	bool overlay;
@@ -24,12 +26,13 @@ struct vkms_config {
 
 /**
  * vkms_config_create() - Create a new VKMS configuration
+ * @dev_name: Name of the device
  *
  * Returns:
  * The new vkms_config or an error. Call vkms_config_destroy() to free the
  * returned configuration.
  */
-struct vkms_config *vkms_config_create(void);
+struct vkms_config *vkms_config_create(const char *dev_name);
 
 /**
  * vkms_config_default_create() - Create the configuration for the default device
@@ -51,6 +54,19 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
  */
 void vkms_config_destroy(struct vkms_config *config);
 
+/**
+ * vkms_config_get_device_name() - Return the name of the device
+ * @config: Configuration to get the device name from
+ *
+ * Returns:
+ * The device name. Only valid while @config is valid.
+ */
+static inline const char *
+vkms_config_get_device_name(struct vkms_config *config)
+{
+	return config->dev_name;
+}
+
 /**
  * vkms_config_register_debugfs() - Register a debugfs file to show the device's
  * configuration
diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c
index 582d5825f42b..ba977ef09b2b 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.c
+++ b/drivers/gpu/drm/vkms/vkms_drv.c
@@ -151,8 +151,10 @@ static int vkms_create(struct vkms_config *config)
 	int ret;
 	struct platform_device *pdev;
 	struct vkms_device *vkms_device;
+	const char *dev_name;
 
-	pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0);
+	dev_name = vkms_config_get_device_name(config);
+	pdev = platform_device_register_simple(dev_name, -1, NULL, 0);
 	if (IS_ERR(pdev))
 		return PTR_ERR(pdev);
 
diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h
index af7081c940d6..a74a7fc3a056 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.h
+++ b/drivers/gpu/drm/vkms/vkms_drv.h
@@ -12,6 +12,8 @@
 #include <drm/drm_encoder.h>
 #include <drm/drm_writeback.h>
 
+#define DEFAULT_DEVICE_NAME "vkms"
+
 #define XRES_MIN    10
 #define YRES_MIN    10
 
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 08/15] drm/vkms: Add a validation function for VKMS configuration
  2025-02-11 11:08 [PATCH v2 00/15] drm/vkms: Allow to configure device José Expósito
                   ` (6 preceding siblings ...)
  2025-02-11 11:09 ` [PATCH v2 07/15] drm/vkms: Set device name from vkms_config José Expósito
@ 2025-02-11 11:09 ` José Expósito
  2025-02-13 13:59   ` Louis Chauvet
  2025-02-11 11:09 ` [PATCH v2 09/15] drm/vkms: Allow to configure multiple planes José Expósito
                   ` (6 subsequent siblings)
  14 siblings, 1 reply; 32+ messages in thread
From: José Expósito @ 2025-02-11 11:09 UTC (permalink / raw)
  To: louis.chauvet
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel,
	José Expósito

From: Louis Chauvet <louis.chauvet@bootlin.com>

As the configuration will be used by userspace, add a validator to avoid
creating a broken DRM device.

For the moment, the function always returns true, but rules will be
added in future patches.

Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
Co-developed-by: José Expósito <jose.exposito89@gmail.com>
Signed-off-by: José Expósito <jose.exposito89@gmail.com>
---
 drivers/gpu/drm/vkms/tests/vkms_config_test.c |  2 ++
 drivers/gpu/drm/vkms/vkms_config.c            |  5 +++++
 drivers/gpu/drm/vkms/vkms_config.h            | 10 ++++++++++
 drivers/gpu/drm/vkms/vkms_drv.h               |  2 +-
 drivers/gpu/drm/vkms/vkms_output.c            |  3 +++
 5 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
index 92798590051b..6e07139d261c 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_config_test.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
@@ -54,6 +54,8 @@ static void vkms_config_test_default_config(struct kunit *test)
 	KUNIT_EXPECT_EQ(test, config->writeback, params->enable_writeback);
 	KUNIT_EXPECT_EQ(test, config->overlay, params->enable_overlay);
 
+	KUNIT_EXPECT_TRUE(test, vkms_config_is_valid(config));
+
 	vkms_config_destroy(config);
 }
 
diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
index 11b0e539920b..67f71d29596e 100644
--- a/drivers/gpu/drm/vkms/vkms_config.c
+++ b/drivers/gpu/drm/vkms/vkms_config.c
@@ -47,6 +47,11 @@ void vkms_config_destroy(struct vkms_config *config)
 	kfree(config);
 }
 
+bool vkms_config_is_valid(struct vkms_config *config)
+{
+	return true;
+}
+
 static int vkms_config_show(struct seq_file *m, void *data)
 {
 	struct drm_debugfs_entry *entry = m->private;
diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
index fcaa909fb2e0..0376dceaf6be 100644
--- a/drivers/gpu/drm/vkms/vkms_config.h
+++ b/drivers/gpu/drm/vkms/vkms_config.h
@@ -67,6 +67,16 @@ vkms_config_get_device_name(struct vkms_config *config)
 	return config->dev_name;
 }
 
+/**
+ * vkms_config_is_valid() - Validate a configuration
+ * @config: Configuration to validate
+ *
+ * Returns:
+ * Whether the configuration is valid or not.
+ * For example, a configuration without primary planes is not valid.
+ */
+bool vkms_config_is_valid(struct vkms_config *config);
+
 /**
  * vkms_config_register_debugfs() - Register a debugfs file to show the device's
  * configuration
diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h
index a74a7fc3a056..95afc39ce985 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.h
+++ b/drivers/gpu/drm/vkms/vkms_drv.h
@@ -204,7 +204,7 @@ struct vkms_config;
 struct vkms_device {
 	struct drm_device drm;
 	struct platform_device *platform;
-	const struct vkms_config *config;
+	struct vkms_config *config;
 };
 
 /*
diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
index 068a7f87ecec..414cc933af41 100644
--- a/drivers/gpu/drm/vkms/vkms_output.c
+++ b/drivers/gpu/drm/vkms/vkms_output.c
@@ -16,6 +16,9 @@ int vkms_output_init(struct vkms_device *vkmsdev)
 	int writeback;
 	unsigned int n;
 
+	if (!vkms_config_is_valid(vkmsdev->config))
+		return -EINVAL;
+
 	/*
 	 * Initialize used plane. One primary plane is required to perform the composition.
 	 *
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 09/15] drm/vkms: Allow to configure multiple planes
  2025-02-11 11:08 [PATCH v2 00/15] drm/vkms: Allow to configure device José Expósito
                   ` (7 preceding siblings ...)
  2025-02-11 11:09 ` [PATCH v2 08/15] drm/vkms: Add a validation function for VKMS configuration José Expósito
@ 2025-02-11 11:09 ` José Expósito
  2025-02-13 13:59   ` Louis Chauvet
  2025-02-11 11:09 ` [PATCH v2 10/15] drm/vkms: Allow to configure multiple CRTCs José Expósito
                   ` (5 subsequent siblings)
  14 siblings, 1 reply; 32+ messages in thread
From: José Expósito @ 2025-02-11 11:09 UTC (permalink / raw)
  To: louis.chauvet
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel,
	José Expósito

Add a list of planes to vkms_config and create as many planes as
configured during output initialization.

For backwards compatibility, add one primary plane and, if configured,
one cursor plane and NUM_OVERLAY_PLANES planes to the default
configuration.

Co-developed-by: Louis Chauvet <louis.chauvet@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
Signed-off-by: José Expósito <jose.exposito89@gmail.com>
---
 .clang-format                                 |   1 +
 drivers/gpu/drm/vkms/tests/vkms_config_test.c | 140 +++++++++++++++++-
 drivers/gpu/drm/vkms/vkms_config.c            | 124 +++++++++++++++-
 drivers/gpu/drm/vkms/vkms_config.h            |  73 ++++++++-
 drivers/gpu/drm/vkms/vkms_output.c            |  42 ++----
 5 files changed, 344 insertions(+), 36 deletions(-)

diff --git a/.clang-format b/.clang-format
index fe1aa1a30d40..c585d2a5b395 100644
--- a/.clang-format
+++ b/.clang-format
@@ -690,6 +690,7 @@ ForEachMacros:
   - 'v4l2_m2m_for_each_src_buf'
   - 'v4l2_m2m_for_each_src_buf_safe'
   - 'virtio_device_for_each_vq'
+  - 'vkms_config_for_each_plane'
   - 'while_for_each_ftrace_op'
   - 'xa_for_each'
   - 'xa_for_each_marked'
diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
index 6e07139d261c..38ec456d9610 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_config_test.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
@@ -24,6 +24,10 @@ static void vkms_config_test_empty_config(struct kunit *test)
 	dev_name = NULL;
 	KUNIT_EXPECT_STREQ(test, vkms_config_get_device_name(config), "test");
 
+	KUNIT_EXPECT_TRUE(test, list_empty(&config->planes));
+
+	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
+
 	vkms_config_destroy(config);
 }
 
@@ -44,16 +48,145 @@ static void vkms_config_test_default_config(struct kunit *test)
 {
 	const struct default_config_case *params = test->param_value;
 	struct vkms_config *config;
+	struct vkms_config_plane *plane_cfg;
+	int n_primaries = 0;
+	int n_cursors = 0;
+	int n_overlays = 0;
 
 	config = vkms_config_default_create(params->enable_cursor,
 					    params->enable_writeback,
 					    params->enable_overlay);
 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
 
-	KUNIT_EXPECT_EQ(test, config->cursor, params->enable_cursor);
 	KUNIT_EXPECT_EQ(test, config->writeback, params->enable_writeback);
-	KUNIT_EXPECT_EQ(test, config->overlay, params->enable_overlay);
 
+	/* Planes */
+	list_for_each_entry(plane_cfg, &config->planes, link) {
+		switch (vkms_config_plane_get_type(plane_cfg)) {
+		case DRM_PLANE_TYPE_PRIMARY:
+			n_primaries++;
+			break;
+		case DRM_PLANE_TYPE_CURSOR:
+			n_cursors++;
+			break;
+		case DRM_PLANE_TYPE_OVERLAY:
+			n_overlays++;
+			break;
+		default:
+			KUNIT_FAIL_AND_ABORT(test, "Unknown plane type");
+		}
+	}
+	KUNIT_EXPECT_EQ(test, n_primaries, 1);
+	KUNIT_EXPECT_EQ(test, n_cursors, params->enable_cursor ? 1 : 0);
+	KUNIT_EXPECT_EQ(test, n_overlays, params->enable_overlay ? 8 : 0);
+
+	KUNIT_EXPECT_TRUE(test, vkms_config_is_valid(config));
+
+	vkms_config_destroy(config);
+}
+
+static void vkms_config_test_get_planes(struct kunit *test)
+{
+	struct vkms_config *config;
+	struct vkms_config_plane *plane_cfg;
+	struct vkms_config_plane *plane_cfg1, *plane_cfg2;
+	int n_planes = 0;
+
+	config = vkms_config_create("test");
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
+
+	vkms_config_for_each_plane(config, plane_cfg)
+		n_planes++;
+	KUNIT_ASSERT_EQ(test, n_planes, 0);
+
+	plane_cfg1 = vkms_config_create_plane(config);
+	vkms_config_for_each_plane(config, plane_cfg) {
+		n_planes++;
+		if (plane_cfg != plane_cfg1)
+			KUNIT_FAIL(test, "Unexpected plane");
+	}
+	KUNIT_ASSERT_EQ(test, n_planes, 1);
+	n_planes = 0;
+
+	plane_cfg2 = vkms_config_create_plane(config);
+	vkms_config_for_each_plane(config, plane_cfg) {
+		n_planes++;
+		if (plane_cfg != plane_cfg1 && plane_cfg != plane_cfg2)
+			KUNIT_FAIL(test, "Unexpected plane");
+	}
+	KUNIT_ASSERT_EQ(test, n_planes, 2);
+	n_planes = 0;
+
+	vkms_config_destroy_plane(plane_cfg1);
+	vkms_config_for_each_plane(config, plane_cfg) {
+		n_planes++;
+		if (plane_cfg != plane_cfg2)
+			KUNIT_FAIL(test, "Unexpected plane");
+	}
+	KUNIT_ASSERT_EQ(test, n_planes, 1);
+
+	vkms_config_destroy(config);
+}
+
+static void vkms_config_test_invalid_plane_number(struct kunit *test)
+{
+	struct vkms_config *config;
+	struct vkms_config_plane *plane_cfg;
+	int n;
+
+	config = vkms_config_default_create(false, false, false);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
+
+	/* Invalid: No planes */
+	plane_cfg = list_first_entry(&config->planes, typeof(*plane_cfg), link);
+	vkms_config_destroy_plane(plane_cfg);
+	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
+
+	/* Invalid: Too many planes */
+	for (n = 0; n <= 32; n++)
+		vkms_config_create_plane(config);
+
+	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
+
+	vkms_config_destroy(config);
+}
+
+static void vkms_config_test_valid_plane_type(struct kunit *test)
+{
+	struct vkms_config *config;
+	struct vkms_config_plane *plane_cfg;
+
+	config = vkms_config_default_create(false, false, false);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
+
+	plane_cfg = list_first_entry(&config->planes, typeof(*plane_cfg), link);
+	vkms_config_destroy_plane(plane_cfg);
+
+	/* Invalid: No primary plane */
+	plane_cfg = vkms_config_create_plane(config);
+	vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_OVERLAY);
+	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
+
+	/* Invalid: Multiple primary planes */
+	plane_cfg = vkms_config_create_plane(config);
+	vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_PRIMARY);
+	plane_cfg = vkms_config_create_plane(config);
+	vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_PRIMARY);
+	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
+
+	/* Valid: One primary plane */
+	vkms_config_destroy_plane(plane_cfg);
+	KUNIT_EXPECT_TRUE(test, vkms_config_is_valid(config));
+
+	/* Invalid: Multiple cursor planes */
+	plane_cfg = vkms_config_create_plane(config);
+	vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_CURSOR);
+	plane_cfg = vkms_config_create_plane(config);
+	vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_CURSOR);
+	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
+
+	/* Valid: One primary and one cursor plane */
+	vkms_config_destroy_plane(plane_cfg);
 	KUNIT_EXPECT_TRUE(test, vkms_config_is_valid(config));
 
 	vkms_config_destroy(config);
@@ -63,6 +196,9 @@ static struct kunit_case vkms_config_test_cases[] = {
 	KUNIT_CASE(vkms_config_test_empty_config),
 	KUNIT_CASE_PARAM(vkms_config_test_default_config,
 			 default_config_gen_params),
+	KUNIT_CASE(vkms_config_test_get_planes),
+	KUNIT_CASE(vkms_config_test_invalid_plane_number),
+	KUNIT_CASE(vkms_config_test_valid_plane_type),
 	{}
 };
 
diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
index 67f71d29596e..8feacfc155be 100644
--- a/drivers/gpu/drm/vkms/vkms_config.c
+++ b/drivers/gpu/drm/vkms/vkms_config.c
@@ -21,6 +21,8 @@ struct vkms_config *vkms_config_create(const char *dev_name)
 		return ERR_PTR(-ENOMEM);
 	}
 
+	INIT_LIST_HEAD(&config->planes);
+
 	return config;
 }
 
@@ -29,26 +31,114 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
 					       bool enable_overlay)
 {
 	struct vkms_config *config;
+	struct vkms_config_plane *plane_cfg;
+	int n;
 
 	config = vkms_config_create(DEFAULT_DEVICE_NAME);
 	if (IS_ERR(config))
 		return config;
 
-	config->cursor = enable_cursor;
 	config->writeback = enable_writeback;
-	config->overlay = enable_overlay;
+
+	plane_cfg = vkms_config_create_plane(config);
+	if (IS_ERR(plane_cfg))
+		goto err_alloc;
+	vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_PRIMARY);
+
+	if (enable_overlay) {
+		for (n = 0; n < NUM_OVERLAY_PLANES; n++) {
+			plane_cfg = vkms_config_create_plane(config);
+			if (IS_ERR(plane_cfg))
+				goto err_alloc;
+			vkms_config_plane_set_type(plane_cfg,
+						   DRM_PLANE_TYPE_OVERLAY);
+		}
+	}
+
+	if (enable_cursor) {
+		plane_cfg = vkms_config_create_plane(config);
+		if (IS_ERR(plane_cfg))
+			goto err_alloc;
+		vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_CURSOR);
+	}
 
 	return config;
+
+err_alloc:
+	vkms_config_destroy(config);
+	return ERR_PTR(-ENOMEM);
 }
 
 void vkms_config_destroy(struct vkms_config *config)
 {
+	struct vkms_config_plane *plane_cfg, *plane_tmp;
+
+	list_for_each_entry_safe(plane_cfg, plane_tmp, &config->planes, link)
+		vkms_config_destroy_plane(plane_cfg);
+
 	kfree_const(config->dev_name);
 	kfree(config);
 }
 
+static bool valid_plane_number(struct vkms_config *config)
+{
+	struct drm_device *dev = &config->dev->drm;
+	size_t n_planes;
+
+	n_planes = list_count_nodes(&config->planes);
+	if (n_planes <= 0 || n_planes >= 32) {
+		drm_info(dev, "The number of planes must be between 1 and 31\n");
+		return false;
+	}
+
+	return true;
+}
+
+static bool valid_plane_type(struct vkms_config *config)
+{
+	struct drm_device *dev = &config->dev->drm;
+	struct vkms_config_plane *plane_cfg;
+	bool has_primary_plane = false;
+	bool has_cursor_plane = false;
+
+	vkms_config_for_each_plane(config, plane_cfg) {
+		enum drm_plane_type type;
+
+		type = vkms_config_plane_get_type(plane_cfg);
+
+		if (type == DRM_PLANE_TYPE_PRIMARY) {
+			if (has_primary_plane) {
+				drm_info(dev, "Multiple primary planes\n");
+				return false;
+			}
+
+			has_primary_plane = true;
+		} else if (type == DRM_PLANE_TYPE_CURSOR) {
+			if (has_cursor_plane) {
+				drm_info(dev, "Multiple cursor planes\n");
+				return false;
+			}
+
+			has_cursor_plane = true;
+		}
+	}
+
+	if (!has_primary_plane) {
+		drm_info(dev, "Primary plane not found\n");
+		return false;
+	}
+
+	return true;
+}
+
 bool vkms_config_is_valid(struct vkms_config *config)
 {
+	if (!valid_plane_number(config))
+		return false;
+
+	if (!valid_plane_type(config))
+		return false;
+
 	return true;
 }
 
@@ -58,12 +148,17 @@ static int vkms_config_show(struct seq_file *m, void *data)
 	struct drm_device *dev = entry->dev;
 	struct vkms_device *vkmsdev = drm_device_to_vkms_device(dev);
 	const char *dev_name;
+	struct vkms_config_plane *plane_cfg;
 
 	dev_name = vkms_config_get_device_name((struct vkms_config *)vkmsdev->config);
 	seq_printf(m, "dev_name=%s\n", dev_name);
 	seq_printf(m, "writeback=%d\n", vkmsdev->config->writeback);
-	seq_printf(m, "cursor=%d\n", vkmsdev->config->cursor);
-	seq_printf(m, "overlay=%d\n", vkmsdev->config->overlay);
+
+	vkms_config_for_each_plane(vkmsdev->config, plane_cfg) {
+		seq_puts(m, "plane:\n");
+		seq_printf(m, "\ttype=%d\n",
+			   vkms_config_plane_get_type(plane_cfg));
+	}
 
 	return 0;
 }
@@ -77,3 +172,24 @@ void vkms_config_register_debugfs(struct vkms_device *vkms_device)
 	drm_debugfs_add_files(&vkms_device->drm, vkms_config_debugfs_list,
 			      ARRAY_SIZE(vkms_config_debugfs_list));
 }
+
+struct vkms_config_plane *vkms_config_create_plane(struct vkms_config *config)
+{
+	struct vkms_config_plane *plane_cfg;
+
+	plane_cfg = kzalloc(sizeof(*plane_cfg), GFP_KERNEL);
+	if (!plane_cfg)
+		return ERR_PTR(-ENOMEM);
+
+	vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_OVERLAY);
+
+	list_add_tail(&plane_cfg->link, &config->planes);
+
+	return plane_cfg;
+}
+
+void vkms_config_destroy_plane(struct vkms_config_plane *plane_cfg)
+{
+	list_del(&plane_cfg->link);
+	kfree(plane_cfg);
+}
diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
index 0376dceaf6be..1c7ffc27f2a8 100644
--- a/drivers/gpu/drm/vkms/vkms_config.h
+++ b/drivers/gpu/drm/vkms/vkms_config.h
@@ -3,6 +3,7 @@
 #ifndef _VKMS_CONFIG_H_
 #define _VKMS_CONFIG_H_
 
+#include <linux/list.h>
 #include <linux/types.h>
 
 #include "vkms_drv.h"
@@ -12,18 +13,44 @@
  *
  * @dev_name: Name of the device
  * @writeback: If true, a writeback buffer can be attached to the CRTC
- * @cursor: If true, a cursor plane is created in the VKMS device
- * @overlay: If true, NUM_OVERLAY_PLANES will be created for the VKMS device
+ * @planes: List of planes configured for the device
  * @dev: Used to store the current VKMS device. Only set when the device is instantiated.
  */
 struct vkms_config {
 	const char *dev_name;
 	bool writeback;
-	bool cursor;
-	bool overlay;
+	struct list_head planes;
 	struct vkms_device *dev;
 };
 
+/**
+ * struct vkms_config_plane
+ *
+ * @link: Link to the others planes in vkms_config
+ * @type: Type of the plane. The creator of configuration needs to ensures that
+ *        at least one primary plane is present.
+ * @plane: Internal usage. This pointer should never be considered as valid.
+ *         It can be used to store a temporary reference to a VKMS plane during
+ *         device creation. This pointer is not managed by the configuration and
+ *         must be managed by other means.
+ */
+struct vkms_config_plane {
+	struct list_head link;
+
+	enum drm_plane_type type;
+
+	/* Internal usage */
+	struct vkms_plane *plane;
+};
+
+/**
+ * vkms_config_for_each_plane - Iterate over the vkms_config planes
+ * @config: &struct vkms_config pointer
+ * @plane_cfg: &struct vkms_config_plane pointer used as cursor
+ */
+#define vkms_config_for_each_plane(config, plane_cfg) \
+	list_for_each_entry((plane_cfg), &(config)->planes, link)
+
 /**
  * vkms_config_create() - Create a new VKMS configuration
  * @dev_name: Name of the device
@@ -84,4 +111,42 @@ bool vkms_config_is_valid(struct vkms_config *config);
  */
 void vkms_config_register_debugfs(struct vkms_device *vkms_device);
 
+/**
+ * vkms_config_create_plane() - Add a new plane configuration
+ * @config: Configuration to add the plane to
+ *
+ * Returns:
+ * The new plane configuration or an error. Call vkms_config_destroy_plane() to
+ * free the returned plane configuration.
+ */
+struct vkms_config_plane *vkms_config_create_plane(struct vkms_config *config);
+
+/**
+ * vkms_config_destroy_plane() - Remove and free a plane configuration
+ * @plane_cfg: Plane configuration to destroy
+ */
+void vkms_config_destroy_plane(struct vkms_config_plane *plane_cfg);
+
+/**
+ * vkms_config_plane_type() - Return the plane type
+ * @plane_cfg: Plane to get the type from
+ */
+static inline enum drm_plane_type
+vkms_config_plane_get_type(struct vkms_config_plane *plane_cfg)
+{
+	return plane_cfg->type;
+}
+
+/**
+ * vkms_config_plane_set_type() - Set the plane type
+ * @plane_cfg: Plane to set the type to
+ * @type: New plane type
+ */
+static inline void
+vkms_config_plane_set_type(struct vkms_config_plane *plane_cfg,
+			   enum drm_plane_type type)
+{
+	plane_cfg->type = type;
+}
+
 #endif /* _VKMS_CONFIG_H_ */
diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
index 414cc933af41..08ea691db299 100644
--- a/drivers/gpu/drm/vkms/vkms_output.c
+++ b/drivers/gpu/drm/vkms/vkms_output.c
@@ -11,28 +11,29 @@ int vkms_output_init(struct vkms_device *vkmsdev)
 	struct vkms_connector *connector;
 	struct drm_encoder *encoder;
 	struct vkms_output *output;
-	struct vkms_plane *primary, *overlay, *cursor = NULL;
+	struct vkms_plane *primary = NULL, *cursor = NULL;
+	struct vkms_config_plane *plane_cfg;
 	int ret;
 	int writeback;
-	unsigned int n;
 
 	if (!vkms_config_is_valid(vkmsdev->config))
 		return -EINVAL;
 
-	/*
-	 * Initialize used plane. One primary plane is required to perform the composition.
-	 *
-	 * The overlay and cursor planes are not mandatory, but can be used to perform complex
-	 * composition.
-	 */
-	primary = vkms_plane_init(vkmsdev, DRM_PLANE_TYPE_PRIMARY);
-	if (IS_ERR(primary))
-		return PTR_ERR(primary);
+	vkms_config_for_each_plane(vkmsdev->config, plane_cfg) {
+		enum drm_plane_type type;
 
-	if (vkmsdev->config->cursor) {
-		cursor = vkms_plane_init(vkmsdev, DRM_PLANE_TYPE_CURSOR);
-		if (IS_ERR(cursor))
-			return PTR_ERR(cursor);
+		type = vkms_config_plane_get_type(plane_cfg);
+
+		plane_cfg->plane = vkms_plane_init(vkmsdev, type);
+		if (IS_ERR(plane_cfg->plane)) {
+			DRM_DEV_ERROR(dev->dev, "Failed to init vkms plane\n");
+			return PTR_ERR(plane_cfg->plane);
+		}
+
+		if (type == DRM_PLANE_TYPE_PRIMARY)
+			primary = plane_cfg->plane;
+		else if (type == DRM_PLANE_TYPE_CURSOR)
+			cursor = plane_cfg->plane;
 	}
 
 	output = vkms_crtc_init(dev, &primary->base,
@@ -42,17 +43,6 @@ int vkms_output_init(struct vkms_device *vkmsdev)
 		return PTR_ERR(output);
 	}
 
-	if (vkmsdev->config->overlay) {
-		for (n = 0; n < NUM_OVERLAY_PLANES; n++) {
-			overlay = vkms_plane_init(vkmsdev, DRM_PLANE_TYPE_OVERLAY);
-			if (IS_ERR(overlay)) {
-				DRM_DEV_ERROR(dev->dev, "Failed to init vkms plane\n");
-				return PTR_ERR(overlay);
-			}
-			overlay->base.possible_crtcs = drm_crtc_mask(&output->crtc);
-		}
-	}
-
 	connector = vkms_connector_init(vkmsdev);
 	if (IS_ERR(connector)) {
 		DRM_ERROR("Failed to init connector\n");
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 10/15] drm/vkms: Allow to configure multiple CRTCs
  2025-02-11 11:08 [PATCH v2 00/15] drm/vkms: Allow to configure device José Expósito
                   ` (8 preceding siblings ...)
  2025-02-11 11:09 ` [PATCH v2 09/15] drm/vkms: Allow to configure multiple planes José Expósito
@ 2025-02-11 11:09 ` José Expósito
  2025-02-13 13:59   ` Louis Chauvet
  2025-02-11 11:09 ` [PATCH v2 11/15] drm/vkms: Allow to attach planes and CRTCs José Expósito
                   ` (4 subsequent siblings)
  14 siblings, 1 reply; 32+ messages in thread
From: José Expósito @ 2025-02-11 11:09 UTC (permalink / raw)
  To: louis.chauvet
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel,
	José Expósito

Add a list of CRTCs to vkms_config and helper functions to add and
remove as many CRTCs as wanted.

For backwards compatibility, add one CRTC to the default configuration.

A future patch will allow to attach planes and CRTCs, but for the
moment there are no changes in the way the output is configured.

Co-developed-by: Louis Chauvet <louis.chauvet@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
Signed-off-by: José Expósito <jose.exposito89@gmail.com>
---
 .clang-format                                 |  1 +
 drivers/gpu/drm/vkms/tests/vkms_config_test.c | 73 ++++++++++++++++-
 drivers/gpu/drm/vkms/vkms_config.c            | 60 +++++++++++++-
 drivers/gpu/drm/vkms/vkms_config.h            | 78 +++++++++++++++++++
 drivers/gpu/drm/vkms/vkms_drv.c               |  3 +-
 5 files changed, 209 insertions(+), 6 deletions(-)

diff --git a/.clang-format b/.clang-format
index c585d2a5b395..e7a901c3617d 100644
--- a/.clang-format
+++ b/.clang-format
@@ -690,6 +690,7 @@ ForEachMacros:
   - 'v4l2_m2m_for_each_src_buf'
   - 'v4l2_m2m_for_each_src_buf_safe'
   - 'virtio_device_for_each_vq'
+  - 'vkms_config_for_each_crtc'
   - 'vkms_config_for_each_plane'
   - 'while_for_each_ftrace_op'
   - 'xa_for_each'
diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
index 38ec456d9610..59bd7baea923 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_config_test.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
@@ -25,6 +25,7 @@ static void vkms_config_test_empty_config(struct kunit *test)
 	KUNIT_EXPECT_STREQ(test, vkms_config_get_device_name(config), "test");
 
 	KUNIT_EXPECT_TRUE(test, list_empty(&config->planes));
+	KUNIT_EXPECT_TRUE(test, list_empty(&config->crtcs));
 
 	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
 
@@ -49,6 +50,7 @@ static void vkms_config_test_default_config(struct kunit *test)
 	const struct default_config_case *params = test->param_value;
 	struct vkms_config *config;
 	struct vkms_config_plane *plane_cfg;
+	struct vkms_config_crtc *crtc_cfg;
 	int n_primaries = 0;
 	int n_cursors = 0;
 	int n_overlays = 0;
@@ -58,8 +60,6 @@ static void vkms_config_test_default_config(struct kunit *test)
 					    params->enable_overlay);
 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
 
-	KUNIT_EXPECT_EQ(test, config->writeback, params->enable_writeback);
-
 	/* Planes */
 	list_for_each_entry(plane_cfg, &config->planes, link) {
 		switch (vkms_config_plane_get_type(plane_cfg)) {
@@ -80,6 +80,13 @@ static void vkms_config_test_default_config(struct kunit *test)
 	KUNIT_EXPECT_EQ(test, n_cursors, params->enable_cursor ? 1 : 0);
 	KUNIT_EXPECT_EQ(test, n_overlays, params->enable_overlay ? 8 : 0);
 
+	/* CRTCs */
+	crtc_cfg = list_first_entry(&config->crtcs, typeof(*crtc_cfg), link);
+
+	KUNIT_EXPECT_EQ(test, list_count_nodes(&config->crtcs), 1);
+	KUNIT_EXPECT_EQ(test, vkms_config_crtc_get_writeback(crtc_cfg),
+			params->enable_writeback);
+
 	KUNIT_EXPECT_TRUE(test, vkms_config_is_valid(config));
 
 	vkms_config_destroy(config);
@@ -128,6 +135,43 @@ static void vkms_config_test_get_planes(struct kunit *test)
 	vkms_config_destroy(config);
 }
 
+static void vkms_config_test_get_crtcs(struct kunit *test)
+{
+	struct vkms_config *config;
+	struct vkms_config_crtc *crtc_cfg;
+	struct vkms_config_crtc *crtc_cfg1, *crtc_cfg2;
+
+	config = vkms_config_create("test");
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
+
+	KUNIT_ASSERT_EQ(test, vkms_config_get_num_crtcs(config), 0);
+	vkms_config_for_each_crtc(config, crtc_cfg)
+		KUNIT_FAIL(test, "Unexpected CRTC");
+
+	crtc_cfg1 = vkms_config_create_crtc(config);
+	KUNIT_ASSERT_EQ(test, vkms_config_get_num_crtcs(config), 1);
+	vkms_config_for_each_crtc(config, crtc_cfg) {
+		if (crtc_cfg != crtc_cfg1)
+			KUNIT_FAIL(test, "Unexpected CRTC");
+	}
+
+	crtc_cfg2 = vkms_config_create_crtc(config);
+	KUNIT_ASSERT_EQ(test, vkms_config_get_num_crtcs(config), 2);
+	vkms_config_for_each_crtc(config, crtc_cfg) {
+		if (crtc_cfg != crtc_cfg1 && crtc_cfg != crtc_cfg2)
+			KUNIT_FAIL(test, "Unexpected CRTC");
+	}
+
+	vkms_config_destroy_crtc(config, crtc_cfg2);
+	KUNIT_ASSERT_EQ(test, vkms_config_get_num_crtcs(config), 1);
+	vkms_config_for_each_crtc(config, crtc_cfg) {
+		if (crtc_cfg != crtc_cfg1)
+			KUNIT_FAIL(test, "Unexpected CRTC");
+	}
+
+	vkms_config_destroy(config);
+}
+
 static void vkms_config_test_invalid_plane_number(struct kunit *test)
 {
 	struct vkms_config *config;
@@ -192,13 +236,38 @@ static void vkms_config_test_valid_plane_type(struct kunit *test)
 	vkms_config_destroy(config);
 }
 
+static void vkms_config_test_invalid_crtc_number(struct kunit *test)
+{
+	struct vkms_config *config;
+	struct vkms_config_crtc *crtc_cfg;
+	int n;
+
+	config = vkms_config_default_create(false, false, false);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
+
+	/* Invalid: No CRTCs */
+	crtc_cfg = list_first_entry(&config->crtcs, typeof(*crtc_cfg), link);
+	vkms_config_destroy_crtc(config, crtc_cfg);
+	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
+
+	/* Invalid: Too many CRTCs */
+	for (n = 0; n <= 32; n++)
+		vkms_config_create_crtc(config);
+
+	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
+
+	vkms_config_destroy(config);
+}
+
 static struct kunit_case vkms_config_test_cases[] = {
 	KUNIT_CASE(vkms_config_test_empty_config),
 	KUNIT_CASE_PARAM(vkms_config_test_default_config,
 			 default_config_gen_params),
 	KUNIT_CASE(vkms_config_test_get_planes),
+	KUNIT_CASE(vkms_config_test_get_crtcs),
 	KUNIT_CASE(vkms_config_test_invalid_plane_number),
 	KUNIT_CASE(vkms_config_test_valid_plane_type),
+	KUNIT_CASE(vkms_config_test_invalid_crtc_number),
 	{}
 };
 
diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
index 8feacfc155be..b08021e62c9f 100644
--- a/drivers/gpu/drm/vkms/vkms_config.c
+++ b/drivers/gpu/drm/vkms/vkms_config.c
@@ -22,6 +22,7 @@ struct vkms_config *vkms_config_create(const char *dev_name)
 	}
 
 	INIT_LIST_HEAD(&config->planes);
+	INIT_LIST_HEAD(&config->crtcs);
 
 	return config;
 }
@@ -32,19 +33,23 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
 {
 	struct vkms_config *config;
 	struct vkms_config_plane *plane_cfg;
+	struct vkms_config_crtc *crtc_cfg;
 	int n;
 
 	config = vkms_config_create(DEFAULT_DEVICE_NAME);
 	if (IS_ERR(config))
 		return config;
 
-	config->writeback = enable_writeback;
-
 	plane_cfg = vkms_config_create_plane(config);
 	if (IS_ERR(plane_cfg))
 		goto err_alloc;
 	vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_PRIMARY);
 
+	crtc_cfg = vkms_config_create_crtc(config);
+	if (IS_ERR(crtc_cfg))
+		goto err_alloc;
+	vkms_config_crtc_set_writeback(crtc_cfg, enable_writeback);
+
 	if (enable_overlay) {
 		for (n = 0; n < NUM_OVERLAY_PLANES; n++) {
 			plane_cfg = vkms_config_create_plane(config);
@@ -72,10 +77,14 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
 void vkms_config_destroy(struct vkms_config *config)
 {
 	struct vkms_config_plane *plane_cfg, *plane_tmp;
+	struct vkms_config_crtc *crtc_cfg, *crtc_tmp;
 
 	list_for_each_entry_safe(plane_cfg, plane_tmp, &config->planes, link)
 		vkms_config_destroy_plane(plane_cfg);
 
+	list_for_each_entry_safe(crtc_cfg, crtc_tmp, &config->crtcs, link)
+		vkms_config_destroy_crtc(config, crtc_cfg);
+
 	kfree_const(config->dev_name);
 	kfree(config);
 }
@@ -131,11 +140,28 @@ static bool valid_plane_type(struct vkms_config *config)
 	return true;
 }
 
+static bool valid_crtc_number(struct vkms_config *config)
+{
+	struct drm_device *dev = &config->dev->drm;
+	size_t n_crtcs;
+
+	n_crtcs = list_count_nodes(&config->crtcs);
+	if (n_crtcs <= 0 || n_crtcs >= 32) {
+		drm_info(dev, "The number of CRTCs must be between 1 and 31\n");
+		return false;
+	}
+
+	return true;
+}
+
 bool vkms_config_is_valid(struct vkms_config *config)
 {
 	if (!valid_plane_number(config))
 		return false;
 
+	if (!valid_crtc_number(config))
+		return false;
+
 	if (!valid_plane_type(config))
 		return false;
 
@@ -149,10 +175,10 @@ static int vkms_config_show(struct seq_file *m, void *data)
 	struct vkms_device *vkmsdev = drm_device_to_vkms_device(dev);
 	const char *dev_name;
 	struct vkms_config_plane *plane_cfg;
+	struct vkms_config_crtc *crtc_cfg;
 
 	dev_name = vkms_config_get_device_name((struct vkms_config *)vkmsdev->config);
 	seq_printf(m, "dev_name=%s\n", dev_name);
-	seq_printf(m, "writeback=%d\n", vkmsdev->config->writeback);
 
 	vkms_config_for_each_plane(vkmsdev->config, plane_cfg) {
 		seq_puts(m, "plane:\n");
@@ -160,6 +186,12 @@ static int vkms_config_show(struct seq_file *m, void *data)
 			   vkms_config_plane_get_type(plane_cfg));
 	}
 
+	vkms_config_for_each_crtc(vkmsdev->config, crtc_cfg) {
+		seq_puts(m, "crtc:\n");
+		seq_printf(m, "\twriteback=%d\n",
+			   vkms_config_crtc_get_writeback(crtc_cfg));
+	}
+
 	return 0;
 }
 
@@ -193,3 +225,25 @@ void vkms_config_destroy_plane(struct vkms_config_plane *plane_cfg)
 	list_del(&plane_cfg->link);
 	kfree(plane_cfg);
 }
+
+struct vkms_config_crtc *vkms_config_create_crtc(struct vkms_config *config)
+{
+	struct vkms_config_crtc *crtc_cfg;
+
+	crtc_cfg = kzalloc(sizeof(*crtc_cfg), GFP_KERNEL);
+	if (!crtc_cfg)
+		return ERR_PTR(-ENOMEM);
+
+	vkms_config_crtc_set_writeback(crtc_cfg, false);
+
+	list_add_tail(&crtc_cfg->link, &config->crtcs);
+
+	return crtc_cfg;
+}
+
+void vkms_config_destroy_crtc(struct vkms_config *config,
+			      struct vkms_config_crtc *crtc_cfg)
+{
+	list_del(&crtc_cfg->link);
+	kfree(crtc_cfg);
+}
diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
index 1c7ffc27f2a8..0f5937b44948 100644
--- a/drivers/gpu/drm/vkms/vkms_config.h
+++ b/drivers/gpu/drm/vkms/vkms_config.h
@@ -14,12 +14,14 @@
  * @dev_name: Name of the device
  * @writeback: If true, a writeback buffer can be attached to the CRTC
  * @planes: List of planes configured for the device
+ * @crtcs: List of CRTCs configured for the device
  * @dev: Used to store the current VKMS device. Only set when the device is instantiated.
  */
 struct vkms_config {
 	const char *dev_name;
 	bool writeback;
 	struct list_head planes;
+	struct list_head crtcs;
 	struct vkms_device *dev;
 };
 
@@ -43,6 +45,25 @@ struct vkms_config_plane {
 	struct vkms_plane *plane;
 };
 
+/**
+ * struct vkms_config_crtc
+ *
+ * @link: Link to the others CRTCs in vkms_config
+ * @writeback: If true, a writeback buffer can be attached to the CRTC
+ * @crtc: Internal usage. This pointer should never be considered as valid.
+ *        It can be used to store a temporary reference to a VKMS CRTC during
+ *        device creation. This pointer is not managed by the configuration and
+ *        must be managed by other means.
+ */
+struct vkms_config_crtc {
+	struct list_head link;
+
+	bool writeback;
+
+	/* Internal usage */
+	struct vkms_output *crtc;
+};
+
 /**
  * vkms_config_for_each_plane - Iterate over the vkms_config planes
  * @config: &struct vkms_config pointer
@@ -51,6 +72,14 @@ struct vkms_config_plane {
 #define vkms_config_for_each_plane(config, plane_cfg) \
 	list_for_each_entry((plane_cfg), &(config)->planes, link)
 
+/**
+ * vkms_config_for_each_crtc - Iterate over the vkms_config CRTCs
+ * @config: &struct vkms_config pointer
+ * @crtc_cfg: &struct vkms_config_crtc pointer used as cursor
+ */
+#define vkms_config_for_each_crtc(config, crtc_cfg) \
+	list_for_each_entry((crtc_cfg), &(config)->crtcs, link)
+
 /**
  * vkms_config_create() - Create a new VKMS configuration
  * @dev_name: Name of the device
@@ -94,6 +123,15 @@ vkms_config_get_device_name(struct vkms_config *config)
 	return config->dev_name;
 }
 
+/**
+ * vkms_config_get_num_crtcs() - Return the number of CRTCs in the configuration
+ * @config: Configuration to get the number of CRTCs from
+ */
+static inline size_t vkms_config_get_num_crtcs(struct vkms_config *config)
+{
+	return list_count_nodes(&config->crtcs);
+}
+
 /**
  * vkms_config_is_valid() - Validate a configuration
  * @config: Configuration to validate
@@ -149,4 +187,44 @@ vkms_config_plane_set_type(struct vkms_config_plane *plane_cfg,
 	plane_cfg->type = type;
 }
 
+/**
+ * vkms_config_create_crtc() - Add a new CRTC configuration
+ * @config: Configuration to add the CRTC to
+ *
+ * Returns:
+ * The new CRTC configuration or an error. Call vkms_config_destroy_crtc() to
+ * free the returned CRTC configuration.
+ */
+struct vkms_config_crtc *vkms_config_create_crtc(struct vkms_config *config);
+
+/**
+ * vkms_config_destroy_crtc() - Remove and free a CRTC configuration
+ * @config: Configuration to remove the CRTC from
+ * @crtc_cfg: CRTC configuration to destroy
+ */
+void vkms_config_destroy_crtc(struct vkms_config *config,
+			      struct vkms_config_crtc *crtc_cfg);
+
+/**
+ * vkms_config_crtc_get_writeback() - If a writeback connector will be created
+ * @crtc_cfg: CRTC with or without a writeback connector
+ */
+static inline bool
+vkms_config_crtc_get_writeback(struct vkms_config_crtc *crtc_cfg)
+{
+	return crtc_cfg->writeback;
+}
+
+/**
+ * vkms_config_crtc_set_writeback() - If a writeback connector will be created
+ * @crtc_cfg: Target CRTC
+ * @writeback: Enable or disable the writeback connector
+ */
+static inline void
+vkms_config_crtc_set_writeback(struct vkms_config_crtc *crtc_cfg,
+			       bool writeback)
+{
+	crtc_cfg->writeback = writeback;
+}
+
 #endif /* _VKMS_CONFIG_H_ */
diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c
index ba977ef09b2b..a24d1655f7b8 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.c
+++ b/drivers/gpu/drm/vkms/vkms_drv.c
@@ -181,7 +181,8 @@ static int vkms_create(struct vkms_config *config)
 		goto out_devres;
 	}
 
-	ret = drm_vblank_init(&vkms_device->drm, 1);
+	ret = drm_vblank_init(&vkms_device->drm,
+			      vkms_config_get_num_crtcs(config));
 	if (ret) {
 		DRM_ERROR("Failed to vblank\n");
 		goto out_devres;
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 11/15] drm/vkms: Allow to attach planes and CRTCs
  2025-02-11 11:08 [PATCH v2 00/15] drm/vkms: Allow to configure device José Expósito
                   ` (9 preceding siblings ...)
  2025-02-11 11:09 ` [PATCH v2 10/15] drm/vkms: Allow to configure multiple CRTCs José Expósito
@ 2025-02-11 11:09 ` José Expósito
  2025-02-11 11:09 ` [PATCH v2 12/15] drm/vkms: Allow to configure multiple encoders José Expósito
                   ` (3 subsequent siblings)
  14 siblings, 0 replies; 32+ messages in thread
From: José Expósito @ 2025-02-11 11:09 UTC (permalink / raw)
  To: louis.chauvet
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel,
	José Expósito

Add a list of possible CRTCs to the plane configuration and helpers to
attach, detach and get the primary and cursor planes attached to a CRTC.

Now that the default configuration has its planes and CRTC correctly
attached, configure the output following the configuration.

Co-developed-by: Louis Chauvet <louis.chauvet@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
Signed-off-by: José Expósito <jose.exposito89@gmail.com>
---
 .clang-format                                 |   1 +
 drivers/gpu/drm/vkms/tests/vkms_config_test.c | 188 ++++++++++++++++++
 drivers/gpu/drm/vkms/vkms_config.c            | 147 ++++++++++++--
 drivers/gpu/drm/vkms/vkms_config.h            |  59 +++++-
 drivers/gpu/drm/vkms/vkms_output.c            |  51 +++--
 5 files changed, 411 insertions(+), 35 deletions(-)

diff --git a/.clang-format b/.clang-format
index e7a901c3617d..6f944fa39841 100644
--- a/.clang-format
+++ b/.clang-format
@@ -692,6 +692,7 @@ ForEachMacros:
   - 'virtio_device_for_each_vq'
   - 'vkms_config_for_each_crtc'
   - 'vkms_config_for_each_plane'
+  - 'vkms_config_plane_for_each_possible_crtc'
   - 'while_for_each_ftrace_op'
   - 'xa_for_each'
   - 'xa_for_each_marked'
diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
index 59bd7baea923..505eebb07a52 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_config_test.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
@@ -87,6 +87,18 @@ static void vkms_config_test_default_config(struct kunit *test)
 	KUNIT_EXPECT_EQ(test, vkms_config_crtc_get_writeback(crtc_cfg),
 			params->enable_writeback);
 
+	list_for_each_entry(plane_cfg, &config->planes, link) {
+		struct vkms_config_crtc *possible_crtc;
+		int n_possible_crtcs = 0;
+		unsigned long idx = 0;
+
+		xa_for_each(&plane_cfg->possible_crtcs, idx, possible_crtc) {
+			KUNIT_EXPECT_PTR_EQ(test, crtc_cfg, possible_crtc);
+			n_possible_crtcs++;
+		}
+		KUNIT_EXPECT_EQ(test, n_possible_crtcs, 1);
+	}
+
 	KUNIT_EXPECT_TRUE(test, vkms_config_is_valid(config));
 
 	vkms_config_destroy(config);
@@ -199,6 +211,8 @@ static void vkms_config_test_valid_plane_type(struct kunit *test)
 {
 	struct vkms_config *config;
 	struct vkms_config_plane *plane_cfg;
+	struct vkms_config_crtc *crtc_cfg;
+	int err;
 
 	config = vkms_config_default_create(false, false, false);
 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
@@ -206,16 +220,26 @@ static void vkms_config_test_valid_plane_type(struct kunit *test)
 	plane_cfg = list_first_entry(&config->planes, typeof(*plane_cfg), link);
 	vkms_config_destroy_plane(plane_cfg);
 
+	crtc_cfg = list_first_entry(&config->crtcs, typeof(*crtc_cfg), link);
+
 	/* Invalid: No primary plane */
 	plane_cfg = vkms_config_create_plane(config);
 	vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_OVERLAY);
+	err = vkms_config_plane_attach_crtc(plane_cfg, crtc_cfg);
+	KUNIT_EXPECT_EQ(test, err, 0);
 	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
 
 	/* Invalid: Multiple primary planes */
 	plane_cfg = vkms_config_create_plane(config);
 	vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_PRIMARY);
+	err = vkms_config_plane_attach_crtc(plane_cfg, crtc_cfg);
+	KUNIT_EXPECT_EQ(test, err, 0);
+
 	plane_cfg = vkms_config_create_plane(config);
 	vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_PRIMARY);
+	err = vkms_config_plane_attach_crtc(plane_cfg, crtc_cfg);
+	KUNIT_EXPECT_EQ(test, err, 0);
+
 	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
 
 	/* Valid: One primary plane */
@@ -225,14 +249,50 @@ static void vkms_config_test_valid_plane_type(struct kunit *test)
 	/* Invalid: Multiple cursor planes */
 	plane_cfg = vkms_config_create_plane(config);
 	vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_CURSOR);
+	err = vkms_config_plane_attach_crtc(plane_cfg, crtc_cfg);
+	KUNIT_EXPECT_EQ(test, err, 0);
+
 	plane_cfg = vkms_config_create_plane(config);
 	vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_CURSOR);
+	err = vkms_config_plane_attach_crtc(plane_cfg, crtc_cfg);
+	KUNIT_EXPECT_EQ(test, err, 0);
+
 	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
 
 	/* Valid: One primary and one cursor plane */
 	vkms_config_destroy_plane(plane_cfg);
 	KUNIT_EXPECT_TRUE(test, vkms_config_is_valid(config));
 
+	/* Invalid: Second CRTC without primary plane */
+	crtc_cfg = vkms_config_create_crtc(config);
+	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
+
+	/* Valid: Second CRTC with a primary plane */
+	plane_cfg = vkms_config_create_plane(config);
+	vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_PRIMARY);
+	err = vkms_config_plane_attach_crtc(plane_cfg, crtc_cfg);
+	KUNIT_EXPECT_EQ(test, err, 0);
+	KUNIT_EXPECT_TRUE(test, vkms_config_is_valid(config));
+
+	vkms_config_destroy(config);
+}
+
+static void vkms_config_test_valid_plane_possible_crtcs(struct kunit *test)
+{
+	struct vkms_config *config;
+	struct vkms_config_plane *plane_cfg;
+	struct vkms_config_crtc *crtc_cfg;
+
+	config = vkms_config_default_create(false, false, false);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
+
+	plane_cfg = list_first_entry(&config->planes, typeof(*plane_cfg), link);
+	crtc_cfg = list_first_entry(&config->crtcs, typeof(*crtc_cfg), link);
+
+	/* Invalid: Primary plane without a possible CRTC */
+	vkms_config_plane_detach_crtc(plane_cfg, crtc_cfg);
+	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
+
 	vkms_config_destroy(config);
 }
 
@@ -259,6 +319,131 @@ static void vkms_config_test_invalid_crtc_number(struct kunit *test)
 	vkms_config_destroy(config);
 }
 
+static void vkms_config_test_plane_attach_crtc(struct kunit *test)
+{
+	struct vkms_config *config;
+	struct vkms_config_plane *overlay_cfg;
+	struct vkms_config_plane *primary_cfg;
+	struct vkms_config_plane *cursor_cfg;
+	struct vkms_config_crtc *crtc_cfg;
+	int err;
+
+	config = vkms_config_create("test");
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
+
+	overlay_cfg = vkms_config_create_plane(config);
+	vkms_config_plane_set_type(overlay_cfg, DRM_PLANE_TYPE_OVERLAY);
+	primary_cfg = vkms_config_create_plane(config);
+	vkms_config_plane_set_type(primary_cfg, DRM_PLANE_TYPE_PRIMARY);
+	cursor_cfg = vkms_config_create_plane(config);
+	vkms_config_plane_set_type(cursor_cfg, DRM_PLANE_TYPE_CURSOR);
+
+	crtc_cfg = vkms_config_create_crtc(config);
+
+	/* No primary or cursor planes */
+	KUNIT_EXPECT_NULL(test, vkms_config_crtc_primary_plane(config, crtc_cfg));
+	KUNIT_EXPECT_NULL(test, vkms_config_crtc_cursor_plane(config, crtc_cfg));
+
+	/* Overlay plane, but no primary or cursor planes */
+	err = vkms_config_plane_attach_crtc(overlay_cfg, crtc_cfg);
+	KUNIT_EXPECT_EQ(test, err, 0);
+	KUNIT_EXPECT_NULL(test, vkms_config_crtc_primary_plane(config, crtc_cfg));
+	KUNIT_EXPECT_NULL(test, vkms_config_crtc_cursor_plane(config, crtc_cfg));
+
+	/* Primary plane, attaching it twice must fail */
+	err = vkms_config_plane_attach_crtc(primary_cfg, crtc_cfg);
+	KUNIT_EXPECT_EQ(test, err, 0);
+	err = vkms_config_plane_attach_crtc(primary_cfg, crtc_cfg);
+	KUNIT_EXPECT_NE(test, err, 0);
+	KUNIT_EXPECT_PTR_EQ(test,
+			    vkms_config_crtc_primary_plane(config, crtc_cfg),
+			    primary_cfg);
+	KUNIT_EXPECT_NULL(test, vkms_config_crtc_cursor_plane(config, crtc_cfg));
+
+	/* Primary and cursor planes */
+	err = vkms_config_plane_attach_crtc(cursor_cfg, crtc_cfg);
+	KUNIT_EXPECT_EQ(test, err, 0);
+	KUNIT_EXPECT_PTR_EQ(test,
+			    vkms_config_crtc_primary_plane(config, crtc_cfg),
+			    primary_cfg);
+	KUNIT_EXPECT_PTR_EQ(test,
+			    vkms_config_crtc_cursor_plane(config, crtc_cfg),
+			    cursor_cfg);
+
+	/* Detach primary and destroy cursor plane */
+	vkms_config_plane_detach_crtc(overlay_cfg, crtc_cfg);
+	vkms_config_plane_detach_crtc(primary_cfg, crtc_cfg);
+	vkms_config_destroy_plane(cursor_cfg);
+	KUNIT_EXPECT_NULL(test, vkms_config_crtc_primary_plane(config, crtc_cfg));
+	KUNIT_EXPECT_NULL(test, vkms_config_crtc_cursor_plane(config, crtc_cfg));
+
+	vkms_config_destroy(config);
+}
+
+static void vkms_config_test_plane_get_possible_crtcs(struct kunit *test)
+{
+	struct vkms_config *config;
+	struct vkms_config_plane *plane_cfg1, *plane_cfg2;
+	struct vkms_config_crtc *crtc_cfg1, *crtc_cfg2;
+	struct vkms_config_crtc *possible_crtc;
+	unsigned long idx = 0;
+	int n_crtcs = 0;
+	int err;
+
+	config = vkms_config_create("test");
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
+
+	plane_cfg1 = vkms_config_create_plane(config);
+	plane_cfg2 = vkms_config_create_plane(config);
+	crtc_cfg1 = vkms_config_create_crtc(config);
+	crtc_cfg2 = vkms_config_create_crtc(config);
+
+	/* No possible CRTCs */
+	vkms_config_plane_for_each_possible_crtc(plane_cfg1, idx, possible_crtc)
+		KUNIT_FAIL(test, "Unexpected possible CRTC");
+
+	vkms_config_plane_for_each_possible_crtc(plane_cfg2, idx, possible_crtc)
+		KUNIT_FAIL(test, "Unexpected possible CRTC");
+
+	/* Plane 1 attached to CRTC 1 and 2 */
+	err = vkms_config_plane_attach_crtc(plane_cfg1, crtc_cfg1);
+	KUNIT_EXPECT_EQ(test, err, 0);
+	err = vkms_config_plane_attach_crtc(plane_cfg1, crtc_cfg2);
+	KUNIT_EXPECT_EQ(test, err, 0);
+
+	vkms_config_plane_for_each_possible_crtc(plane_cfg1, idx, possible_crtc) {
+		n_crtcs++;
+		if (possible_crtc != crtc_cfg1 && possible_crtc != crtc_cfg2)
+			KUNIT_FAIL(test, "Unexpected possible CRTC");
+	}
+	KUNIT_ASSERT_EQ(test, n_crtcs, 2);
+	n_crtcs = 0;
+
+	vkms_config_plane_for_each_possible_crtc(plane_cfg2, idx, possible_crtc)
+		KUNIT_FAIL(test, "Unexpected possible CRTC");
+
+	/* Plane 1 attached to CRTC 1 and plane 2 to CRTC 2 */
+	vkms_config_plane_detach_crtc(plane_cfg1, crtc_cfg2);
+	vkms_config_plane_for_each_possible_crtc(plane_cfg1, idx, possible_crtc) {
+		n_crtcs++;
+		if (possible_crtc != crtc_cfg1)
+			KUNIT_FAIL(test, "Unexpected possible CRTC");
+	}
+	KUNIT_ASSERT_EQ(test, n_crtcs, 1);
+	n_crtcs = 0;
+
+	err = vkms_config_plane_attach_crtc(plane_cfg2, crtc_cfg2);
+	KUNIT_EXPECT_EQ(test, err, 0);
+	vkms_config_plane_for_each_possible_crtc(plane_cfg2, idx, possible_crtc) {
+		n_crtcs++;
+		if (possible_crtc != crtc_cfg2)
+			KUNIT_FAIL(test, "Unexpected possible CRTC");
+	}
+	KUNIT_ASSERT_EQ(test, n_crtcs, 1);
+
+	vkms_config_destroy(config);
+}
+
 static struct kunit_case vkms_config_test_cases[] = {
 	KUNIT_CASE(vkms_config_test_empty_config),
 	KUNIT_CASE_PARAM(vkms_config_test_default_config,
@@ -267,7 +452,10 @@ static struct kunit_case vkms_config_test_cases[] = {
 	KUNIT_CASE(vkms_config_test_get_crtcs),
 	KUNIT_CASE(vkms_config_test_invalid_plane_number),
 	KUNIT_CASE(vkms_config_test_valid_plane_type),
+	KUNIT_CASE(vkms_config_test_valid_plane_possible_crtcs),
 	KUNIT_CASE(vkms_config_test_invalid_crtc_number),
+	KUNIT_CASE(vkms_config_test_plane_attach_crtc),
+	KUNIT_CASE(vkms_config_test_plane_get_possible_crtcs),
 	{}
 };
 
diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
index b08021e62c9f..d96f9e5f9b72 100644
--- a/drivers/gpu/drm/vkms/vkms_config.c
+++ b/drivers/gpu/drm/vkms/vkms_config.c
@@ -50,13 +50,20 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
 		goto err_alloc;
 	vkms_config_crtc_set_writeback(crtc_cfg, enable_writeback);
 
+	if (vkms_config_plane_attach_crtc(plane_cfg, crtc_cfg))
+		goto err_alloc;
+
 	if (enable_overlay) {
 		for (n = 0; n < NUM_OVERLAY_PLANES; n++) {
 			plane_cfg = vkms_config_create_plane(config);
 			if (IS_ERR(plane_cfg))
 				goto err_alloc;
+
 			vkms_config_plane_set_type(plane_cfg,
 						   DRM_PLANE_TYPE_OVERLAY);
+
+			if (vkms_config_plane_attach_crtc(plane_cfg, crtc_cfg))
+				goto err_alloc;
 		}
 	}
 
@@ -64,7 +71,11 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
 		plane_cfg = vkms_config_create_plane(config);
 		if (IS_ERR(plane_cfg))
 			goto err_alloc;
+
 		vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_CURSOR);
+
+		if (vkms_config_plane_attach_crtc(plane_cfg, crtc_cfg))
+			goto err_alloc;
 	}
 
 	return config;
@@ -103,7 +114,8 @@ static bool valid_plane_number(struct vkms_config *config)
 	return true;
 }
 
-static bool valid_plane_type(struct vkms_config *config)
+static bool valid_planes_for_crtc(struct vkms_config *config,
+				  struct vkms_config_crtc *crtc_cfg)
 {
 	struct drm_device *dev = &config->dev->drm;
 	struct vkms_config_plane *plane_cfg;
@@ -111,24 +123,31 @@ static bool valid_plane_type(struct vkms_config *config)
 	bool has_cursor_plane = false;
 
 	vkms_config_for_each_plane(config, plane_cfg) {
+		struct vkms_config_crtc *possible_crtc;
+		unsigned long idx = 0;
 		enum drm_plane_type type;
 
 		type = vkms_config_plane_get_type(plane_cfg);
 
-		if (type == DRM_PLANE_TYPE_PRIMARY) {
-			if (has_primary_plane) {
-				drm_info(dev, "Multiple primary planes\n");
-				return false;
-			}
+		vkms_config_plane_for_each_possible_crtc(plane_cfg, idx, possible_crtc) {
+			if (possible_crtc != crtc_cfg)
+				continue;
 
-			has_primary_plane = true;
-		} else if (type == DRM_PLANE_TYPE_CURSOR) {
-			if (has_cursor_plane) {
-				drm_info(dev, "Multiple cursor planes\n");
-				return false;
-			}
+			if (type == DRM_PLANE_TYPE_PRIMARY) {
+				if (has_primary_plane) {
+					drm_info(dev, "Multiple primary planes\n");
+					return false;
+				}
 
-			has_cursor_plane = true;
+				has_primary_plane = true;
+			} else if (type == DRM_PLANE_TYPE_CURSOR) {
+				if (has_cursor_plane) {
+					drm_info(dev, "Multiple cursor planes\n");
+					return false;
+				}
+
+				has_cursor_plane = true;
+			}
 		}
 	}
 
@@ -140,6 +159,21 @@ static bool valid_plane_type(struct vkms_config *config)
 	return true;
 }
 
+static bool valid_plane_possible_crtcs(struct vkms_config *config)
+{
+	struct drm_device *dev = &config->dev->drm;
+	struct vkms_config_plane *plane_cfg;
+
+	vkms_config_for_each_plane(config, plane_cfg) {
+		if (xa_empty(&plane_cfg->possible_crtcs)) {
+			drm_info(dev, "All planes must have at least one possible CRTC\n");
+			return false;
+		}
+	}
+
+	return true;
+}
+
 static bool valid_crtc_number(struct vkms_config *config)
 {
 	struct drm_device *dev = &config->dev->drm;
@@ -156,15 +190,22 @@ static bool valid_crtc_number(struct vkms_config *config)
 
 bool vkms_config_is_valid(struct vkms_config *config)
 {
+	struct vkms_config_crtc *crtc_cfg;
+
 	if (!valid_plane_number(config))
 		return false;
 
 	if (!valid_crtc_number(config))
 		return false;
 
-	if (!valid_plane_type(config))
+	if (!valid_plane_possible_crtcs(config))
 		return false;
 
+	vkms_config_for_each_crtc(config, crtc_cfg) {
+		if (!valid_planes_for_crtc(config, crtc_cfg))
+			return false;
+	}
+
 	return true;
 }
 
@@ -214,6 +255,7 @@ struct vkms_config_plane *vkms_config_create_plane(struct vkms_config *config)
 		return ERR_PTR(-ENOMEM);
 
 	vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_OVERLAY);
+	xa_init_flags(&plane_cfg->possible_crtcs, XA_FLAGS_ALLOC);
 
 	list_add_tail(&plane_cfg->link, &config->planes);
 
@@ -222,10 +264,39 @@ struct vkms_config_plane *vkms_config_create_plane(struct vkms_config *config)
 
 void vkms_config_destroy_plane(struct vkms_config_plane *plane_cfg)
 {
+	xa_destroy(&plane_cfg->possible_crtcs);
 	list_del(&plane_cfg->link);
 	kfree(plane_cfg);
 }
 
+int __must_check vkms_config_plane_attach_crtc(struct vkms_config_plane *plane_cfg,
+					       struct vkms_config_crtc *crtc_cfg)
+{
+	struct vkms_config_crtc *possible_crtc;
+	unsigned long idx = 0;
+	u32 crtc_idx = 0;
+
+	vkms_config_plane_for_each_possible_crtc(plane_cfg, idx, possible_crtc) {
+		if (possible_crtc == crtc_cfg)
+			return -EINVAL;
+	}
+
+	return xa_alloc(&plane_cfg->possible_crtcs, &crtc_idx, crtc_cfg,
+			xa_limit_32b, GFP_KERNEL);
+}
+
+void vkms_config_plane_detach_crtc(struct vkms_config_plane *plane_cfg,
+				   struct vkms_config_crtc *crtc_cfg)
+{
+	struct vkms_config_crtc *possible_crtc;
+	unsigned long idx = 0;
+
+	vkms_config_plane_for_each_possible_crtc(plane_cfg, idx, possible_crtc) {
+		if (possible_crtc == crtc_cfg)
+			xa_erase(&plane_cfg->possible_crtcs, idx);
+	}
+}
+
 struct vkms_config_crtc *vkms_config_create_crtc(struct vkms_config *config)
 {
 	struct vkms_config_crtc *crtc_cfg;
@@ -244,6 +315,54 @@ struct vkms_config_crtc *vkms_config_create_crtc(struct vkms_config *config)
 void vkms_config_destroy_crtc(struct vkms_config *config,
 			      struct vkms_config_crtc *crtc_cfg)
 {
+	struct vkms_config_plane *plane_cfg;
+
+	vkms_config_for_each_plane(config, plane_cfg)
+		vkms_config_plane_detach_crtc(plane_cfg, crtc_cfg);
+
 	list_del(&crtc_cfg->link);
 	kfree(crtc_cfg);
 }
+
+/**
+ * vkms_config_crtc_get_plane() - Return the first attached plane to a CRTC with
+ * the specific type
+ * @config: Configuration containing the CRTC and the plane
+ * @crtc_cfg: Only find planes attached to this CRTC
+ * @type: Plane type to search
+ *
+ * Returns:
+ * The first plane found attached to @crtc_cfg with the type @type.
+ */
+static struct vkms_config_plane *vkms_config_crtc_get_plane(const struct vkms_config *config,
+							    struct vkms_config_crtc *crtc_cfg,
+							    enum drm_plane_type type)
+{
+	struct vkms_config_plane *plane_cfg;
+	struct vkms_config_crtc *possible_crtc;
+	enum drm_plane_type current_type;
+	unsigned long idx = 0;
+
+	vkms_config_for_each_plane(config, plane_cfg) {
+		current_type = vkms_config_plane_get_type(plane_cfg);
+
+		vkms_config_plane_for_each_possible_crtc(plane_cfg, idx, possible_crtc) {
+			if (possible_crtc == crtc_cfg && current_type == type)
+				return plane_cfg;
+		}
+	}
+
+	return NULL;
+}
+
+struct vkms_config_plane *vkms_config_crtc_primary_plane(const struct vkms_config *config,
+							 struct vkms_config_crtc *crtc_cfg)
+{
+	return vkms_config_crtc_get_plane(config, crtc_cfg, DRM_PLANE_TYPE_PRIMARY);
+}
+
+struct vkms_config_plane *vkms_config_crtc_cursor_plane(const struct vkms_config *config,
+							struct vkms_config_crtc *crtc_cfg)
+{
+	return vkms_config_crtc_get_plane(config, crtc_cfg, DRM_PLANE_TYPE_CURSOR);
+}
diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
index 0f5937b44948..0fe0ded1d8e4 100644
--- a/drivers/gpu/drm/vkms/vkms_config.h
+++ b/drivers/gpu/drm/vkms/vkms_config.h
@@ -5,6 +5,7 @@
 
 #include <linux/list.h>
 #include <linux/types.h>
+#include <linux/xarray.h>
 
 #include "vkms_drv.h"
 
@@ -12,14 +13,12 @@
  * struct vkms_config - General configuration for VKMS driver
  *
  * @dev_name: Name of the device
- * @writeback: If true, a writeback buffer can be attached to the CRTC
  * @planes: List of planes configured for the device
  * @crtcs: List of CRTCs configured for the device
  * @dev: Used to store the current VKMS device. Only set when the device is instantiated.
  */
 struct vkms_config {
 	const char *dev_name;
-	bool writeback;
 	struct list_head planes;
 	struct list_head crtcs;
 	struct vkms_device *dev;
@@ -31,6 +30,7 @@ struct vkms_config {
  * @link: Link to the others planes in vkms_config
  * @type: Type of the plane. The creator of configuration needs to ensures that
  *        at least one primary plane is present.
+ * @possible_crtcs: Array of CRTCs that can be used with this plane
  * @plane: Internal usage. This pointer should never be considered as valid.
  *         It can be used to store a temporary reference to a VKMS plane during
  *         device creation. This pointer is not managed by the configuration and
@@ -40,6 +40,7 @@ struct vkms_config_plane {
 	struct list_head link;
 
 	enum drm_plane_type type;
+	struct xarray possible_crtcs;
 
 	/* Internal usage */
 	struct vkms_plane *plane;
@@ -80,6 +81,16 @@ struct vkms_config_crtc {
 #define vkms_config_for_each_crtc(config, crtc_cfg) \
 	list_for_each_entry((crtc_cfg), &(config)->crtcs, link)
 
+/**
+ * vkms_config_plane_for_each_possible_crtc - Iterate over the vkms_config_plane
+ * possible CRTCs
+ * @plane_cfg: &struct vkms_config_plane pointer
+ * @idx: Index of the cursor
+ * @possible_crtc: &struct vkms_config_crtc pointer used as cursor
+ */
+#define vkms_config_plane_for_each_possible_crtc(plane_cfg, idx, possible_crtc) \
+	xa_for_each(&(plane_cfg)->possible_crtcs, idx, (possible_crtc))
+
 /**
  * vkms_config_create() - Create a new VKMS configuration
  * @dev_name: Name of the device
@@ -187,6 +198,22 @@ vkms_config_plane_set_type(struct vkms_config_plane *plane_cfg,
 	plane_cfg->type = type;
 }
 
+/**
+ * vkms_config_plane_attach_crtc - Attach a plane to a CRTC
+ * @plane_cfg: Plane to attach
+ * @crtc_cfg: CRTC to attach @plane_cfg to
+ */
+int __must_check vkms_config_plane_attach_crtc(struct vkms_config_plane *plane_cfg,
+					       struct vkms_config_crtc *crtc_cfg);
+
+/**
+ * vkms_config_plane_attach_crtc - Detach a plane from a CRTC
+ * @plane_cfg: Plane to detach
+ * @crtc_cfg: CRTC to detach @plane_cfg from
+ */
+void vkms_config_plane_detach_crtc(struct vkms_config_plane *plane_cfg,
+				   struct vkms_config_crtc *crtc_cfg);
+
 /**
  * vkms_config_create_crtc() - Add a new CRTC configuration
  * @config: Configuration to add the CRTC to
@@ -227,4 +254,32 @@ vkms_config_crtc_set_writeback(struct vkms_config_crtc *crtc_cfg,
 	crtc_cfg->writeback = writeback;
 }
 
+/**
+ * vkms_config_crtc_primary_plane() - Return the primary plane for a CRTC
+ * @config: Configuration containing the CRTC
+ * @crtc_config: Target CRTC
+ *
+ * Note that, if multiple primary planes are found, the first one is returned.
+ * In this case, the configuration will be invalid. See vkms_config_is_valid().
+ *
+ * Returns:
+ * The primary plane or NULL if none is assigned yet.
+ */
+struct vkms_config_plane *vkms_config_crtc_primary_plane(const struct vkms_config *config,
+							 struct vkms_config_crtc *crtc_cfg);
+
+/**
+ * vkms_config_crtc_cursor_plane() - Return the cursor plane for a CRTC
+ * @config: Configuration containing the CRTC
+ * @crtc_config: Target CRTC
+ *
+ * Note that, if multiple cursor planes are found, the first one is returned.
+ * In this case, the configuration will be invalid. See vkms_config_is_valid().
+ *
+ * Returns:
+ * The cursor plane or NULL if none is assigned yet.
+ */
+struct vkms_config_plane *vkms_config_crtc_cursor_plane(const struct vkms_config *config,
+							struct vkms_config_crtc *crtc_cfg);
+
 #endif /* _VKMS_CONFIG_H_ */
diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
index 08ea691db299..f63bc8e3014b 100644
--- a/drivers/gpu/drm/vkms/vkms_output.c
+++ b/drivers/gpu/drm/vkms/vkms_output.c
@@ -10,9 +10,8 @@ int vkms_output_init(struct vkms_device *vkmsdev)
 	struct drm_device *dev = &vkmsdev->drm;
 	struct vkms_connector *connector;
 	struct drm_encoder *encoder;
-	struct vkms_output *output;
-	struct vkms_plane *primary = NULL, *cursor = NULL;
 	struct vkms_config_plane *plane_cfg;
+	struct vkms_config_crtc *crtc_cfg;
 	int ret;
 	int writeback;
 
@@ -29,18 +28,37 @@ int vkms_output_init(struct vkms_device *vkmsdev)
 			DRM_DEV_ERROR(dev->dev, "Failed to init vkms plane\n");
 			return PTR_ERR(plane_cfg->plane);
 		}
+	}
+
+	vkms_config_for_each_crtc(vkmsdev->config, crtc_cfg) {
+		struct vkms_config_plane *primary, *cursor;
+
+		primary = vkms_config_crtc_primary_plane(vkmsdev->config, crtc_cfg);
+		cursor = vkms_config_crtc_cursor_plane(vkmsdev->config, crtc_cfg);
 
-		if (type == DRM_PLANE_TYPE_PRIMARY)
-			primary = plane_cfg->plane;
-		else if (type == DRM_PLANE_TYPE_CURSOR)
-			cursor = plane_cfg->plane;
+		crtc_cfg->crtc = vkms_crtc_init(dev, &primary->plane->base,
+						cursor ? &cursor->plane->base : NULL);
+		if (IS_ERR(crtc_cfg->crtc)) {
+			DRM_ERROR("Failed to allocate CRTC\n");
+			return PTR_ERR(crtc_cfg->crtc);
+		}
+
+		/* Initialize the writeback component */
+		if (vkms_config_crtc_get_writeback(crtc_cfg)) {
+			writeback = vkms_enable_writeback_connector(vkmsdev, crtc_cfg->crtc);
+			if (writeback)
+				DRM_ERROR("Failed to init writeback connector\n");
+		}
 	}
 
-	output = vkms_crtc_init(dev, &primary->base,
-				cursor ? &cursor->base : NULL);
-	if (IS_ERR(output)) {
-		DRM_ERROR("Failed to allocate CRTC\n");
-		return PTR_ERR(output);
+	vkms_config_for_each_plane(vkmsdev->config, plane_cfg) {
+		struct vkms_config_crtc *possible_crtc;
+		unsigned long idx = 0;
+
+		vkms_config_plane_for_each_possible_crtc(plane_cfg, idx, possible_crtc) {
+			plane_cfg->plane->base.possible_crtcs |=
+				drm_crtc_mask(&possible_crtc->crtc->crtc);
+		}
 	}
 
 	connector = vkms_connector_init(vkmsdev);
@@ -60,7 +78,9 @@ int vkms_output_init(struct vkms_device *vkmsdev)
 		DRM_ERROR("Failed to init encoder\n");
 		return ret;
 	}
-	encoder->possible_crtcs = drm_crtc_mask(&output->crtc);
+
+	vkms_config_for_each_crtc(vkmsdev->config, crtc_cfg)
+		encoder->possible_crtcs = drm_crtc_mask(&crtc_cfg->crtc->crtc);
 
 	/* Attach the encoder and the connector */
 	ret = drm_connector_attach_encoder(&connector->base, encoder);
@@ -69,13 +89,6 @@ int vkms_output_init(struct vkms_device *vkmsdev)
 		return ret;
 	}
 
-	/* Initialize the writeback component */
-	if (vkmsdev->config->writeback) {
-		writeback = vkms_enable_writeback_connector(vkmsdev, output);
-		if (writeback)
-			DRM_ERROR("Failed to init writeback connector\n");
-	}
-
 	drm_mode_config_reset(dev);
 
 	return ret;
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 12/15] drm/vkms: Allow to configure multiple encoders
  2025-02-11 11:08 [PATCH v2 00/15] drm/vkms: Allow to configure device José Expósito
                   ` (10 preceding siblings ...)
  2025-02-11 11:09 ` [PATCH v2 11/15] drm/vkms: Allow to attach planes and CRTCs José Expósito
@ 2025-02-11 11:09 ` José Expósito
  2025-02-13 13:59   ` Louis Chauvet
  2025-02-11 11:09 ` [PATCH v2 13/15] drm/vkms: Allow to attach encoders and CRTCs José Expósito
                   ` (2 subsequent siblings)
  14 siblings, 1 reply; 32+ messages in thread
From: José Expósito @ 2025-02-11 11:09 UTC (permalink / raw)
  To: louis.chauvet
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel,
	José Expósito

Add a list of encoders to vkms_config and helper functions to add and
remove as many encoders as wanted.

For backwards compatibility, add one encoder to the default
configuration.

A future patch will allow to attach encoders and CRTCs, but for the
moment there are no changes in the way the output is configured.

Co-developed-by: Louis Chauvet <louis.chauvet@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
Signed-off-by: José Expósito <jose.exposito89@gmail.com>
---
 .clang-format                                 |  1 +
 drivers/gpu/drm/vkms/tests/vkms_config_test.c | 73 +++++++++++++++++++
 drivers/gpu/drm/vkms/vkms_config.c            | 51 +++++++++++++
 drivers/gpu/drm/vkms/vkms_config.h            | 44 +++++++++++
 4 files changed, 169 insertions(+)

diff --git a/.clang-format b/.clang-format
index 6f944fa39841..c355a2f58eed 100644
--- a/.clang-format
+++ b/.clang-format
@@ -691,6 +691,7 @@ ForEachMacros:
   - 'v4l2_m2m_for_each_src_buf_safe'
   - 'virtio_device_for_each_vq'
   - 'vkms_config_for_each_crtc'
+  - 'vkms_config_for_each_encoder'
   - 'vkms_config_for_each_plane'
   - 'vkms_config_plane_for_each_possible_crtc'
   - 'while_for_each_ftrace_op'
diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
index 505eebb07a52..0bb76a1e6c79 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_config_test.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
@@ -26,6 +26,7 @@ static void vkms_config_test_empty_config(struct kunit *test)
 
 	KUNIT_EXPECT_TRUE(test, list_empty(&config->planes));
 	KUNIT_EXPECT_TRUE(test, list_empty(&config->crtcs));
+	KUNIT_EXPECT_TRUE(test, list_empty(&config->encoders));
 
 	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
 
@@ -99,6 +100,9 @@ static void vkms_config_test_default_config(struct kunit *test)
 		KUNIT_EXPECT_EQ(test, n_possible_crtcs, 1);
 	}
 
+	/* Encoders */
+	KUNIT_EXPECT_EQ(test, list_count_nodes(&config->encoders), 1);
+
 	KUNIT_EXPECT_TRUE(test, vkms_config_is_valid(config));
 
 	vkms_config_destroy(config);
@@ -184,6 +188,50 @@ static void vkms_config_test_get_crtcs(struct kunit *test)
 	vkms_config_destroy(config);
 }
 
+static void vkms_config_test_get_encoders(struct kunit *test)
+{
+	struct vkms_config *config;
+	struct vkms_config_encoder *encoder_cfg;
+	struct vkms_config_encoder *encoder_cfg1, *encoder_cfg2;
+	int n_encoders = 0;
+
+	config = vkms_config_create("test");
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
+
+	vkms_config_for_each_encoder(config, encoder_cfg)
+		n_encoders++;
+	KUNIT_ASSERT_EQ(test, n_encoders, 0);
+
+	encoder_cfg1 = vkms_config_create_encoder(config);
+	vkms_config_for_each_encoder(config, encoder_cfg) {
+		n_encoders++;
+		if (encoder_cfg != encoder_cfg1)
+			KUNIT_FAIL(test, "Unexpected encoder");
+	}
+	KUNIT_ASSERT_EQ(test, n_encoders, 1);
+	n_encoders = 0;
+
+	encoder_cfg2 = vkms_config_create_encoder(config);
+	vkms_config_for_each_encoder(config, encoder_cfg) {
+		n_encoders++;
+		if (encoder_cfg != encoder_cfg1 && encoder_cfg != encoder_cfg2)
+			KUNIT_FAIL(test, "Unexpected encoder");
+	}
+	KUNIT_ASSERT_EQ(test, n_encoders, 2);
+	n_encoders = 0;
+
+	vkms_config_destroy_encoder(config, encoder_cfg2);
+	vkms_config_for_each_encoder(config, encoder_cfg) {
+		n_encoders++;
+		if (encoder_cfg != encoder_cfg1)
+			KUNIT_FAIL(test, "Unexpected encoder");
+	}
+	KUNIT_ASSERT_EQ(test, n_encoders, 1);
+	n_encoders = 0;
+
+	vkms_config_destroy(config);
+}
+
 static void vkms_config_test_invalid_plane_number(struct kunit *test)
 {
 	struct vkms_config *config;
@@ -319,6 +367,29 @@ static void vkms_config_test_invalid_crtc_number(struct kunit *test)
 	vkms_config_destroy(config);
 }
 
+static void vkms_config_test_invalid_encoder_number(struct kunit *test)
+{
+	struct vkms_config *config;
+	struct vkms_config_encoder *encoder_cfg;
+	int n;
+
+	config = vkms_config_default_create(false, false, false);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
+
+	/* Invalid: No encoders */
+	encoder_cfg = list_first_entry(&config->encoders, typeof(*encoder_cfg), link);
+	vkms_config_destroy_encoder(config, encoder_cfg);
+	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
+
+	/* Invalid: Too many encoders */
+	for (n = 0; n <= 32; n++)
+		vkms_config_create_encoder(config);
+
+	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
+
+	vkms_config_destroy(config);
+}
+
 static void vkms_config_test_plane_attach_crtc(struct kunit *test)
 {
 	struct vkms_config *config;
@@ -450,10 +521,12 @@ static struct kunit_case vkms_config_test_cases[] = {
 			 default_config_gen_params),
 	KUNIT_CASE(vkms_config_test_get_planes),
 	KUNIT_CASE(vkms_config_test_get_crtcs),
+	KUNIT_CASE(vkms_config_test_get_encoders),
 	KUNIT_CASE(vkms_config_test_invalid_plane_number),
 	KUNIT_CASE(vkms_config_test_valid_plane_type),
 	KUNIT_CASE(vkms_config_test_valid_plane_possible_crtcs),
 	KUNIT_CASE(vkms_config_test_invalid_crtc_number),
+	KUNIT_CASE(vkms_config_test_invalid_encoder_number),
 	KUNIT_CASE(vkms_config_test_plane_attach_crtc),
 	KUNIT_CASE(vkms_config_test_plane_get_possible_crtcs),
 	{}
diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
index d96f9e5f9b72..0cf6105fe743 100644
--- a/drivers/gpu/drm/vkms/vkms_config.c
+++ b/drivers/gpu/drm/vkms/vkms_config.c
@@ -23,6 +23,7 @@ struct vkms_config *vkms_config_create(const char *dev_name)
 
 	INIT_LIST_HEAD(&config->planes);
 	INIT_LIST_HEAD(&config->crtcs);
+	INIT_LIST_HEAD(&config->encoders);
 
 	return config;
 }
@@ -34,6 +35,7 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
 	struct vkms_config *config;
 	struct vkms_config_plane *plane_cfg;
 	struct vkms_config_crtc *crtc_cfg;
+	struct vkms_config_encoder *encoder_cfg;
 	int n;
 
 	config = vkms_config_create(DEFAULT_DEVICE_NAME);
@@ -78,6 +80,10 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
 			goto err_alloc;
 	}
 
+	encoder_cfg = vkms_config_create_encoder(config);
+	if (IS_ERR(encoder_cfg))
+		goto err_alloc;
+
 	return config;
 
 err_alloc:
@@ -89,6 +95,7 @@ void vkms_config_destroy(struct vkms_config *config)
 {
 	struct vkms_config_plane *plane_cfg, *plane_tmp;
 	struct vkms_config_crtc *crtc_cfg, *crtc_tmp;
+	struct vkms_config_encoder *encoder_cfg, *encoder_tmp;
 
 	list_for_each_entry_safe(plane_cfg, plane_tmp, &config->planes, link)
 		vkms_config_destroy_plane(plane_cfg);
@@ -96,6 +103,9 @@ void vkms_config_destroy(struct vkms_config *config)
 	list_for_each_entry_safe(crtc_cfg, crtc_tmp, &config->crtcs, link)
 		vkms_config_destroy_crtc(config, crtc_cfg);
 
+	list_for_each_entry_safe(encoder_cfg, encoder_tmp, &config->encoders, link)
+		vkms_config_destroy_encoder(config, encoder_cfg);
+
 	kfree_const(config->dev_name);
 	kfree(config);
 }
@@ -188,6 +198,20 @@ static bool valid_crtc_number(struct vkms_config *config)
 	return true;
 }
 
+static bool valid_encoder_number(struct vkms_config *config)
+{
+	struct drm_device *dev = &config->dev->drm;
+	size_t n_encoders;
+
+	n_encoders = list_count_nodes(&config->encoders);
+	if (n_encoders <= 0 || n_encoders >= 32) {
+		drm_info(dev, "The number of encoders must be between 1 and 31\n");
+		return false;
+	}
+
+	return true;
+}
+
 bool vkms_config_is_valid(struct vkms_config *config)
 {
 	struct vkms_config_crtc *crtc_cfg;
@@ -198,6 +222,9 @@ bool vkms_config_is_valid(struct vkms_config *config)
 	if (!valid_crtc_number(config))
 		return false;
 
+	if (!valid_encoder_number(config))
+		return false;
+
 	if (!valid_plane_possible_crtcs(config))
 		return false;
 
@@ -217,6 +244,7 @@ static int vkms_config_show(struct seq_file *m, void *data)
 	const char *dev_name;
 	struct vkms_config_plane *plane_cfg;
 	struct vkms_config_crtc *crtc_cfg;
+	struct vkms_config_encoder *encoder_cfg;
 
 	dev_name = vkms_config_get_device_name((struct vkms_config *)vkmsdev->config);
 	seq_printf(m, "dev_name=%s\n", dev_name);
@@ -233,6 +261,9 @@ static int vkms_config_show(struct seq_file *m, void *data)
 			   vkms_config_crtc_get_writeback(crtc_cfg));
 	}
 
+	vkms_config_for_each_encoder(vkmsdev->config, encoder_cfg)
+		seq_puts(m, "encoder\n");
+
 	return 0;
 }
 
@@ -366,3 +397,23 @@ struct vkms_config_plane *vkms_config_crtc_cursor_plane(const struct vkms_config
 {
 	return vkms_config_crtc_get_plane(config, crtc_cfg, DRM_PLANE_TYPE_CURSOR);
 }
+
+struct vkms_config_encoder *vkms_config_create_encoder(struct vkms_config *config)
+{
+	struct vkms_config_encoder *encoder_cfg;
+
+	encoder_cfg = kzalloc(sizeof(*encoder_cfg), GFP_KERNEL);
+	if (!encoder_cfg)
+		return ERR_PTR(-ENOMEM);
+
+	list_add_tail(&encoder_cfg->link, &config->encoders);
+
+	return encoder_cfg;
+}
+
+void vkms_config_destroy_encoder(struct vkms_config *config,
+				 struct vkms_config_encoder *encoder_cfg)
+{
+	list_del(&encoder_cfg->link);
+	kfree(encoder_cfg);
+}
diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
index 0fe0ded1d8e4..2ba80c4c9ce5 100644
--- a/drivers/gpu/drm/vkms/vkms_config.h
+++ b/drivers/gpu/drm/vkms/vkms_config.h
@@ -15,12 +15,14 @@
  * @dev_name: Name of the device
  * @planes: List of planes configured for the device
  * @crtcs: List of CRTCs configured for the device
+ * @encoders: List of encoders configured for the device
  * @dev: Used to store the current VKMS device. Only set when the device is instantiated.
  */
 struct vkms_config {
 	const char *dev_name;
 	struct list_head planes;
 	struct list_head crtcs;
+	struct list_head encoders;
 	struct vkms_device *dev;
 };
 
@@ -65,6 +67,22 @@ struct vkms_config_crtc {
 	struct vkms_output *crtc;
 };
 
+/**
+ * struct vkms_config_encoder
+ *
+ * @link: Link to the others encoders in vkms_config
+ * @encoder: Internal usage. This pointer should never be considered as valid.
+ *           It can be used to store a temporary reference to a VKMS encoder
+ *           during device creation. This pointer is not managed by the
+ *           configuration and must be managed by other means.
+ */
+struct vkms_config_encoder {
+	struct list_head link;
+
+	/* Internal usage */
+	struct drm_encoder *encoder;
+};
+
 /**
  * vkms_config_for_each_plane - Iterate over the vkms_config planes
  * @config: &struct vkms_config pointer
@@ -81,6 +99,14 @@ struct vkms_config_crtc {
 #define vkms_config_for_each_crtc(config, crtc_cfg) \
 	list_for_each_entry((crtc_cfg), &(config)->crtcs, link)
 
+/**
+ * vkms_config_for_each_encoder - Iterate over the vkms_config encoders
+ * @config: &struct vkms_config pointer
+ * @encoder_cfg: &struct vkms_config_encoder pointer used as cursor
+ */
+#define vkms_config_for_each_encoder(config, encoder_cfg) \
+	list_for_each_entry((encoder_cfg), &(config)->encoders, link)
+
 /**
  * vkms_config_plane_for_each_possible_crtc - Iterate over the vkms_config_plane
  * possible CRTCs
@@ -282,4 +308,22 @@ struct vkms_config_plane *vkms_config_crtc_primary_plane(const struct vkms_confi
 struct vkms_config_plane *vkms_config_crtc_cursor_plane(const struct vkms_config *config,
 							struct vkms_config_crtc *crtc_cfg);
 
+/**
+ * vkms_config_create_encoder() - Add a new encoder configuration
+ * @config: Configuration to add the encoder to
+ *
+ * Returns:
+ * The new encoder configuration or an error. Call vkms_config_destroy_encoder()
+ * to free the returned encoder configuration.
+ */
+struct vkms_config_encoder *vkms_config_create_encoder(struct vkms_config *config);
+
+/**
+ * vkms_config_destroy_encoder() - Remove and free a encoder configuration
+ * @config: Configuration to remove the encoder from
+ * @encoder_cfg: Encoder configuration to destroy
+ */
+void vkms_config_destroy_encoder(struct vkms_config *config,
+				 struct vkms_config_encoder *encoder_cfg);
+
 #endif /* _VKMS_CONFIG_H_ */
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 13/15] drm/vkms: Allow to attach encoders and CRTCs
  2025-02-11 11:08 [PATCH v2 00/15] drm/vkms: Allow to configure device José Expósito
                   ` (11 preceding siblings ...)
  2025-02-11 11:09 ` [PATCH v2 12/15] drm/vkms: Allow to configure multiple encoders José Expósito
@ 2025-02-11 11:09 ` José Expósito
  2025-02-13 13:59   ` Louis Chauvet
  2025-02-11 11:09 ` [PATCH v2 14/15] drm/vkms: Allow to configure multiple connectors José Expósito
  2025-02-11 11:09 ` [PATCH v2 15/15] drm/vkms: Allow to attach connectors and encoders José Expósito
  14 siblings, 1 reply; 32+ messages in thread
From: José Expósito @ 2025-02-11 11:09 UTC (permalink / raw)
  To: louis.chauvet
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel,
	José Expósito

Add a list of possible CRTCs to the encoder configuration and helpers to
attach and detach them.

Now that the default configuration has its encoder and CRTC correctly
attached, configure the output following the configuration.

Co-developed-by: Louis Chauvet <louis.chauvet@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
Signed-off-by: José Expósito <jose.exposito89@gmail.com>
---
 .clang-format                                 |   1 +
 drivers/gpu/drm/vkms/tests/vkms_config_test.c | 115 ++++++++++++++++++
 drivers/gpu/drm/vkms/vkms_config.c            |  77 ++++++++++++
 drivers/gpu/drm/vkms/vkms_config.h            |  29 +++++
 drivers/gpu/drm/vkms/vkms_output.c            |  49 +++++---
 5 files changed, 251 insertions(+), 20 deletions(-)

diff --git a/.clang-format b/.clang-format
index c355a2f58eed..5d21c0e4edbd 100644
--- a/.clang-format
+++ b/.clang-format
@@ -693,6 +693,7 @@ ForEachMacros:
   - 'vkms_config_for_each_crtc'
   - 'vkms_config_for_each_encoder'
   - 'vkms_config_for_each_plane'
+  - 'vkms_config_encoder_for_each_possible_crtc'
   - 'vkms_config_plane_for_each_possible_crtc'
   - 'while_for_each_ftrace_op'
   - 'xa_for_each'
diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
index 0bb76a1e6c79..7458d175acb6 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_config_test.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
@@ -260,6 +260,7 @@ static void vkms_config_test_valid_plane_type(struct kunit *test)
 	struct vkms_config *config;
 	struct vkms_config_plane *plane_cfg;
 	struct vkms_config_crtc *crtc_cfg;
+	struct vkms_config_encoder *encoder_cfg;
 	int err;
 
 	config = vkms_config_default_create(false, false, false);
@@ -313,6 +314,9 @@ static void vkms_config_test_valid_plane_type(struct kunit *test)
 
 	/* Invalid: Second CRTC without primary plane */
 	crtc_cfg = vkms_config_create_crtc(config);
+	encoder_cfg = vkms_config_create_encoder(config);
+	err = vkms_config_encoder_attach_crtc(encoder_cfg, crtc_cfg);
+	KUNIT_EXPECT_EQ(test, err, 0);
 	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
 
 	/* Valid: Second CRTC with a primary plane */
@@ -390,6 +394,51 @@ static void vkms_config_test_invalid_encoder_number(struct kunit *test)
 	vkms_config_destroy(config);
 }
 
+static void vkms_config_test_valid_encoder_possible_crtcs(struct kunit *test)
+{
+	struct vkms_config *config;
+	struct vkms_config_plane *plane_cfg;
+	struct vkms_config_crtc *crtc_cfg1, *crtc_cfg2;
+	struct vkms_config_encoder *encoder_cfg;
+	int err;
+
+	config = vkms_config_default_create(false, false, false);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
+
+	crtc_cfg1 = list_first_entry(&config->crtcs, typeof(*crtc_cfg1), link);
+
+	/* Invalid: Encoder without a possible CRTC */
+	encoder_cfg = vkms_config_create_encoder(config);
+	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
+
+	/* Valid: Second CRTC with shared encoder */
+	crtc_cfg2 = vkms_config_create_crtc(config);
+
+	plane_cfg = vkms_config_create_plane(config);
+	vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_PRIMARY);
+	err = vkms_config_plane_attach_crtc(plane_cfg, crtc_cfg2);
+	KUNIT_EXPECT_EQ(test, err, 0);
+
+	err = vkms_config_encoder_attach_crtc(encoder_cfg, crtc_cfg1);
+	KUNIT_EXPECT_EQ(test, err, 0);
+
+	err = vkms_config_encoder_attach_crtc(encoder_cfg, crtc_cfg2);
+	KUNIT_EXPECT_EQ(test, err, 0);
+
+	KUNIT_EXPECT_TRUE(test, vkms_config_is_valid(config));
+
+	/* Invalid: Second CRTC without encoders */
+	vkms_config_encoder_detach_crtc(encoder_cfg, crtc_cfg2);
+	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
+
+	/* Valid: First CRTC with 2 possible encoder */
+	vkms_config_destroy_plane(plane_cfg);
+	vkms_config_destroy_crtc(config, crtc_cfg2);
+	KUNIT_EXPECT_TRUE(test, vkms_config_is_valid(config));
+
+	vkms_config_destroy(config);
+}
+
 static void vkms_config_test_plane_attach_crtc(struct kunit *test)
 {
 	struct vkms_config *config;
@@ -515,6 +564,70 @@ static void vkms_config_test_plane_get_possible_crtcs(struct kunit *test)
 	vkms_config_destroy(config);
 }
 
+static void vkms_config_test_encoder_get_possible_crtcs(struct kunit *test)
+{
+	struct vkms_config *config;
+	struct vkms_config_encoder *encoder_cfg1, *encoder_cfg2;
+	struct vkms_config_crtc *crtc_cfg1, *crtc_cfg2;
+	struct vkms_config_crtc *possible_crtc;
+	unsigned long idx = 0;
+	int n_crtcs = 0;
+	int err;
+
+	config = vkms_config_create("test");
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
+
+	encoder_cfg1 = vkms_config_create_encoder(config);
+	encoder_cfg2 = vkms_config_create_encoder(config);
+	crtc_cfg1 = vkms_config_create_crtc(config);
+	crtc_cfg2 = vkms_config_create_crtc(config);
+
+	/* No possible CRTCs */
+	vkms_config_encoder_for_each_possible_crtc(encoder_cfg1, idx, possible_crtc)
+		KUNIT_FAIL(test, "Unexpected possible CRTC");
+
+	vkms_config_encoder_for_each_possible_crtc(encoder_cfg2, idx, possible_crtc)
+		KUNIT_FAIL(test, "Unexpected possible CRTC");
+
+	/* Encoder 1 attached to CRTC 1 and 2 */
+	err = vkms_config_encoder_attach_crtc(encoder_cfg1, crtc_cfg1);
+	KUNIT_EXPECT_EQ(test, err, 0);
+	err = vkms_config_encoder_attach_crtc(encoder_cfg1, crtc_cfg2);
+	KUNIT_EXPECT_EQ(test, err, 0);
+
+	vkms_config_encoder_for_each_possible_crtc(encoder_cfg1, idx, possible_crtc) {
+		n_crtcs++;
+		if (possible_crtc != crtc_cfg1 && possible_crtc != crtc_cfg2)
+			KUNIT_FAIL(test, "Unexpected possible CRTC");
+	}
+	KUNIT_ASSERT_EQ(test, n_crtcs, 2);
+	n_crtcs = 0;
+
+	vkms_config_encoder_for_each_possible_crtc(encoder_cfg2, idx, possible_crtc)
+		KUNIT_FAIL(test, "Unexpected possible CRTC");
+
+	/* Encoder 1 attached to CRTC 1 and encoder 2 to CRTC 2 */
+	vkms_config_encoder_detach_crtc(encoder_cfg1, crtc_cfg2);
+	vkms_config_encoder_for_each_possible_crtc(encoder_cfg1, idx, possible_crtc) {
+		n_crtcs++;
+		if (possible_crtc != crtc_cfg1)
+			KUNIT_FAIL(test, "Unexpected possible CRTC");
+	}
+	KUNIT_ASSERT_EQ(test, n_crtcs, 1);
+	n_crtcs = 0;
+
+	err = vkms_config_encoder_attach_crtc(encoder_cfg2, crtc_cfg2);
+	KUNIT_EXPECT_EQ(test, err, 0);
+	vkms_config_encoder_for_each_possible_crtc(encoder_cfg2, idx, possible_crtc) {
+		n_crtcs++;
+		if (possible_crtc != crtc_cfg2)
+			KUNIT_FAIL(test, "Unexpected possible CRTC");
+	}
+	KUNIT_ASSERT_EQ(test, n_crtcs, 1);
+
+	vkms_config_destroy(config);
+}
+
 static struct kunit_case vkms_config_test_cases[] = {
 	KUNIT_CASE(vkms_config_test_empty_config),
 	KUNIT_CASE_PARAM(vkms_config_test_default_config,
@@ -527,8 +640,10 @@ static struct kunit_case vkms_config_test_cases[] = {
 	KUNIT_CASE(vkms_config_test_valid_plane_possible_crtcs),
 	KUNIT_CASE(vkms_config_test_invalid_crtc_number),
 	KUNIT_CASE(vkms_config_test_invalid_encoder_number),
+	KUNIT_CASE(vkms_config_test_valid_encoder_possible_crtcs),
 	KUNIT_CASE(vkms_config_test_plane_attach_crtc),
 	KUNIT_CASE(vkms_config_test_plane_get_possible_crtcs),
+	KUNIT_CASE(vkms_config_test_encoder_get_possible_crtcs),
 	{}
 };
 
diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
index 0cf6105fe743..f727c0009489 100644
--- a/drivers/gpu/drm/vkms/vkms_config.c
+++ b/drivers/gpu/drm/vkms/vkms_config.c
@@ -84,6 +84,9 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
 	if (IS_ERR(encoder_cfg))
 		goto err_alloc;
 
+	if (vkms_config_encoder_attach_crtc(encoder_cfg, crtc_cfg))
+		goto err_alloc;
+
 	return config;
 
 err_alloc:
@@ -212,6 +215,42 @@ static bool valid_encoder_number(struct vkms_config *config)
 	return true;
 }
 
+static bool valid_encoder_possible_crtcs(struct vkms_config *config)
+{
+	struct drm_device *dev = &config->dev->drm;
+	struct vkms_config_crtc *crtc_cfg;
+	struct vkms_config_encoder *encoder_cfg;
+
+	vkms_config_for_each_encoder(config, encoder_cfg) {
+		if (xa_empty(&encoder_cfg->possible_crtcs)) {
+			drm_info(dev, "All encoders must have at least one possible CRTC\n");
+			return false;
+		}
+	}
+
+	vkms_config_for_each_crtc(config, crtc_cfg) {
+		bool crtc_has_encoder = false;
+
+		vkms_config_for_each_encoder(config, encoder_cfg) {
+			struct vkms_config_crtc *possible_crtc;
+			unsigned long idx = 0;
+
+			vkms_config_encoder_for_each_possible_crtc(encoder_cfg,
+								   idx, possible_crtc) {
+				if (possible_crtc == crtc_cfg)
+					crtc_has_encoder = true;
+			}
+		}
+
+		if (!crtc_has_encoder) {
+			drm_info(dev, "All CRTCs must have at least one possible encoder\n");
+			return false;
+		}
+	}
+
+	return true;
+}
+
 bool vkms_config_is_valid(struct vkms_config *config)
 {
 	struct vkms_config_crtc *crtc_cfg;
@@ -233,6 +272,9 @@ bool vkms_config_is_valid(struct vkms_config *config)
 			return false;
 	}
 
+	if (!valid_encoder_possible_crtcs(config))
+		return false;
+
 	return true;
 }
 
@@ -347,10 +389,14 @@ void vkms_config_destroy_crtc(struct vkms_config *config,
 			      struct vkms_config_crtc *crtc_cfg)
 {
 	struct vkms_config_plane *plane_cfg;
+	struct vkms_config_encoder *encoder_cfg;
 
 	vkms_config_for_each_plane(config, plane_cfg)
 		vkms_config_plane_detach_crtc(plane_cfg, crtc_cfg);
 
+	vkms_config_for_each_encoder(config, encoder_cfg)
+		vkms_config_encoder_detach_crtc(encoder_cfg, crtc_cfg);
+
 	list_del(&crtc_cfg->link);
 	kfree(crtc_cfg);
 }
@@ -406,6 +452,8 @@ struct vkms_config_encoder *vkms_config_create_encoder(struct vkms_config *confi
 	if (!encoder_cfg)
 		return ERR_PTR(-ENOMEM);
 
+	xa_init_flags(&encoder_cfg->possible_crtcs, XA_FLAGS_ALLOC);
+
 	list_add_tail(&encoder_cfg->link, &config->encoders);
 
 	return encoder_cfg;
@@ -414,6 +462,35 @@ struct vkms_config_encoder *vkms_config_create_encoder(struct vkms_config *confi
 void vkms_config_destroy_encoder(struct vkms_config *config,
 				 struct vkms_config_encoder *encoder_cfg)
 {
+	xa_destroy(&encoder_cfg->possible_crtcs);
 	list_del(&encoder_cfg->link);
 	kfree(encoder_cfg);
 }
+
+int __must_check vkms_config_encoder_attach_crtc(struct vkms_config_encoder *encoder_cfg,
+						 struct vkms_config_crtc *crtc_cfg)
+{
+	struct vkms_config_crtc *possible_crtc;
+	unsigned long idx = 0;
+	u32 crtc_idx = 0;
+
+	vkms_config_encoder_for_each_possible_crtc(encoder_cfg, idx, possible_crtc) {
+		if (possible_crtc == crtc_cfg)
+			return -EINVAL;
+	}
+
+	return xa_alloc(&encoder_cfg->possible_crtcs, &crtc_idx, crtc_cfg,
+			xa_limit_32b, GFP_KERNEL);
+}
+
+void vkms_config_encoder_detach_crtc(struct vkms_config_encoder *encoder_cfg,
+				     struct vkms_config_crtc *crtc_cfg)
+{
+	struct vkms_config_crtc *possible_crtc;
+	unsigned long idx = 0;
+
+	vkms_config_encoder_for_each_possible_crtc(encoder_cfg, idx, possible_crtc) {
+		if (possible_crtc == crtc_cfg)
+			xa_erase(&encoder_cfg->possible_crtcs, idx);
+	}
+}
diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
index 2ba80c4c9ce5..28c24afebe1e 100644
--- a/drivers/gpu/drm/vkms/vkms_config.h
+++ b/drivers/gpu/drm/vkms/vkms_config.h
@@ -71,6 +71,7 @@ struct vkms_config_crtc {
  * struct vkms_config_encoder
  *
  * @link: Link to the others encoders in vkms_config
+ * @possible_crtcs: Array of CRTCs that can be used with this encoder
  * @encoder: Internal usage. This pointer should never be considered as valid.
  *           It can be used to store a temporary reference to a VKMS encoder
  *           during device creation. This pointer is not managed by the
@@ -79,6 +80,8 @@ struct vkms_config_crtc {
 struct vkms_config_encoder {
 	struct list_head link;
 
+	struct xarray possible_crtcs;
+
 	/* Internal usage */
 	struct drm_encoder *encoder;
 };
@@ -117,6 +120,16 @@ struct vkms_config_encoder {
 #define vkms_config_plane_for_each_possible_crtc(plane_cfg, idx, possible_crtc) \
 	xa_for_each(&(plane_cfg)->possible_crtcs, idx, (possible_crtc))
 
+/**
+ * vkms_config_encoder_for_each_possible_crtc - Iterate over the
+ * vkms_config_encoder possible CRTCs
+ * @encoder_cfg: &struct vkms_config_encoder pointer
+ * @idx: Index of the cursor
+ * @possible_crtc: &struct vkms_config_crtc pointer used as cursor
+ */
+#define vkms_config_encoder_for_each_possible_crtc(encoder_cfg, idx, possible_crtc) \
+	xa_for_each(&(encoder_cfg)->possible_crtcs, idx, (possible_crtc))
+
 /**
  * vkms_config_create() - Create a new VKMS configuration
  * @dev_name: Name of the device
@@ -326,4 +339,20 @@ struct vkms_config_encoder *vkms_config_create_encoder(struct vkms_config *confi
 void vkms_config_destroy_encoder(struct vkms_config *config,
 				 struct vkms_config_encoder *encoder_cfg);
 
+/**
+ * vkms_config_encoder_attach_crtc - Attach a encoder to a CRTC
+ * @encoder_cfg: Encoder to attach
+ * @crtc_cfg: CRTC to attach @encoder_cfg to
+ */
+int __must_check vkms_config_encoder_attach_crtc(struct vkms_config_encoder *encoder_cfg,
+						 struct vkms_config_crtc *crtc_cfg);
+
+/**
+ * vkms_config_encoder_detach_crtc - Detach a encoder from a CRTC
+ * @encoder_cfg: Encoder to detach
+ * @crtc_cfg: CRTC to detach @encoder_cfg from
+ */
+void vkms_config_encoder_detach_crtc(struct vkms_config_encoder *encoder_cfg,
+				     struct vkms_config_crtc *crtc_cfg);
+
 #endif /* _VKMS_CONFIG_H_ */
diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
index f63bc8e3014b..8920d6b5d105 100644
--- a/drivers/gpu/drm/vkms/vkms_output.c
+++ b/drivers/gpu/drm/vkms/vkms_output.c
@@ -9,9 +9,9 @@ int vkms_output_init(struct vkms_device *vkmsdev)
 {
 	struct drm_device *dev = &vkmsdev->drm;
 	struct vkms_connector *connector;
-	struct drm_encoder *encoder;
 	struct vkms_config_plane *plane_cfg;
 	struct vkms_config_crtc *crtc_cfg;
+	struct vkms_config_encoder *encoder_cfg;
 	int ret;
 	int writeback;
 
@@ -61,32 +61,41 @@ int vkms_output_init(struct vkms_device *vkmsdev)
 		}
 	}
 
+	vkms_config_for_each_encoder(vkmsdev->config, encoder_cfg) {
+		struct vkms_config_crtc *possible_crtc;
+		unsigned long idx = 0;
+
+		encoder_cfg->encoder = drmm_kzalloc(dev, sizeof(*encoder_cfg->encoder), GFP_KERNEL);
+		if (!encoder_cfg->encoder) {
+			DRM_ERROR("Failed to allocate encoder\n");
+			return -ENOMEM;
+		}
+		ret = drmm_encoder_init(dev, encoder_cfg->encoder, NULL,
+					DRM_MODE_ENCODER_VIRTUAL, NULL);
+		if (ret) {
+			DRM_ERROR("Failed to init encoder\n");
+			return ret;
+		}
+
+		vkms_config_encoder_for_each_possible_crtc(encoder_cfg, idx, possible_crtc) {
+			encoder_cfg->encoder->possible_crtcs |=
+				drm_crtc_mask(&possible_crtc->crtc->crtc);
+		}
+	}
+
 	connector = vkms_connector_init(vkmsdev);
 	if (IS_ERR(connector)) {
 		DRM_ERROR("Failed to init connector\n");
 		return PTR_ERR(connector);
 	}
 
-	encoder = drmm_kzalloc(dev, sizeof(*encoder), GFP_KERNEL);
-	if (!encoder) {
-		DRM_ERROR("Failed to allocate encoder\n");
-		return -ENOMEM;
-	}
-	ret = drmm_encoder_init(dev, encoder, NULL,
-				DRM_MODE_ENCODER_VIRTUAL, NULL);
-	if (ret) {
-		DRM_ERROR("Failed to init encoder\n");
-		return ret;
-	}
-
-	vkms_config_for_each_crtc(vkmsdev->config, crtc_cfg)
-		encoder->possible_crtcs = drm_crtc_mask(&crtc_cfg->crtc->crtc);
-
 	/* Attach the encoder and the connector */
-	ret = drm_connector_attach_encoder(&connector->base, encoder);
-	if (ret) {
-		DRM_ERROR("Failed to attach connector to encoder\n");
-		return ret;
+	vkms_config_for_each_encoder(vkmsdev->config, encoder_cfg) {
+		ret = drm_connector_attach_encoder(&connector->base, encoder_cfg->encoder);
+		if (ret) {
+			DRM_ERROR("Failed to attach connector to encoder\n");
+			return ret;
+		}
 	}
 
 	drm_mode_config_reset(dev);
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 14/15] drm/vkms: Allow to configure multiple connectors
  2025-02-11 11:08 [PATCH v2 00/15] drm/vkms: Allow to configure device José Expósito
                   ` (12 preceding siblings ...)
  2025-02-11 11:09 ` [PATCH v2 13/15] drm/vkms: Allow to attach encoders and CRTCs José Expósito
@ 2025-02-11 11:09 ` José Expósito
  2025-02-13 13:59   ` Louis Chauvet
  2025-02-11 11:09 ` [PATCH v2 15/15] drm/vkms: Allow to attach connectors and encoders José Expósito
  14 siblings, 1 reply; 32+ messages in thread
From: José Expósito @ 2025-02-11 11:09 UTC (permalink / raw)
  To: louis.chauvet
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel,
	José Expósito

Add a list of connectors to vkms_config and helper functions to add and
remove as many connectors as wanted.

For backwards compatibility, add one enabled connector to the default
configuration.

A future patch will allow to attach connectors and encoders, but for the
moment there are no changes in the way the output is configured.

Co-developed-by: Louis Chauvet <louis.chauvet@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
Signed-off-by: José Expósito <jose.exposito89@gmail.com>
---
 .clang-format                                 |  1 +
 drivers/gpu/drm/vkms/tests/vkms_config_test.c | 74 +++++++++++++++++++
 drivers/gpu/drm/vkms/vkms_config.c            | 50 +++++++++++++
 drivers/gpu/drm/vkms/vkms_config.h            | 42 +++++++++++
 drivers/gpu/drm/vkms/vkms_connector.c         | 11 +++
 5 files changed, 178 insertions(+)

diff --git a/.clang-format b/.clang-format
index 5d21c0e4edbd..ca49832993c5 100644
--- a/.clang-format
+++ b/.clang-format
@@ -690,6 +690,7 @@ ForEachMacros:
   - 'v4l2_m2m_for_each_src_buf'
   - 'v4l2_m2m_for_each_src_buf_safe'
   - 'virtio_device_for_each_vq'
+  - 'vkms_config_for_each_connector'
   - 'vkms_config_for_each_crtc'
   - 'vkms_config_for_each_encoder'
   - 'vkms_config_for_each_plane'
diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
index 7458d175acb6..cba7e9d2fcad 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_config_test.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
@@ -27,6 +27,7 @@ static void vkms_config_test_empty_config(struct kunit *test)
 	KUNIT_EXPECT_TRUE(test, list_empty(&config->planes));
 	KUNIT_EXPECT_TRUE(test, list_empty(&config->crtcs));
 	KUNIT_EXPECT_TRUE(test, list_empty(&config->encoders));
+	KUNIT_EXPECT_TRUE(test, list_empty(&config->connectors));
 
 	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
 
@@ -103,6 +104,9 @@ static void vkms_config_test_default_config(struct kunit *test)
 	/* Encoders */
 	KUNIT_EXPECT_EQ(test, list_count_nodes(&config->encoders), 1);
 
+	/* Connectors */
+	KUNIT_EXPECT_EQ(test, list_count_nodes(&config->connectors), 1);
+
 	KUNIT_EXPECT_TRUE(test, vkms_config_is_valid(config));
 
 	vkms_config_destroy(config);
@@ -232,6 +236,51 @@ static void vkms_config_test_get_encoders(struct kunit *test)
 	vkms_config_destroy(config);
 }
 
+static void vkms_config_test_get_connectors(struct kunit *test)
+{
+	struct vkms_config *config;
+	struct vkms_config_connector *connector_cfg;
+	struct vkms_config_connector *connector_cfg1, *connector_cfg2;
+	int n_connectors = 0;
+
+	config = vkms_config_create("test");
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
+
+	vkms_config_for_each_connector(config, connector_cfg)
+		n_connectors++;
+	KUNIT_ASSERT_EQ(test, n_connectors, 0);
+
+	connector_cfg1 = vkms_config_create_connector(config);
+	vkms_config_for_each_connector(config, connector_cfg) {
+		n_connectors++;
+		if (connector_cfg != connector_cfg1)
+			KUNIT_FAIL(test, "Unexpected connector");
+	}
+	KUNIT_ASSERT_EQ(test, n_connectors, 1);
+	n_connectors = 0;
+
+	connector_cfg2 = vkms_config_create_connector(config);
+	vkms_config_for_each_connector(config, connector_cfg) {
+		n_connectors++;
+		if (connector_cfg != connector_cfg1 &&
+		    connector_cfg != connector_cfg2)
+			KUNIT_FAIL(test, "Unexpected connector");
+	}
+	KUNIT_ASSERT_EQ(test, n_connectors, 2);
+	n_connectors = 0;
+
+	vkms_config_destroy_connector(connector_cfg2);
+	vkms_config_for_each_connector(config, connector_cfg) {
+		n_connectors++;
+		if (connector_cfg != connector_cfg1)
+			KUNIT_FAIL(test, "Unexpected connector");
+	}
+	KUNIT_ASSERT_EQ(test, n_connectors, 1);
+	n_connectors = 0;
+
+	vkms_config_destroy(config);
+}
+
 static void vkms_config_test_invalid_plane_number(struct kunit *test)
 {
 	struct vkms_config *config;
@@ -439,6 +488,29 @@ static void vkms_config_test_valid_encoder_possible_crtcs(struct kunit *test)
 	vkms_config_destroy(config);
 }
 
+static void vkms_config_test_invalid_connector_number(struct kunit *test)
+{
+	struct vkms_config *config;
+	struct vkms_config_connector *connector_cfg;
+	int n;
+
+	config = vkms_config_default_create(false, false, false);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
+
+	/* Invalid: No connectors */
+	connector_cfg = list_first_entry(&config->connectors, typeof(*connector_cfg), link);
+	vkms_config_destroy_connector(connector_cfg);
+	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
+
+	/* Invalid: Too many connectors */
+	for (n = 0; n <= 32; n++)
+		connector_cfg = vkms_config_create_connector(config);
+
+	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
+
+	vkms_config_destroy(config);
+}
+
 static void vkms_config_test_plane_attach_crtc(struct kunit *test)
 {
 	struct vkms_config *config;
@@ -635,12 +707,14 @@ static struct kunit_case vkms_config_test_cases[] = {
 	KUNIT_CASE(vkms_config_test_get_planes),
 	KUNIT_CASE(vkms_config_test_get_crtcs),
 	KUNIT_CASE(vkms_config_test_get_encoders),
+	KUNIT_CASE(vkms_config_test_get_connectors),
 	KUNIT_CASE(vkms_config_test_invalid_plane_number),
 	KUNIT_CASE(vkms_config_test_valid_plane_type),
 	KUNIT_CASE(vkms_config_test_valid_plane_possible_crtcs),
 	KUNIT_CASE(vkms_config_test_invalid_crtc_number),
 	KUNIT_CASE(vkms_config_test_invalid_encoder_number),
 	KUNIT_CASE(vkms_config_test_valid_encoder_possible_crtcs),
+	KUNIT_CASE(vkms_config_test_invalid_connector_number),
 	KUNIT_CASE(vkms_config_test_plane_attach_crtc),
 	KUNIT_CASE(vkms_config_test_plane_get_possible_crtcs),
 	KUNIT_CASE(vkms_config_test_encoder_get_possible_crtcs),
diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
index f727c0009489..d52280d3bbee 100644
--- a/drivers/gpu/drm/vkms/vkms_config.c
+++ b/drivers/gpu/drm/vkms/vkms_config.c
@@ -24,6 +24,7 @@ struct vkms_config *vkms_config_create(const char *dev_name)
 	INIT_LIST_HEAD(&config->planes);
 	INIT_LIST_HEAD(&config->crtcs);
 	INIT_LIST_HEAD(&config->encoders);
+	INIT_LIST_HEAD(&config->connectors);
 
 	return config;
 }
@@ -36,6 +37,7 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
 	struct vkms_config_plane *plane_cfg;
 	struct vkms_config_crtc *crtc_cfg;
 	struct vkms_config_encoder *encoder_cfg;
+	struct vkms_config_connector *connector_cfg;
 	int n;
 
 	config = vkms_config_create(DEFAULT_DEVICE_NAME);
@@ -87,6 +89,10 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
 	if (vkms_config_encoder_attach_crtc(encoder_cfg, crtc_cfg))
 		goto err_alloc;
 
+	connector_cfg = vkms_config_create_connector(config);
+	if (IS_ERR(connector_cfg))
+		goto err_alloc;
+
 	return config;
 
 err_alloc:
@@ -99,6 +105,7 @@ void vkms_config_destroy(struct vkms_config *config)
 	struct vkms_config_plane *plane_cfg, *plane_tmp;
 	struct vkms_config_crtc *crtc_cfg, *crtc_tmp;
 	struct vkms_config_encoder *encoder_cfg, *encoder_tmp;
+	struct vkms_config_connector *connector_cfg, *connector_tmp;
 
 	list_for_each_entry_safe(plane_cfg, plane_tmp, &config->planes, link)
 		vkms_config_destroy_plane(plane_cfg);
@@ -109,6 +116,9 @@ void vkms_config_destroy(struct vkms_config *config)
 	list_for_each_entry_safe(encoder_cfg, encoder_tmp, &config->encoders, link)
 		vkms_config_destroy_encoder(config, encoder_cfg);
 
+	list_for_each_entry_safe(connector_cfg, connector_tmp, &config->connectors, link)
+		vkms_config_destroy_connector(connector_cfg);
+
 	kfree_const(config->dev_name);
 	kfree(config);
 }
@@ -251,6 +261,20 @@ static bool valid_encoder_possible_crtcs(struct vkms_config *config)
 	return true;
 }
 
+static bool valid_connector_number(struct vkms_config *config)
+{
+	struct drm_device *dev = &config->dev->drm;
+	size_t n_connectors;
+
+	n_connectors = list_count_nodes(&config->connectors);
+	if (n_connectors <= 0 || n_connectors >= 32) {
+		drm_info(dev, "The number of connectors must be between 1 and 31\n");
+		return false;
+	}
+
+	return true;
+}
+
 bool vkms_config_is_valid(struct vkms_config *config)
 {
 	struct vkms_config_crtc *crtc_cfg;
@@ -264,6 +288,9 @@ bool vkms_config_is_valid(struct vkms_config *config)
 	if (!valid_encoder_number(config))
 		return false;
 
+	if (!valid_connector_number(config))
+		return false;
+
 	if (!valid_plane_possible_crtcs(config))
 		return false;
 
@@ -287,6 +314,7 @@ static int vkms_config_show(struct seq_file *m, void *data)
 	struct vkms_config_plane *plane_cfg;
 	struct vkms_config_crtc *crtc_cfg;
 	struct vkms_config_encoder *encoder_cfg;
+	struct vkms_config_connector *connector_cfg;
 
 	dev_name = vkms_config_get_device_name((struct vkms_config *)vkmsdev->config);
 	seq_printf(m, "dev_name=%s\n", dev_name);
@@ -306,6 +334,9 @@ static int vkms_config_show(struct seq_file *m, void *data)
 	vkms_config_for_each_encoder(vkmsdev->config, encoder_cfg)
 		seq_puts(m, "encoder\n");
 
+	vkms_config_for_each_connector(vkmsdev->config, connector_cfg)
+		seq_puts(m, "connector\n");
+
 	return 0;
 }
 
@@ -494,3 +525,22 @@ void vkms_config_encoder_detach_crtc(struct vkms_config_encoder *encoder_cfg,
 			xa_erase(&encoder_cfg->possible_crtcs, idx);
 	}
 }
+
+struct vkms_config_connector *vkms_config_create_connector(struct vkms_config *config)
+{
+	struct vkms_config_connector *connector_cfg;
+
+	connector_cfg = kzalloc(sizeof(*connector_cfg), GFP_KERNEL);
+	if (!connector_cfg)
+		return ERR_PTR(-ENOMEM);
+
+	list_add_tail(&connector_cfg->link, &config->connectors);
+
+	return connector_cfg;
+}
+
+void vkms_config_destroy_connector(struct vkms_config_connector *connector_cfg)
+{
+	list_del(&connector_cfg->link);
+	kfree(connector_cfg);
+}
diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
index 28c24afebe1e..8451c2f127b6 100644
--- a/drivers/gpu/drm/vkms/vkms_config.h
+++ b/drivers/gpu/drm/vkms/vkms_config.h
@@ -16,6 +16,7 @@
  * @planes: List of planes configured for the device
  * @crtcs: List of CRTCs configured for the device
  * @encoders: List of encoders configured for the device
+ * @connectors: List of connectors configured for the device
  * @dev: Used to store the current VKMS device. Only set when the device is instantiated.
  */
 struct vkms_config {
@@ -23,6 +24,7 @@ struct vkms_config {
 	struct list_head planes;
 	struct list_head crtcs;
 	struct list_head encoders;
+	struct list_head connectors;
 	struct vkms_device *dev;
 };
 
@@ -86,6 +88,22 @@ struct vkms_config_encoder {
 	struct drm_encoder *encoder;
 };
 
+/**
+ * struct vkms_config_connector
+ *
+ * @link: Link to the others connector in vkms_config
+ * @connector: Internal usage. This pointer should never be considered as valid.
+ *             It can be used to store a temporary reference to a VKMS connector
+ *             during device creation. This pointer is not managed by the
+ *             configuration and must be managed by other means.
+ */
+struct vkms_config_connector {
+	struct list_head link;
+
+	/* Internal usage */
+	struct vkms_connector *connector;
+};
+
 /**
  * vkms_config_for_each_plane - Iterate over the vkms_config planes
  * @config: &struct vkms_config pointer
@@ -110,6 +128,14 @@ struct vkms_config_encoder {
 #define vkms_config_for_each_encoder(config, encoder_cfg) \
 	list_for_each_entry((encoder_cfg), &(config)->encoders, link)
 
+/**
+ * vkms_config_for_each_connector - Iterate over the vkms_config connectors
+ * @config: &struct vkms_config pointer
+ * @connector_cfg: &struct vkms_config_connector pointer used as cursor
+ */
+#define vkms_config_for_each_connector(config, connector_cfg) \
+	list_for_each_entry((connector_cfg), &(config)->connectors, link)
+
 /**
  * vkms_config_plane_for_each_possible_crtc - Iterate over the vkms_config_plane
  * possible CRTCs
@@ -355,4 +381,20 @@ int __must_check vkms_config_encoder_attach_crtc(struct vkms_config_encoder *enc
 void vkms_config_encoder_detach_crtc(struct vkms_config_encoder *encoder_cfg,
 				     struct vkms_config_crtc *crtc_cfg);
 
+/**
+ * vkms_config_create_connector() - Add a new connector configuration
+ * @config: Configuration to add the connector to
+ *
+ * Returns:
+ * The new connector configuration or an error. Call
+ * vkms_config_destroy_connector() to free the returned connector configuration.
+ */
+struct vkms_config_connector *vkms_config_create_connector(struct vkms_config *config);
+
+/**
+ * vkms_config_destroy_connector() - Remove and free a connector configuration
+ * @connector_cfg: Connector configuration to destroy
+ */
+void vkms_config_destroy_connector(struct vkms_config_connector *connector_cfg);
+
 #endif /* _VKMS_CONFIG_H_ */
diff --git a/drivers/gpu/drm/vkms/vkms_connector.c b/drivers/gpu/drm/vkms/vkms_connector.c
index ab8b52a84151..48b10cba322a 100644
--- a/drivers/gpu/drm/vkms/vkms_connector.c
+++ b/drivers/gpu/drm/vkms/vkms_connector.c
@@ -25,8 +25,19 @@ static int vkms_conn_get_modes(struct drm_connector *connector)
 	return count;
 }
 
+static struct drm_encoder *vkms_conn_best_encoder(struct drm_connector *connector)
+{
+	struct drm_encoder *encoder;
+
+	drm_connector_for_each_possible_encoder(connector, encoder)
+		return encoder;
+
+	return NULL;
+}
+
 static const struct drm_connector_helper_funcs vkms_conn_helper_funcs = {
 	.get_modes    = vkms_conn_get_modes,
+	.best_encoder = vkms_conn_best_encoder,
 };
 
 struct vkms_connector *vkms_connector_init(struct vkms_device *vkmsdev)
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 15/15] drm/vkms: Allow to attach connectors and encoders
  2025-02-11 11:08 [PATCH v2 00/15] drm/vkms: Allow to configure device José Expósito
                   ` (13 preceding siblings ...)
  2025-02-11 11:09 ` [PATCH v2 14/15] drm/vkms: Allow to configure multiple connectors José Expósito
@ 2025-02-11 11:09 ` José Expósito
  2025-02-13 13:59   ` Louis Chauvet
  14 siblings, 1 reply; 32+ messages in thread
From: José Expósito @ 2025-02-11 11:09 UTC (permalink / raw)
  To: louis.chauvet
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel,
	José Expósito

Add a list of possible encoders to the connector configuration and
helpers to attach and detach them.

Now that the default configuration has its connector and encoder
correctly, configure the output following the configuration.

Co-developed-by: Louis Chauvet <louis.chauvet@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
Signed-off-by: José Expósito <jose.exposito89@gmail.com>
---
 .clang-format                                 |  1 +
 drivers/gpu/drm/vkms/tests/vkms_config_test.c | 94 +++++++++++++++++++
 drivers/gpu/drm/vkms/vkms_config.c            | 60 ++++++++++++
 drivers/gpu/drm/vkms/vkms_config.h            | 29 ++++++
 drivers/gpu/drm/vkms/vkms_output.c            | 33 ++++---
 5 files changed, 204 insertions(+), 13 deletions(-)

diff --git a/.clang-format b/.clang-format
index ca49832993c5..7630990aa07a 100644
--- a/.clang-format
+++ b/.clang-format
@@ -694,6 +694,7 @@ ForEachMacros:
   - 'vkms_config_for_each_crtc'
   - 'vkms_config_for_each_encoder'
   - 'vkms_config_for_each_plane'
+  - 'vkms_config_connector_for_each_possible_encoder'
   - 'vkms_config_encoder_for_each_possible_crtc'
   - 'vkms_config_plane_for_each_possible_crtc'
   - 'while_for_each_ftrace_op'
diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
index cba7e9d2fcad..2d104ecfde3b 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_config_test.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
@@ -511,6 +511,27 @@ static void vkms_config_test_invalid_connector_number(struct kunit *test)
 	vkms_config_destroy(config);
 }
 
+static void vkms_config_test_valid_connector_possible_encoders(struct kunit *test)
+{
+	struct vkms_config *config;
+	struct vkms_config_encoder *encoder_cfg;
+	struct vkms_config_connector *connector_cfg;
+
+	config = vkms_config_default_create(false, false, false);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
+
+	encoder_cfg = list_first_entry(&config->encoders,
+				       typeof(*encoder_cfg), link);
+	connector_cfg = list_first_entry(&config->connectors,
+					 typeof(*connector_cfg), link);
+
+	/* Invalid: Connector without a possible encoder */
+	vkms_config_connector_detach_encoder(connector_cfg, encoder_cfg);
+	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
+
+	vkms_config_destroy(config);
+}
+
 static void vkms_config_test_plane_attach_crtc(struct kunit *test)
 {
 	struct vkms_config *config;
@@ -700,6 +721,77 @@ static void vkms_config_test_encoder_get_possible_crtcs(struct kunit *test)
 	vkms_config_destroy(config);
 }
 
+static void vkms_config_test_connector_get_possible_encoders(struct kunit *test)
+{
+	struct vkms_config *config;
+	struct vkms_config_connector *connector_cfg1, *connector_cfg2;
+	struct vkms_config_encoder *encoder_cfg1, *encoder_cfg2;
+	struct vkms_config_encoder *possible_encoder;
+	unsigned long idx = 0;
+	int n_encoders = 0;
+	int err;
+
+	config = vkms_config_create("test");
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
+
+	connector_cfg1 = vkms_config_create_connector(config);
+	connector_cfg2 = vkms_config_create_connector(config);
+	encoder_cfg1 = vkms_config_create_encoder(config);
+	encoder_cfg2 = vkms_config_create_encoder(config);
+
+	/* No possible encoders */
+	vkms_config_connector_for_each_possible_encoder(connector_cfg1, idx,
+							possible_encoder)
+		KUNIT_FAIL(test, "Unexpected possible encoder");
+
+	vkms_config_connector_for_each_possible_encoder(connector_cfg2, idx,
+							possible_encoder)
+		KUNIT_FAIL(test, "Unexpected possible encoder");
+
+	/* Connector 1 attached to encoders 1 and 2 */
+	err = vkms_config_connector_attach_encoder(connector_cfg1, encoder_cfg1);
+	KUNIT_EXPECT_EQ(test, err, 0);
+	err = vkms_config_connector_attach_encoder(connector_cfg1, encoder_cfg2);
+	KUNIT_EXPECT_EQ(test, err, 0);
+
+	vkms_config_connector_for_each_possible_encoder(connector_cfg1, idx,
+							possible_encoder) {
+		n_encoders++;
+		if (possible_encoder != encoder_cfg1 &&
+		    possible_encoder != encoder_cfg2)
+			KUNIT_FAIL(test, "Unexpected possible encoder");
+	}
+	KUNIT_ASSERT_EQ(test, n_encoders, 2);
+	n_encoders = 0;
+
+	vkms_config_connector_for_each_possible_encoder(connector_cfg2, idx,
+							possible_encoder)
+		KUNIT_FAIL(test, "Unexpected possible encoder");
+
+	/* Connector 1 attached to encoder 1 and connector 2 to encoder 2 */
+	vkms_config_connector_detach_encoder(connector_cfg1, encoder_cfg2);
+	vkms_config_connector_for_each_possible_encoder(connector_cfg1, idx,
+							possible_encoder) {
+		n_encoders++;
+		if (possible_encoder != encoder_cfg1)
+			KUNIT_FAIL(test, "Unexpected possible encoder");
+	}
+	KUNIT_ASSERT_EQ(test, n_encoders, 1);
+	n_encoders = 0;
+
+	err = vkms_config_connector_attach_encoder(connector_cfg2, encoder_cfg2);
+	KUNIT_EXPECT_EQ(test, err, 0);
+	vkms_config_connector_for_each_possible_encoder(connector_cfg2, idx,
+							possible_encoder) {
+		n_encoders++;
+		if (possible_encoder != encoder_cfg2)
+			KUNIT_FAIL(test, "Unexpected possible encoder");
+	}
+	KUNIT_ASSERT_EQ(test, n_encoders, 1);
+
+	vkms_config_destroy(config);
+}
+
 static struct kunit_case vkms_config_test_cases[] = {
 	KUNIT_CASE(vkms_config_test_empty_config),
 	KUNIT_CASE_PARAM(vkms_config_test_default_config,
@@ -715,9 +807,11 @@ static struct kunit_case vkms_config_test_cases[] = {
 	KUNIT_CASE(vkms_config_test_invalid_encoder_number),
 	KUNIT_CASE(vkms_config_test_valid_encoder_possible_crtcs),
 	KUNIT_CASE(vkms_config_test_invalid_connector_number),
+	KUNIT_CASE(vkms_config_test_valid_connector_possible_encoders),
 	KUNIT_CASE(vkms_config_test_plane_attach_crtc),
 	KUNIT_CASE(vkms_config_test_plane_get_possible_crtcs),
 	KUNIT_CASE(vkms_config_test_encoder_get_possible_crtcs),
+	KUNIT_CASE(vkms_config_test_connector_get_possible_encoders),
 	{}
 };
 
diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
index d52280d3bbee..3d95dc713151 100644
--- a/drivers/gpu/drm/vkms/vkms_config.c
+++ b/drivers/gpu/drm/vkms/vkms_config.c
@@ -93,6 +93,9 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
 	if (IS_ERR(connector_cfg))
 		goto err_alloc;
 
+	if (vkms_config_connector_attach_encoder(connector_cfg, encoder_cfg))
+		goto err_alloc;
+
 	return config;
 
 err_alloc:
@@ -275,6 +278,22 @@ static bool valid_connector_number(struct vkms_config *config)
 	return true;
 }
 
+static bool valid_connector_possible_encoders(struct vkms_config *config)
+{
+	struct drm_device *dev = &config->dev->drm;
+	struct vkms_config_connector *connector_cfg;
+
+	vkms_config_for_each_connector(config, connector_cfg) {
+		if (xa_empty(&connector_cfg->possible_encoders)) {
+			drm_info(dev,
+				 "All connectors must have at least one possible encoder\n");
+			return false;
+		}
+	}
+
+	return true;
+}
+
 bool vkms_config_is_valid(struct vkms_config *config)
 {
 	struct vkms_config_crtc *crtc_cfg;
@@ -302,6 +321,9 @@ bool vkms_config_is_valid(struct vkms_config *config)
 	if (!valid_encoder_possible_crtcs(config))
 		return false;
 
+	if (!valid_connector_possible_encoders(config))
+		return false;
+
 	return true;
 }
 
@@ -493,6 +515,11 @@ struct vkms_config_encoder *vkms_config_create_encoder(struct vkms_config *confi
 void vkms_config_destroy_encoder(struct vkms_config *config,
 				 struct vkms_config_encoder *encoder_cfg)
 {
+	struct vkms_config_connector *connector_cfg;
+
+	vkms_config_for_each_connector(config, connector_cfg)
+		vkms_config_connector_detach_encoder(connector_cfg, encoder_cfg);
+
 	xa_destroy(&encoder_cfg->possible_crtcs);
 	list_del(&encoder_cfg->link);
 	kfree(encoder_cfg);
@@ -534,6 +561,8 @@ struct vkms_config_connector *vkms_config_create_connector(struct vkms_config *c
 	if (!connector_cfg)
 		return ERR_PTR(-ENOMEM);
 
+	xa_init_flags(&connector_cfg->possible_encoders, XA_FLAGS_ALLOC);
+
 	list_add_tail(&connector_cfg->link, &config->connectors);
 
 	return connector_cfg;
@@ -541,6 +570,37 @@ struct vkms_config_connector *vkms_config_create_connector(struct vkms_config *c
 
 void vkms_config_destroy_connector(struct vkms_config_connector *connector_cfg)
 {
+	xa_destroy(&connector_cfg->possible_encoders);
 	list_del(&connector_cfg->link);
 	kfree(connector_cfg);
 }
+
+int __must_check vkms_config_connector_attach_encoder(struct vkms_config_connector *connector_cfg,
+						      struct vkms_config_encoder *encoder_cfg)
+{
+	struct vkms_config_encoder *possible_encoder;
+	unsigned long idx = 0;
+	u32 encoder_idx = 0;
+
+	vkms_config_connector_for_each_possible_encoder(connector_cfg, idx,
+							possible_encoder) {
+		if (possible_encoder == encoder_cfg)
+			return -EINVAL;
+	}
+
+	return xa_alloc(&connector_cfg->possible_encoders, &encoder_idx,
+			encoder_cfg, xa_limit_32b, GFP_KERNEL);
+}
+
+void vkms_config_connector_detach_encoder(struct vkms_config_connector *connector_cfg,
+					  struct vkms_config_encoder *encoder_cfg)
+{
+	struct vkms_config_encoder *possible_encoder;
+	unsigned long idx = 0;
+
+	vkms_config_connector_for_each_possible_encoder(connector_cfg, idx,
+							possible_encoder) {
+		if (possible_encoder == encoder_cfg)
+			xa_erase(&connector_cfg->possible_encoders, idx);
+	}
+}
diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
index 8451c2f127b6..c87513d174f2 100644
--- a/drivers/gpu/drm/vkms/vkms_config.h
+++ b/drivers/gpu/drm/vkms/vkms_config.h
@@ -92,6 +92,7 @@ struct vkms_config_encoder {
  * struct vkms_config_connector
  *
  * @link: Link to the others connector in vkms_config
+ * @possible_encoders: Array of encoders that can be used with this connector
  * @connector: Internal usage. This pointer should never be considered as valid.
  *             It can be used to store a temporary reference to a VKMS connector
  *             during device creation. This pointer is not managed by the
@@ -100,6 +101,8 @@ struct vkms_config_encoder {
 struct vkms_config_connector {
 	struct list_head link;
 
+	struct xarray possible_encoders;
+
 	/* Internal usage */
 	struct vkms_connector *connector;
 };
@@ -156,6 +159,16 @@ struct vkms_config_connector {
 #define vkms_config_encoder_for_each_possible_crtc(encoder_cfg, idx, possible_crtc) \
 	xa_for_each(&(encoder_cfg)->possible_crtcs, idx, (possible_crtc))
 
+/**
+ * vkms_config_connector_for_each_possible_encoder - Iterate over the
+ * vkms_config_connector possible encoders
+ * @connector_cfg: &struct vkms_config_connector pointer
+ * @idx: Index of the cursor
+ * @possible_encoder: &struct vkms_config_encoder pointer used as cursor
+ */
+#define vkms_config_connector_for_each_possible_encoder(connector_cfg, idx, possible_encoder) \
+	xa_for_each(&(connector_cfg)->possible_encoders, idx, (possible_encoder))
+
 /**
  * vkms_config_create() - Create a new VKMS configuration
  * @dev_name: Name of the device
@@ -397,4 +410,20 @@ struct vkms_config_connector *vkms_config_create_connector(struct vkms_config *c
  */
 void vkms_config_destroy_connector(struct vkms_config_connector *connector_cfg);
 
+/**
+ * vkms_config_connector_attach_encoder - Attach a connector to an encoder
+ * @connector_cfg: Connector to attach
+ * @encoder_cfg: Encoder to attach @connector_cfg to
+ */
+int __must_check vkms_config_connector_attach_encoder(struct vkms_config_connector *connector_cfg,
+						      struct vkms_config_encoder *encoder_cfg);
+
+/**
+ * vkms_config_connector_detach_encoder - Detach a connector from an encoder
+ * @connector_cfg: Connector to detach
+ * @encoder_cfg: Encoder to detach @connector_cfg from
+ */
+void vkms_config_connector_detach_encoder(struct vkms_config_connector *connector_cfg,
+					  struct vkms_config_encoder *encoder_cfg);
+
 #endif /* _VKMS_CONFIG_H_ */
diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
index 8920d6b5d105..8d7ca0cdd79f 100644
--- a/drivers/gpu/drm/vkms/vkms_output.c
+++ b/drivers/gpu/drm/vkms/vkms_output.c
@@ -8,10 +8,10 @@
 int vkms_output_init(struct vkms_device *vkmsdev)
 {
 	struct drm_device *dev = &vkmsdev->drm;
-	struct vkms_connector *connector;
 	struct vkms_config_plane *plane_cfg;
 	struct vkms_config_crtc *crtc_cfg;
 	struct vkms_config_encoder *encoder_cfg;
+	struct vkms_config_connector *connector_cfg;
 	int ret;
 	int writeback;
 
@@ -83,22 +83,29 @@ int vkms_output_init(struct vkms_device *vkmsdev)
 		}
 	}
 
-	connector = vkms_connector_init(vkmsdev);
-	if (IS_ERR(connector)) {
-		DRM_ERROR("Failed to init connector\n");
-		return PTR_ERR(connector);
-	}
+	vkms_config_for_each_connector(vkmsdev->config, connector_cfg) {
+		struct vkms_config_encoder *possible_encoder;
+		unsigned long idx = 0;
 
-	/* Attach the encoder and the connector */
-	vkms_config_for_each_encoder(vkmsdev->config, encoder_cfg) {
-		ret = drm_connector_attach_encoder(&connector->base, encoder_cfg->encoder);
-		if (ret) {
-			DRM_ERROR("Failed to attach connector to encoder\n");
-			return ret;
+		connector_cfg->connector = vkms_connector_init(vkmsdev);
+		if (IS_ERR(connector_cfg->connector)) {
+			DRM_ERROR("Failed to init connector\n");
+			return PTR_ERR(connector_cfg->connector);
+		}
+
+		vkms_config_connector_for_each_possible_encoder(connector_cfg,
+								idx,
+								possible_encoder) {
+			ret = drm_connector_attach_encoder(&connector_cfg->connector->base,
+							   possible_encoder->encoder);
+			if (ret) {
+				DRM_ERROR("Failed to attach connector to encoder\n");
+				return ret;
+			}
 		}
 	}
 
 	drm_mode_config_reset(dev);
 
-	return ret;
+	return 0;
 }
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 01/15] drm/vkms: Fix use after free and double free on init error
  2025-02-11 11:08 ` [PATCH v2 01/15] drm/vkms: Fix use after free and double free on init error José Expósito
@ 2025-02-11 14:33   ` Thomas Zimmermann
  2025-02-12  8:52     ` José Expósito
  0 siblings, 1 reply; 32+ messages in thread
From: Thomas Zimmermann @ 2025-02-11 14:33 UTC (permalink / raw)
  To: José Expósito, louis.chauvet
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	airlied, dri-devel, linux-kernel

Hi

Am 11.02.25 um 12:08 schrieb José Expósito:
> If the driver initialization fails, the vkms_exit() function might
> access an uninitialized or freed default_config pointer and it might
> double free it.
>
> Fix both possible errors by initializing default_config only when the
> driver initialization succeeded.

Could you send this patch separately, so that it can go into 
drm-misc-fixes quickly?

Best regards
Thomas

>
> Reported-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Link: https://lore.kernel.org/all/Z5uDHcCmAwiTsGte@louis-chauvet-laptop/
> Fixes: 2df7af93fdad ("drm/vkms: Add vkms_config type")
> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
> ---
>   drivers/gpu/drm/vkms/vkms_drv.c | 15 +++++++++------
>   1 file changed, 9 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c
> index 7c142bfc3bd9..b6de91134a22 100644
> --- a/drivers/gpu/drm/vkms/vkms_drv.c
> +++ b/drivers/gpu/drm/vkms/vkms_drv.c
> @@ -235,17 +235,19 @@ static int __init vkms_init(void)
>   	if (!config)
>   		return -ENOMEM;
>   
> -	default_config = config;
> -
>   	config->cursor = enable_cursor;
>   	config->writeback = enable_writeback;
>   	config->overlay = enable_overlay;
>   
>   	ret = vkms_create(config);
> -	if (ret)
> +	if (ret) {
>   		kfree(config);
> +		return ret;
> +	}
>   
> -	return ret;
> +	default_config = config;
> +
> +	return 0;
>   }
>   
>   static void vkms_destroy(struct vkms_config *config)
> @@ -269,9 +271,10 @@ static void vkms_destroy(struct vkms_config *config)
>   
>   static void __exit vkms_exit(void)
>   {
> -	if (default_config->dev)
> -		vkms_destroy(default_config);
> +	if (!default_config)
> +		return;
>   
> +	vkms_destroy(default_config);
>   	kfree(default_config);
>   }
>   

-- 
--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Frankenstrasse 146, 90461 Nuernberg, Germany
GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman
HRB 36809 (AG Nuernberg)


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 01/15] drm/vkms: Fix use after free and double free on init error
  2025-02-11 14:33   ` Thomas Zimmermann
@ 2025-02-12  8:52     ` José Expósito
  0 siblings, 0 replies; 32+ messages in thread
From: José Expósito @ 2025-02-12  8:52 UTC (permalink / raw)
  To: Thomas Zimmermann
  Cc: louis.chauvet, hamohammed.sa, simona, melissa.srw,
	maarten.lankhorst, mripard, airlied, dri-devel, linux-kernel

Hi Thomas,

On Tue, Feb 11, 2025 at 03:33:55PM +0100, Thomas Zimmermann wrote:
> Hi
> 
> Am 11.02.25 um 12:08 schrieb José Expósito:
> > If the driver initialization fails, the vkms_exit() function might
> > access an uninitialized or freed default_config pointer and it might
> > double free it.
> > 
> > Fix both possible errors by initializing default_config only when the
> > driver initialization succeeded.
> 
> Could you send this patch separately, so that it can go into drm-misc-fixes
> quickly?

Sure, I just sent it as a separate patch.

Thanks,
Jose
 
> Best regards
> Thomas
> 
> > 
> > Reported-by: Louis Chauvet <louis.chauvet@bootlin.com>
> > Link: https://lore.kernel.org/all/Z5uDHcCmAwiTsGte@louis-chauvet-laptop/
> > Fixes: 2df7af93fdad ("drm/vkms: Add vkms_config type")
> > Signed-off-by: José Expósito <jose.exposito89@gmail.com>
> > ---
> >   drivers/gpu/drm/vkms/vkms_drv.c | 15 +++++++++------
> >   1 file changed, 9 insertions(+), 6 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c
> > index 7c142bfc3bd9..b6de91134a22 100644
> > --- a/drivers/gpu/drm/vkms/vkms_drv.c
> > +++ b/drivers/gpu/drm/vkms/vkms_drv.c
> > @@ -235,17 +235,19 @@ static int __init vkms_init(void)
> >   	if (!config)
> >   		return -ENOMEM;
> > -	default_config = config;
> > -
> >   	config->cursor = enable_cursor;
> >   	config->writeback = enable_writeback;
> >   	config->overlay = enable_overlay;
> >   	ret = vkms_create(config);
> > -	if (ret)
> > +	if (ret) {
> >   		kfree(config);
> > +		return ret;
> > +	}
> > -	return ret;
> > +	default_config = config;
> > +
> > +	return 0;
> >   }
> >   static void vkms_destroy(struct vkms_config *config)
> > @@ -269,9 +271,10 @@ static void vkms_destroy(struct vkms_config *config)
> >   static void __exit vkms_exit(void)
> >   {
> > -	if (default_config->dev)
> > -		vkms_destroy(default_config);
> > +	if (!default_config)
> > +		return;
> > +	vkms_destroy(default_config);
> >   	kfree(default_config);
> >   }
> 
> -- 
> --
> Thomas Zimmermann
> Graphics Driver Developer
> SUSE Software Solutions Germany GmbH
> Frankenstrasse 146, 90461 Nuernberg, Germany
> GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman
> HRB 36809 (AG Nuernberg)
> 

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 06/15] drm/vkms: Move default_config creation to its own function
  2025-02-11 11:09 ` [PATCH v2 06/15] drm/vkms: Move default_config creation to its own function José Expósito
@ 2025-02-13 13:59   ` Louis Chauvet
  0 siblings, 0 replies; 32+ messages in thread
From: Louis Chauvet @ 2025-02-13 13:59 UTC (permalink / raw)
  To: José Expósito
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel

On 11/02/25 - 12:09, José Expósito wrote:
> Extract the initialization of the default configuration to a function.
> Refactor, no functional changes.
> 
> Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Co-developed-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: José Expósito <jose.exposito89@gmail.com>

I just tested to build vkms in module, and it does not works. Can you add 
this for the next iteration?

diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
index 1c3b10f9caaa..0af8e6dc0a01 100644
--- a/drivers/gpu/drm/vkms/vkms_config.c
+++ b/drivers/gpu/drm/vkms/vkms_config.c
@@ -36,6 +36,7 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,

        return config;
 }
+EXPORT_SYMBOL_IF_KUNIT(vkms_config_default_create);

 void vkms_config_destroy(struct vkms_config *config)
 {

Thanks,
Louis Chauvet


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 10/15] drm/vkms: Allow to configure multiple CRTCs
  2025-02-11 11:09 ` [PATCH v2 10/15] drm/vkms: Allow to configure multiple CRTCs José Expósito
@ 2025-02-13 13:59   ` Louis Chauvet
  0 siblings, 0 replies; 32+ messages in thread
From: Louis Chauvet @ 2025-02-13 13:59 UTC (permalink / raw)
  To: José Expósito
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel

On 11/02/25 - 12:09, José Expósito wrote:
> Add a list of CRTCs to vkms_config and helper functions to add and
> remove as many CRTCs as wanted.
> 
> For backwards compatibility, add one CRTC to the default configuration.
> 
> A future patch will allow to attach planes and CRTCs, but for the
> moment there are no changes in the way the output is configured.
> 
> Co-developed-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: José Expósito <jose.exposito89@gmail.com>

Build broken as module:

diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
index 0d0afc3047e0..63a3754b84a7 100644
--- a/drivers/gpu/drm/vkms/vkms_config.c
+++ b/drivers/gpu/drm/vkms/vkms_config.c
@@ -247,6 +247,7 @@ struct vkms_config_crtc *vkms_config_create_crtc(struct vkms_config *config)

        return crtc_cfg;
 }
+EXPORT_SYMBOL_IF_KUNIT(vkms_config_create_crtc);

 void vkms_config_destroy_crtc(struct vkms_config *config,
                              struct vkms_config_crtc *crtc_cfg)
@@ -254,3 +255,4 @@ void vkms_config_destroy_crtc(struct vkms_config *config,
        list_del(&crtc_cfg->link);
        kfree(crtc_cfg);
 }
+EXPORT_SYMBOL_IF_KUNIT(vkms_config_destroy_crtc);


> ---
>  .clang-format                                 |  1 +
>  drivers/gpu/drm/vkms/tests/vkms_config_test.c | 73 ++++++++++++++++-
>  drivers/gpu/drm/vkms/vkms_config.c            | 60 +++++++++++++-
>  drivers/gpu/drm/vkms/vkms_config.h            | 78 +++++++++++++++++++
>  drivers/gpu/drm/vkms/vkms_drv.c               |  3 +-
>  5 files changed, 209 insertions(+), 6 deletions(-)
> 
> diff --git a/.clang-format b/.clang-format
> index c585d2a5b395..e7a901c3617d 100644
> --- a/.clang-format
> +++ b/.clang-format
> @@ -690,6 +690,7 @@ ForEachMacros:
>    - 'v4l2_m2m_for_each_src_buf'
>    - 'v4l2_m2m_for_each_src_buf_safe'
>    - 'virtio_device_for_each_vq'
> +  - 'vkms_config_for_each_crtc'
>    - 'vkms_config_for_each_plane'
>    - 'while_for_each_ftrace_op'
>    - 'xa_for_each'
> diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
> index 38ec456d9610..59bd7baea923 100644
> --- a/drivers/gpu/drm/vkms/tests/vkms_config_test.c
> +++ b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
> @@ -25,6 +25,7 @@ static void vkms_config_test_empty_config(struct kunit *test)
>  	KUNIT_EXPECT_STREQ(test, vkms_config_get_device_name(config), "test");
>  
>  	KUNIT_EXPECT_TRUE(test, list_empty(&config->planes));
> +	KUNIT_EXPECT_TRUE(test, list_empty(&config->crtcs));
>  
>  	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
>  
> @@ -49,6 +50,7 @@ static void vkms_config_test_default_config(struct kunit *test)
>  	const struct default_config_case *params = test->param_value;
>  	struct vkms_config *config;
>  	struct vkms_config_plane *plane_cfg;
> +	struct vkms_config_crtc *crtc_cfg;
>  	int n_primaries = 0;
>  	int n_cursors = 0;
>  	int n_overlays = 0;
> @@ -58,8 +60,6 @@ static void vkms_config_test_default_config(struct kunit *test)
>  					    params->enable_overlay);
>  	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
>  
> -	KUNIT_EXPECT_EQ(test, config->writeback, params->enable_writeback);
> -
>  	/* Planes */
>  	list_for_each_entry(plane_cfg, &config->planes, link) {
>  		switch (vkms_config_plane_get_type(plane_cfg)) {
> @@ -80,6 +80,13 @@ static void vkms_config_test_default_config(struct kunit *test)
>  	KUNIT_EXPECT_EQ(test, n_cursors, params->enable_cursor ? 1 : 0);
>  	KUNIT_EXPECT_EQ(test, n_overlays, params->enable_overlay ? 8 : 0);
>  
> +	/* CRTCs */
> +	crtc_cfg = list_first_entry(&config->crtcs, typeof(*crtc_cfg), link);
> +
> +	KUNIT_EXPECT_EQ(test, list_count_nodes(&config->crtcs), 1);

Can you switch the two lines? First test we only have one crtc, then get 
it.

> +	KUNIT_EXPECT_EQ(test, vkms_config_crtc_get_writeback(crtc_cfg),
> +			params->enable_writeback);
> +
>  	KUNIT_EXPECT_TRUE(test, vkms_config_is_valid(config));
>  
>  	vkms_config_destroy(config);
> @@ -128,6 +135,43 @@ static void vkms_config_test_get_planes(struct kunit *test)
>  	vkms_config_destroy(config);
>  }
>  
> +static void vkms_config_test_get_crtcs(struct kunit *test)
> +{
> +	struct vkms_config *config;
> +	struct vkms_config_crtc *crtc_cfg;
> +	struct vkms_config_crtc *crtc_cfg1, *crtc_cfg2;
> +
> +	config = vkms_config_create("test");
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
> +
> +	KUNIT_ASSERT_EQ(test, vkms_config_get_num_crtcs(config), 0);
> +	vkms_config_for_each_crtc(config, crtc_cfg)
> +		KUNIT_FAIL(test, "Unexpected CRTC");
> +
> +	crtc_cfg1 = vkms_config_create_crtc(config);
> +	KUNIT_ASSERT_EQ(test, vkms_config_get_num_crtcs(config), 1);
> +	vkms_config_for_each_crtc(config, crtc_cfg) {
> +		if (crtc_cfg != crtc_cfg1)
> +			KUNIT_FAIL(test, "Unexpected CRTC");
> +	}
> +
> +	crtc_cfg2 = vkms_config_create_crtc(config);
> +	KUNIT_ASSERT_EQ(test, vkms_config_get_num_crtcs(config), 2);
> +	vkms_config_for_each_crtc(config, crtc_cfg) {
> +		if (crtc_cfg != crtc_cfg1 && crtc_cfg != crtc_cfg2)
> +			KUNIT_FAIL(test, "Unexpected CRTC");
> +	}
> +
> +	vkms_config_destroy_crtc(config, crtc_cfg2);
> +	KUNIT_ASSERT_EQ(test, vkms_config_get_num_crtcs(config), 1);
> +	vkms_config_for_each_crtc(config, crtc_cfg) {
> +		if (crtc_cfg != crtc_cfg1)
> +			KUNIT_FAIL(test, "Unexpected CRTC");
> +	}
> +
> +	vkms_config_destroy(config);
> +}
> +
>  static void vkms_config_test_invalid_plane_number(struct kunit *test)
>  {
>  	struct vkms_config *config;
> @@ -192,13 +236,38 @@ static void vkms_config_test_valid_plane_type(struct kunit *test)
>  	vkms_config_destroy(config);
>  }
>  
> +static void vkms_config_test_invalid_crtc_number(struct kunit *test)
> +{
> +	struct vkms_config *config;
> +	struct vkms_config_crtc *crtc_cfg;
> +	int n;
> +
> +	config = vkms_config_default_create(false, false, false);
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
> +
> +	/* Invalid: No CRTCs */
> +	crtc_cfg = list_first_entry(&config->crtcs, typeof(*crtc_cfg), link);
> +	vkms_config_destroy_crtc(config, crtc_cfg);
> +	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
> +
> +	/* Invalid: Too many CRTCs */
> +	for (n = 0; n <= 32; n++)
> +		vkms_config_create_crtc(config);
> +
> +	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
> +
> +	vkms_config_destroy(config);
> +}
> +
>  static struct kunit_case vkms_config_test_cases[] = {
>  	KUNIT_CASE(vkms_config_test_empty_config),
>  	KUNIT_CASE_PARAM(vkms_config_test_default_config,
>  			 default_config_gen_params),
>  	KUNIT_CASE(vkms_config_test_get_planes),
> +	KUNIT_CASE(vkms_config_test_get_crtcs),
>  	KUNIT_CASE(vkms_config_test_invalid_plane_number),
>  	KUNIT_CASE(vkms_config_test_valid_plane_type),
> +	KUNIT_CASE(vkms_config_test_invalid_crtc_number),
>  	{}
>  };
>  
> diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
> index 8feacfc155be..b08021e62c9f 100644
> --- a/drivers/gpu/drm/vkms/vkms_config.c
> +++ b/drivers/gpu/drm/vkms/vkms_config.c
> @@ -22,6 +22,7 @@ struct vkms_config *vkms_config_create(const char *dev_name)
>  	}
>  
>  	INIT_LIST_HEAD(&config->planes);
> +	INIT_LIST_HEAD(&config->crtcs);
>  
>  	return config;
>  }
> @@ -32,19 +33,23 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
>  {
>  	struct vkms_config *config;
>  	struct vkms_config_plane *plane_cfg;
> +	struct vkms_config_crtc *crtc_cfg;
>  	int n;
>  
>  	config = vkms_config_create(DEFAULT_DEVICE_NAME);
>  	if (IS_ERR(config))
>  		return config;
>  
> -	config->writeback = enable_writeback;
> -
>  	plane_cfg = vkms_config_create_plane(config);
>  	if (IS_ERR(plane_cfg))
>  		goto err_alloc;
>  	vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_PRIMARY);
>  
> +	crtc_cfg = vkms_config_create_crtc(config);
> +	if (IS_ERR(crtc_cfg))
> +		goto err_alloc;
> +	vkms_config_crtc_set_writeback(crtc_cfg, enable_writeback);
> +
>  	if (enable_overlay) {
>  		for (n = 0; n < NUM_OVERLAY_PLANES; n++) {
>  			plane_cfg = vkms_config_create_plane(config);
> @@ -72,10 +77,14 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
>  void vkms_config_destroy(struct vkms_config *config)
>  {
>  	struct vkms_config_plane *plane_cfg, *plane_tmp;
> +	struct vkms_config_crtc *crtc_cfg, *crtc_tmp;
>  
>  	list_for_each_entry_safe(plane_cfg, plane_tmp, &config->planes, link)
>  		vkms_config_destroy_plane(plane_cfg);
>  
> +	list_for_each_entry_safe(crtc_cfg, crtc_tmp, &config->crtcs, link)
> +		vkms_config_destroy_crtc(config, crtc_cfg);
> +
>  	kfree_const(config->dev_name);
>  	kfree(config);
>  }
> @@ -131,11 +140,28 @@ static bool valid_plane_type(struct vkms_config *config)
>  	return true;
>  }
>  
> +static bool valid_crtc_number(struct vkms_config *config)
> +{
> +	struct drm_device *dev = &config->dev->drm;
> +	size_t n_crtcs;
> +
> +	n_crtcs = list_count_nodes(&config->crtcs);
> +	if (n_crtcs <= 0 || n_crtcs >= 32) {
> +		drm_info(dev, "The number of CRTCs must be between 1 and 31\n");
> +		return false;
> +	}
> +
> +	return true;
> +}
> +
>  bool vkms_config_is_valid(struct vkms_config *config)
>  {
>  	if (!valid_plane_number(config))
>  		return false;
>  
> +	if (!valid_crtc_number(config))
> +		return false;
> +
>  	if (!valid_plane_type(config))
>  		return false;
>  
> @@ -149,10 +175,10 @@ static int vkms_config_show(struct seq_file *m, void *data)
>  	struct vkms_device *vkmsdev = drm_device_to_vkms_device(dev);
>  	const char *dev_name;
>  	struct vkms_config_plane *plane_cfg;
> +	struct vkms_config_crtc *crtc_cfg;
>  
>  	dev_name = vkms_config_get_device_name((struct vkms_config *)vkmsdev->config);
>  	seq_printf(m, "dev_name=%s\n", dev_name);
> -	seq_printf(m, "writeback=%d\n", vkmsdev->config->writeback);
>  
>  	vkms_config_for_each_plane(vkmsdev->config, plane_cfg) {
>  		seq_puts(m, "plane:\n");
> @@ -160,6 +186,12 @@ static int vkms_config_show(struct seq_file *m, void *data)
>  			   vkms_config_plane_get_type(plane_cfg));
>  	}
>  
> +	vkms_config_for_each_crtc(vkmsdev->config, crtc_cfg) {
> +		seq_puts(m, "crtc:\n");
> +		seq_printf(m, "\twriteback=%d\n",
> +			   vkms_config_crtc_get_writeback(crtc_cfg));
> +	}
> +
>  	return 0;
>  }
>  
> @@ -193,3 +225,25 @@ void vkms_config_destroy_plane(struct vkms_config_plane *plane_cfg)
>  	list_del(&plane_cfg->link);
>  	kfree(plane_cfg);
>  }
> +
> +struct vkms_config_crtc *vkms_config_create_crtc(struct vkms_config *config)
> +{
> +	struct vkms_config_crtc *crtc_cfg;
> +
> +	crtc_cfg = kzalloc(sizeof(*crtc_cfg), GFP_KERNEL);
> +	if (!crtc_cfg)
> +		return ERR_PTR(-ENOMEM);
> +
> +	vkms_config_crtc_set_writeback(crtc_cfg, false);
> +
> +	list_add_tail(&crtc_cfg->link, &config->crtcs);
> +
> +	return crtc_cfg;
> +}
> +
> +void vkms_config_destroy_crtc(struct vkms_config *config,
> +			      struct vkms_config_crtc *crtc_cfg)
> +{
> +	list_del(&crtc_cfg->link);
> +	kfree(crtc_cfg);
> +}
> diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
> index 1c7ffc27f2a8..0f5937b44948 100644
> --- a/drivers/gpu/drm/vkms/vkms_config.h
> +++ b/drivers/gpu/drm/vkms/vkms_config.h
> @@ -14,12 +14,14 @@
>   * @dev_name: Name of the device
>   * @writeback: If true, a writeback buffer can be attached to the CRTC
>   * @planes: List of planes configured for the device
> + * @crtcs: List of CRTCs configured for the device
>   * @dev: Used to store the current VKMS device. Only set when the device is instantiated.
>   */
>  struct vkms_config {
>  	const char *dev_name;
>  	bool writeback;
>  	struct list_head planes;
> +	struct list_head crtcs;
>  	struct vkms_device *dev;
>  };
>  
> @@ -43,6 +45,25 @@ struct vkms_config_plane {
>  	struct vkms_plane *plane;
>  };
>  
> +/**
> + * struct vkms_config_crtc
> + *
> + * @link: Link to the others CRTCs in vkms_config
> + * @writeback: If true, a writeback buffer can be attached to the CRTC
> + * @crtc: Internal usage. This pointer should never be considered as valid.
> + *        It can be used to store a temporary reference to a VKMS CRTC during
> + *        device creation. This pointer is not managed by the configuration and
> + *        must be managed by other means.
> + */
> +struct vkms_config_crtc {
> +	struct list_head link;
> +
> +	bool writeback;
> +
> +	/* Internal usage */
> +	struct vkms_output *crtc;
> +};
> +
>  /**
>   * vkms_config_for_each_plane - Iterate over the vkms_config planes
>   * @config: &struct vkms_config pointer
> @@ -51,6 +72,14 @@ struct vkms_config_plane {
>  #define vkms_config_for_each_plane(config, plane_cfg) \
>  	list_for_each_entry((plane_cfg), &(config)->planes, link)
>  
> +/**
> + * vkms_config_for_each_crtc - Iterate over the vkms_config CRTCs
> + * @config: &struct vkms_config pointer
> + * @crtc_cfg: &struct vkms_config_crtc pointer used as cursor
> + */
> +#define vkms_config_for_each_crtc(config, crtc_cfg) \
> +	list_for_each_entry((crtc_cfg), &(config)->crtcs, link)
> +
>  /**
>   * vkms_config_create() - Create a new VKMS configuration
>   * @dev_name: Name of the device
> @@ -94,6 +123,15 @@ vkms_config_get_device_name(struct vkms_config *config)
>  	return config->dev_name;
>  }
>  
> +/**
> + * vkms_config_get_num_crtcs() - Return the number of CRTCs in the configuration
> + * @config: Configuration to get the number of CRTCs from
> + */
> +static inline size_t vkms_config_get_num_crtcs(struct vkms_config *config)
> +{
> +	return list_count_nodes(&config->crtcs);
> +}
> +
>  /**
>   * vkms_config_is_valid() - Validate a configuration
>   * @config: Configuration to validate
> @@ -149,4 +187,44 @@ vkms_config_plane_set_type(struct vkms_config_plane *plane_cfg,
>  	plane_cfg->type = type;
>  }
>  
> +/**
> + * vkms_config_create_crtc() - Add a new CRTC configuration
> + * @config: Configuration to add the CRTC to
> + *
> + * Returns:
> + * The new CRTC configuration or an error. Call vkms_config_destroy_crtc() to
> + * free the returned CRTC configuration.
> + */
> +struct vkms_config_crtc *vkms_config_create_crtc(struct vkms_config *config);
> +
> +/**
> + * vkms_config_destroy_crtc() - Remove and free a CRTC configuration
> + * @config: Configuration to remove the CRTC from
> + * @crtc_cfg: CRTC configuration to destroy
> + */
> +void vkms_config_destroy_crtc(struct vkms_config *config,
> +			      struct vkms_config_crtc *crtc_cfg);
> +
> +/**
> + * vkms_config_crtc_get_writeback() - If a writeback connector will be created
> + * @crtc_cfg: CRTC with or without a writeback connector
> + */
> +static inline bool
> +vkms_config_crtc_get_writeback(struct vkms_config_crtc *crtc_cfg)
> +{
> +	return crtc_cfg->writeback;
> +}
> +
> +/**
> + * vkms_config_crtc_set_writeback() - If a writeback connector will be created
> + * @crtc_cfg: Target CRTC
> + * @writeback: Enable or disable the writeback connector
> + */
> +static inline void
> +vkms_config_crtc_set_writeback(struct vkms_config_crtc *crtc_cfg,
> +			       bool writeback)
> +{
> +	crtc_cfg->writeback = writeback;
> +}
> +
>  #endif /* _VKMS_CONFIG_H_ */
> diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c
> index ba977ef09b2b..a24d1655f7b8 100644
> --- a/drivers/gpu/drm/vkms/vkms_drv.c
> +++ b/drivers/gpu/drm/vkms/vkms_drv.c
> @@ -181,7 +181,8 @@ static int vkms_create(struct vkms_config *config)
>  		goto out_devres;
>  	}
>  
> -	ret = drm_vblank_init(&vkms_device->drm, 1);
> +	ret = drm_vblank_init(&vkms_device->drm,
> +			      vkms_config_get_num_crtcs(config));

See my comment on v2, I think we should keep 1 at this point.

>  	if (ret) {
>  		DRM_ERROR("Failed to vblank\n");
>  		goto out_devres;
> -- 
> 2.48.1
> 

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 13/15] drm/vkms: Allow to attach encoders and CRTCs
  2025-02-11 11:09 ` [PATCH v2 13/15] drm/vkms: Allow to attach encoders and CRTCs José Expósito
@ 2025-02-13 13:59   ` Louis Chauvet
  0 siblings, 0 replies; 32+ messages in thread
From: Louis Chauvet @ 2025-02-13 13:59 UTC (permalink / raw)
  To: José Expósito
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel

On 11/02/25 - 12:09, José Expósito wrote:
> Add a list of possible CRTCs to the encoder configuration and helpers to
> attach and detach them.
> 
> Now that the default configuration has its encoder and CRTC correctly
> attached, configure the output following the configuration.
> 
> Co-developed-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: José Expósito <jose.exposito89@gmail.com>

Build is broken when compiling VKMS as a module:

diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
index fdb535be912a..a5e77c752da3 100644
--- a/drivers/gpu/drm/vkms/vkms_config.c
+++ b/drivers/gpu/drm/vkms/vkms_config.c
@@ -497,6 +497,7 @@ int __must_check vkms_config_encoder_attach_crtc(struct vkms_config_encoder *enc
        return xa_alloc(&encoder_cfg->possible_crtcs, &crtc_idx, crtc_cfg,
                        xa_limit_32b, GFP_KERNEL);
 }
+EXPORT_SYMBOL_IF_KUNIT(vkms_config_encoder_attach_crtc);

 void vkms_config_encoder_detach_crtc(struct vkms_config_encoder *encoder_cfg,
                                     struct vkms_config_crtc *crtc_cfg)
@@ -509,3 +510,4 @@ void vkms_config_encoder_detach_crtc(struct vkms_config_encoder *encoder_cfg,
                        xa_erase(&encoder_cfg->possible_crtcs, idx);
        }
 }
+EXPORT_SYMBOL_IF_KUNIT(vkms_config_encoder_detach_crtc);


> ---
>  .clang-format                                 |   1 +
>  drivers/gpu/drm/vkms/tests/vkms_config_test.c | 115 ++++++++++++++++++
>  drivers/gpu/drm/vkms/vkms_config.c            |  77 ++++++++++++
>  drivers/gpu/drm/vkms/vkms_config.h            |  29 +++++
>  drivers/gpu/drm/vkms/vkms_output.c            |  49 +++++---
>  5 files changed, 251 insertions(+), 20 deletions(-)
> 
> diff --git a/.clang-format b/.clang-format
> index c355a2f58eed..5d21c0e4edbd 100644
> --- a/.clang-format
> +++ b/.clang-format
> @@ -693,6 +693,7 @@ ForEachMacros:
>    - 'vkms_config_for_each_crtc'
>    - 'vkms_config_for_each_encoder'
>    - 'vkms_config_for_each_plane'
> +  - 'vkms_config_encoder_for_each_possible_crtc'
>    - 'vkms_config_plane_for_each_possible_crtc'
>    - 'while_for_each_ftrace_op'
>    - 'xa_for_each'
> diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
> index 0bb76a1e6c79..7458d175acb6 100644
> --- a/drivers/gpu/drm/vkms/tests/vkms_config_test.c
> +++ b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
> @@ -260,6 +260,7 @@ static void vkms_config_test_valid_plane_type(struct kunit *test)
>  	struct vkms_config *config;
>  	struct vkms_config_plane *plane_cfg;
>  	struct vkms_config_crtc *crtc_cfg;
> +	struct vkms_config_encoder *encoder_cfg;
>  	int err;
>  
>  	config = vkms_config_default_create(false, false, false);
> @@ -313,6 +314,9 @@ static void vkms_config_test_valid_plane_type(struct kunit *test)
>  
>  	/* Invalid: Second CRTC without primary plane */
>  	crtc_cfg = vkms_config_create_crtc(config);
> +	encoder_cfg = vkms_config_create_encoder(config);
> +	err = vkms_config_encoder_attach_crtc(encoder_cfg, crtc_cfg);
> +	KUNIT_EXPECT_EQ(test, err, 0);
>  	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
>  
>  	/* Valid: Second CRTC with a primary plane */
> @@ -390,6 +394,51 @@ static void vkms_config_test_invalid_encoder_number(struct kunit *test)
>  	vkms_config_destroy(config);
>  }
>  
> +static void vkms_config_test_valid_encoder_possible_crtcs(struct kunit *test)
> +{
> +	struct vkms_config *config;
> +	struct vkms_config_plane *plane_cfg;
> +	struct vkms_config_crtc *crtc_cfg1, *crtc_cfg2;
> +	struct vkms_config_encoder *encoder_cfg;
> +	int err;
> +
> +	config = vkms_config_default_create(false, false, false);
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
> +
> +	crtc_cfg1 = list_first_entry(&config->crtcs, typeof(*crtc_cfg1), link);
> +
> +	/* Invalid: Encoder without a possible CRTC */
> +	encoder_cfg = vkms_config_create_encoder(config);
> +	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
> +
> +	/* Valid: Second CRTC with shared encoder */
> +	crtc_cfg2 = vkms_config_create_crtc(config);
> +
> +	plane_cfg = vkms_config_create_plane(config);
> +	vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_PRIMARY);
> +	err = vkms_config_plane_attach_crtc(plane_cfg, crtc_cfg2);
> +	KUNIT_EXPECT_EQ(test, err, 0);
> +
> +	err = vkms_config_encoder_attach_crtc(encoder_cfg, crtc_cfg1);
> +	KUNIT_EXPECT_EQ(test, err, 0);
> +
> +	err = vkms_config_encoder_attach_crtc(encoder_cfg, crtc_cfg2);
> +	KUNIT_EXPECT_EQ(test, err, 0);
> +
> +	KUNIT_EXPECT_TRUE(test, vkms_config_is_valid(config));
> +
> +	/* Invalid: Second CRTC without encoders */
> +	vkms_config_encoder_detach_crtc(encoder_cfg, crtc_cfg2);
> +	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
> +
> +	/* Valid: First CRTC with 2 possible encoder */
> +	vkms_config_destroy_plane(plane_cfg);
> +	vkms_config_destroy_crtc(config, crtc_cfg2);
> +	KUNIT_EXPECT_TRUE(test, vkms_config_is_valid(config));
> +
> +	vkms_config_destroy(config);
> +}
> +
>  static void vkms_config_test_plane_attach_crtc(struct kunit *test)
>  {
>  	struct vkms_config *config;
> @@ -515,6 +564,70 @@ static void vkms_config_test_plane_get_possible_crtcs(struct kunit *test)
>  	vkms_config_destroy(config);
>  }
>  
> +static void vkms_config_test_encoder_get_possible_crtcs(struct kunit *test)
> +{
> +	struct vkms_config *config;
> +	struct vkms_config_encoder *encoder_cfg1, *encoder_cfg2;
> +	struct vkms_config_crtc *crtc_cfg1, *crtc_cfg2;
> +	struct vkms_config_crtc *possible_crtc;
> +	unsigned long idx = 0;
> +	int n_crtcs = 0;
> +	int err;
> +
> +	config = vkms_config_create("test");
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
> +
> +	encoder_cfg1 = vkms_config_create_encoder(config);
> +	encoder_cfg2 = vkms_config_create_encoder(config);
> +	crtc_cfg1 = vkms_config_create_crtc(config);
> +	crtc_cfg2 = vkms_config_create_crtc(config);
> +
> +	/* No possible CRTCs */
> +	vkms_config_encoder_for_each_possible_crtc(encoder_cfg1, idx, possible_crtc)
> +		KUNIT_FAIL(test, "Unexpected possible CRTC");
> +
> +	vkms_config_encoder_for_each_possible_crtc(encoder_cfg2, idx, possible_crtc)
> +		KUNIT_FAIL(test, "Unexpected possible CRTC");
> +
> +	/* Encoder 1 attached to CRTC 1 and 2 */
> +	err = vkms_config_encoder_attach_crtc(encoder_cfg1, crtc_cfg1);
> +	KUNIT_EXPECT_EQ(test, err, 0);
> +	err = vkms_config_encoder_attach_crtc(encoder_cfg1, crtc_cfg2);
> +	KUNIT_EXPECT_EQ(test, err, 0);
> +
> +	vkms_config_encoder_for_each_possible_crtc(encoder_cfg1, idx, possible_crtc) {
> +		n_crtcs++;
> +		if (possible_crtc != crtc_cfg1 && possible_crtc != crtc_cfg2)
> +			KUNIT_FAIL(test, "Unexpected possible CRTC");
> +	}
> +	KUNIT_ASSERT_EQ(test, n_crtcs, 2);
> +	n_crtcs = 0;
> +
> +	vkms_config_encoder_for_each_possible_crtc(encoder_cfg2, idx, possible_crtc)
> +		KUNIT_FAIL(test, "Unexpected possible CRTC");
> +
> +	/* Encoder 1 attached to CRTC 1 and encoder 2 to CRTC 2 */
> +	vkms_config_encoder_detach_crtc(encoder_cfg1, crtc_cfg2);
> +	vkms_config_encoder_for_each_possible_crtc(encoder_cfg1, idx, possible_crtc) {
> +		n_crtcs++;
> +		if (possible_crtc != crtc_cfg1)
> +			KUNIT_FAIL(test, "Unexpected possible CRTC");
> +	}
> +	KUNIT_ASSERT_EQ(test, n_crtcs, 1);
> +	n_crtcs = 0;
> +
> +	err = vkms_config_encoder_attach_crtc(encoder_cfg2, crtc_cfg2);
> +	KUNIT_EXPECT_EQ(test, err, 0);
> +	vkms_config_encoder_for_each_possible_crtc(encoder_cfg2, idx, possible_crtc) {
> +		n_crtcs++;
> +		if (possible_crtc != crtc_cfg2)
> +			KUNIT_FAIL(test, "Unexpected possible CRTC");
> +	}
> +	KUNIT_ASSERT_EQ(test, n_crtcs, 1);
> +
> +	vkms_config_destroy(config);
> +}
> +
>  static struct kunit_case vkms_config_test_cases[] = {
>  	KUNIT_CASE(vkms_config_test_empty_config),
>  	KUNIT_CASE_PARAM(vkms_config_test_default_config,
> @@ -527,8 +640,10 @@ static struct kunit_case vkms_config_test_cases[] = {
>  	KUNIT_CASE(vkms_config_test_valid_plane_possible_crtcs),
>  	KUNIT_CASE(vkms_config_test_invalid_crtc_number),
>  	KUNIT_CASE(vkms_config_test_invalid_encoder_number),
> +	KUNIT_CASE(vkms_config_test_valid_encoder_possible_crtcs),
>  	KUNIT_CASE(vkms_config_test_plane_attach_crtc),
>  	KUNIT_CASE(vkms_config_test_plane_get_possible_crtcs),
> +	KUNIT_CASE(vkms_config_test_encoder_get_possible_crtcs),
>  	{}
>  };
>  
> diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
> index 0cf6105fe743..f727c0009489 100644
> --- a/drivers/gpu/drm/vkms/vkms_config.c
> +++ b/drivers/gpu/drm/vkms/vkms_config.c
> @@ -84,6 +84,9 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
>  	if (IS_ERR(encoder_cfg))
>  		goto err_alloc;
>  
> +	if (vkms_config_encoder_attach_crtc(encoder_cfg, crtc_cfg))
> +		goto err_alloc;
> +
>  	return config;
>  
>  err_alloc:
> @@ -212,6 +215,42 @@ static bool valid_encoder_number(struct vkms_config *config)
>  	return true;
>  }
>  
> +static bool valid_encoder_possible_crtcs(struct vkms_config *config)
> +{
> +	struct drm_device *dev = &config->dev->drm;

Ditto

> +	struct vkms_config_crtc *crtc_cfg;
> +	struct vkms_config_encoder *encoder_cfg;
> +
> +	vkms_config_for_each_encoder(config, encoder_cfg) {
> +		if (xa_empty(&encoder_cfg->possible_crtcs)) {
> +			drm_info(dev, "All encoders must have at least one possible CRTC\n");
> +			return false;
> +		}
> +	}
> +
> +	vkms_config_for_each_crtc(config, crtc_cfg) {
> +		bool crtc_has_encoder = false;
> +
> +		vkms_config_for_each_encoder(config, encoder_cfg) {
> +			struct vkms_config_crtc *possible_crtc;
> +			unsigned long idx = 0;
> +
> +			vkms_config_encoder_for_each_possible_crtc(encoder_cfg,
> +								   idx, possible_crtc) {
> +				if (possible_crtc == crtc_cfg)
> +					crtc_has_encoder = true;
> +			}
> +		}
> +
> +		if (!crtc_has_encoder) {
> +			drm_info(dev, "All CRTCs must have at least one possible encoder\n");
> +			return false;
> +		}
> +	}
> +
> +	return true;
> +}
> +
>  bool vkms_config_is_valid(struct vkms_config *config)
>  {
>  	struct vkms_config_crtc *crtc_cfg;
> @@ -233,6 +272,9 @@ bool vkms_config_is_valid(struct vkms_config *config)
>  			return false;
>  	}
>  
> +	if (!valid_encoder_possible_crtcs(config))
> +		return false;
> +
>  	return true;
>  }
>  
> @@ -347,10 +389,14 @@ void vkms_config_destroy_crtc(struct vkms_config *config,
>  			      struct vkms_config_crtc *crtc_cfg)
>  {
>  	struct vkms_config_plane *plane_cfg;
> +	struct vkms_config_encoder *encoder_cfg;
>  
>  	vkms_config_for_each_plane(config, plane_cfg)
>  		vkms_config_plane_detach_crtc(plane_cfg, crtc_cfg);
>  
> +	vkms_config_for_each_encoder(config, encoder_cfg)
> +		vkms_config_encoder_detach_crtc(encoder_cfg, crtc_cfg);
> +
>  	list_del(&crtc_cfg->link);
>  	kfree(crtc_cfg);
>  }
> @@ -406,6 +452,8 @@ struct vkms_config_encoder *vkms_config_create_encoder(struct vkms_config *confi
>  	if (!encoder_cfg)
>  		return ERR_PTR(-ENOMEM);
>  
> +	xa_init_flags(&encoder_cfg->possible_crtcs, XA_FLAGS_ALLOC);
> +
>  	list_add_tail(&encoder_cfg->link, &config->encoders);
>  
>  	return encoder_cfg;
> @@ -414,6 +462,35 @@ struct vkms_config_encoder *vkms_config_create_encoder(struct vkms_config *confi
>  void vkms_config_destroy_encoder(struct vkms_config *config,
>  				 struct vkms_config_encoder *encoder_cfg)
>  {
> +	xa_destroy(&encoder_cfg->possible_crtcs);
>  	list_del(&encoder_cfg->link);
>  	kfree(encoder_cfg);
>  }
> +
> +int __must_check vkms_config_encoder_attach_crtc(struct vkms_config_encoder *encoder_cfg,
> +						 struct vkms_config_crtc *crtc_cfg)
> +{
> +	struct vkms_config_crtc *possible_crtc;
> +	unsigned long idx = 0;
> +	u32 crtc_idx = 0;
> +
> +	vkms_config_encoder_for_each_possible_crtc(encoder_cfg, idx, possible_crtc) {
> +		if (possible_crtc == crtc_cfg)
> +			return -EINVAL;
> +	}
> +
> +	return xa_alloc(&encoder_cfg->possible_crtcs, &crtc_idx, crtc_cfg,
> +			xa_limit_32b, GFP_KERNEL);
> +}
> +
> +void vkms_config_encoder_detach_crtc(struct vkms_config_encoder *encoder_cfg,
> +				     struct vkms_config_crtc *crtc_cfg)
> +{
> +	struct vkms_config_crtc *possible_crtc;
> +	unsigned long idx = 0;
> +
> +	vkms_config_encoder_for_each_possible_crtc(encoder_cfg, idx, possible_crtc) {
> +		if (possible_crtc == crtc_cfg)
> +			xa_erase(&encoder_cfg->possible_crtcs, idx);
> +	}
> +}
> diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
> index 2ba80c4c9ce5..28c24afebe1e 100644
> --- a/drivers/gpu/drm/vkms/vkms_config.h
> +++ b/drivers/gpu/drm/vkms/vkms_config.h
> @@ -71,6 +71,7 @@ struct vkms_config_crtc {
>   * struct vkms_config_encoder
>   *
>   * @link: Link to the others encoders in vkms_config
> + * @possible_crtcs: Array of CRTCs that can be used with this encoder
>   * @encoder: Internal usage. This pointer should never be considered as valid.
>   *           It can be used to store a temporary reference to a VKMS encoder
>   *           during device creation. This pointer is not managed by the
> @@ -79,6 +80,8 @@ struct vkms_config_crtc {
>  struct vkms_config_encoder {
>  	struct list_head link;
>  
> +	struct xarray possible_crtcs;
> +
>  	/* Internal usage */
>  	struct drm_encoder *encoder;
>  };
> @@ -117,6 +120,16 @@ struct vkms_config_encoder {
>  #define vkms_config_plane_for_each_possible_crtc(plane_cfg, idx, possible_crtc) \
>  	xa_for_each(&(plane_cfg)->possible_crtcs, idx, (possible_crtc))
>  
> +/**
> + * vkms_config_encoder_for_each_possible_crtc - Iterate over the
> + * vkms_config_encoder possible CRTCs
> + * @encoder_cfg: &struct vkms_config_encoder pointer
> + * @idx: Index of the cursor
> + * @possible_crtc: &struct vkms_config_crtc pointer used as cursor
> + */
> +#define vkms_config_encoder_for_each_possible_crtc(encoder_cfg, idx, possible_crtc) \
> +	xa_for_each(&(encoder_cfg)->possible_crtcs, idx, (possible_crtc))
> +
>  /**
>   * vkms_config_create() - Create a new VKMS configuration
>   * @dev_name: Name of the device
> @@ -326,4 +339,20 @@ struct vkms_config_encoder *vkms_config_create_encoder(struct vkms_config *confi
>  void vkms_config_destroy_encoder(struct vkms_config *config,
>  				 struct vkms_config_encoder *encoder_cfg);
>  
> +/**
> + * vkms_config_encoder_attach_crtc - Attach a encoder to a CRTC
> + * @encoder_cfg: Encoder to attach
> + * @crtc_cfg: CRTC to attach @encoder_cfg to
> + */
> +int __must_check vkms_config_encoder_attach_crtc(struct vkms_config_encoder *encoder_cfg,
> +						 struct vkms_config_crtc *crtc_cfg);
> +
> +/**
> + * vkms_config_encoder_detach_crtc - Detach a encoder from a CRTC
> + * @encoder_cfg: Encoder to detach
> + * @crtc_cfg: CRTC to detach @encoder_cfg from
> + */
> +void vkms_config_encoder_detach_crtc(struct vkms_config_encoder *encoder_cfg,
> +				     struct vkms_config_crtc *crtc_cfg);
> +
>  #endif /* _VKMS_CONFIG_H_ */
> diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
> index f63bc8e3014b..8920d6b5d105 100644
> --- a/drivers/gpu/drm/vkms/vkms_output.c
> +++ b/drivers/gpu/drm/vkms/vkms_output.c
> @@ -9,9 +9,9 @@ int vkms_output_init(struct vkms_device *vkmsdev)
>  {
>  	struct drm_device *dev = &vkmsdev->drm;
>  	struct vkms_connector *connector;
> -	struct drm_encoder *encoder;
>  	struct vkms_config_plane *plane_cfg;
>  	struct vkms_config_crtc *crtc_cfg;
> +	struct vkms_config_encoder *encoder_cfg;
>  	int ret;
>  	int writeback;
>  
> @@ -61,32 +61,41 @@ int vkms_output_init(struct vkms_device *vkmsdev)
>  		}
>  	}
>  
> +	vkms_config_for_each_encoder(vkmsdev->config, encoder_cfg) {
> +		struct vkms_config_crtc *possible_crtc;
> +		unsigned long idx = 0;
> +
> +		encoder_cfg->encoder = drmm_kzalloc(dev, sizeof(*encoder_cfg->encoder), GFP_KERNEL);
> +		if (!encoder_cfg->encoder) {
> +			DRM_ERROR("Failed to allocate encoder\n");
> +			return -ENOMEM;
> +		}
> +		ret = drmm_encoder_init(dev, encoder_cfg->encoder, NULL,
> +					DRM_MODE_ENCODER_VIRTUAL, NULL);
> +		if (ret) {
> +			DRM_ERROR("Failed to init encoder\n");
> +			return ret;
> +		}
> +
> +		vkms_config_encoder_for_each_possible_crtc(encoder_cfg, idx, possible_crtc) {
> +			encoder_cfg->encoder->possible_crtcs |=
> +				drm_crtc_mask(&possible_crtc->crtc->crtc);
> +		}
> +	}
> +
>  	connector = vkms_connector_init(vkmsdev);
>  	if (IS_ERR(connector)) {
>  		DRM_ERROR("Failed to init connector\n");
>  		return PTR_ERR(connector);
>  	}
>  
> -	encoder = drmm_kzalloc(dev, sizeof(*encoder), GFP_KERNEL);
> -	if (!encoder) {
> -		DRM_ERROR("Failed to allocate encoder\n");
> -		return -ENOMEM;
> -	}
> -	ret = drmm_encoder_init(dev, encoder, NULL,
> -				DRM_MODE_ENCODER_VIRTUAL, NULL);
> -	if (ret) {
> -		DRM_ERROR("Failed to init encoder\n");
> -		return ret;
> -	}
> -
> -	vkms_config_for_each_crtc(vkmsdev->config, crtc_cfg)
> -		encoder->possible_crtcs = drm_crtc_mask(&crtc_cfg->crtc->crtc);
> -
>  	/* Attach the encoder and the connector */
> -	ret = drm_connector_attach_encoder(&connector->base, encoder);
> -	if (ret) {
> -		DRM_ERROR("Failed to attach connector to encoder\n");
> -		return ret;
> +	vkms_config_for_each_encoder(vkmsdev->config, encoder_cfg) {
> +		ret = drm_connector_attach_encoder(&connector->base, encoder_cfg->encoder);
> +		if (ret) {
> +			DRM_ERROR("Failed to attach connector to encoder\n");
> +			return ret;
> +		}
>  	}
>  
>  	drm_mode_config_reset(dev);
> -- 
> 2.48.1
> 

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 12/15] drm/vkms: Allow to configure multiple encoders
  2025-02-11 11:09 ` [PATCH v2 12/15] drm/vkms: Allow to configure multiple encoders José Expósito
@ 2025-02-13 13:59   ` Louis Chauvet
  0 siblings, 0 replies; 32+ messages in thread
From: Louis Chauvet @ 2025-02-13 13:59 UTC (permalink / raw)
  To: José Expósito
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel

On 11/02/25 - 12:09, José Expósito wrote:
> Add a list of encoders to vkms_config and helper functions to add and
> remove as many encoders as wanted.
> 
> For backwards compatibility, add one encoder to the default
> configuration.
> 
> A future patch will allow to attach encoders and CRTCs, but for the
> moment there are no changes in the way the output is configured.
> 
> Co-developed-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: José Expósito <jose.exposito89@gmail.com>

The module build is broken:

diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
index c31d3ee1a733..b9d564882cbd 100644
--- a/drivers/gpu/drm/vkms/vkms_config.c
+++ b/drivers/gpu/drm/vkms/vkms_config.c
@@ -423,6 +423,7 @@ struct vkms_config_encoder *vkms_config_create_encoder(struct vkms_config *confi

        return encoder_cfg;
 }
+EXPORT_SYMBOL_IF_KUNIT(vkms_config_create_encoder);

 void vkms_config_destroy_encoder(struct vkms_config *config,
                                 struct vkms_config_encoder *encoder_cfg)
@@ -430,3 +431,4 @@ void vkms_config_destroy_encoder(struct vkms_config *config,
        list_del(&encoder_cfg->link);
        kfree(encoder_cfg);
 }
+EXPORT_SYMBOL_IF_KUNIT(vkms_config_destroy_encoder);


> ---
>  .clang-format                                 |  1 +
>  drivers/gpu/drm/vkms/tests/vkms_config_test.c | 73 +++++++++++++++++++
>  drivers/gpu/drm/vkms/vkms_config.c            | 51 +++++++++++++
>  drivers/gpu/drm/vkms/vkms_config.h            | 44 +++++++++++
>  4 files changed, 169 insertions(+)
> 
> diff --git a/.clang-format b/.clang-format
> index 6f944fa39841..c355a2f58eed 100644
> --- a/.clang-format
> +++ b/.clang-format
> @@ -691,6 +691,7 @@ ForEachMacros:
>    - 'v4l2_m2m_for_each_src_buf_safe'
>    - 'virtio_device_for_each_vq'
>    - 'vkms_config_for_each_crtc'
> +  - 'vkms_config_for_each_encoder'
>    - 'vkms_config_for_each_plane'
>    - 'vkms_config_plane_for_each_possible_crtc'
>    - 'while_for_each_ftrace_op'
> diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
> index 505eebb07a52..0bb76a1e6c79 100644
> --- a/drivers/gpu/drm/vkms/tests/vkms_config_test.c
> +++ b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
> @@ -26,6 +26,7 @@ static void vkms_config_test_empty_config(struct kunit *test)
>  
>  	KUNIT_EXPECT_TRUE(test, list_empty(&config->planes));
>  	KUNIT_EXPECT_TRUE(test, list_empty(&config->crtcs));
> +	KUNIT_EXPECT_TRUE(test, list_empty(&config->encoders));
>  
>  	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
>  
> @@ -99,6 +100,9 @@ static void vkms_config_test_default_config(struct kunit *test)
>  		KUNIT_EXPECT_EQ(test, n_possible_crtcs, 1);
>  	}
>  
> +	/* Encoders */
> +	KUNIT_EXPECT_EQ(test, list_count_nodes(&config->encoders), 1);
> +
>  	KUNIT_EXPECT_TRUE(test, vkms_config_is_valid(config));
>  
>  	vkms_config_destroy(config);
> @@ -184,6 +188,50 @@ static void vkms_config_test_get_crtcs(struct kunit *test)
>  	vkms_config_destroy(config);
>  }
>  
> +static void vkms_config_test_get_encoders(struct kunit *test)
> +{
> +	struct vkms_config *config;
> +	struct vkms_config_encoder *encoder_cfg;
> +	struct vkms_config_encoder *encoder_cfg1, *encoder_cfg2;
> +	int n_encoders = 0;
> +
> +	config = vkms_config_create("test");
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
> +
> +	vkms_config_for_each_encoder(config, encoder_cfg)
> +		n_encoders++;
> +	KUNIT_ASSERT_EQ(test, n_encoders, 0);
> +
> +	encoder_cfg1 = vkms_config_create_encoder(config);
> +	vkms_config_for_each_encoder(config, encoder_cfg) {
> +		n_encoders++;
> +		if (encoder_cfg != encoder_cfg1)
> +			KUNIT_FAIL(test, "Unexpected encoder");
> +	}
> +	KUNIT_ASSERT_EQ(test, n_encoders, 1);
> +	n_encoders = 0;
> +
> +	encoder_cfg2 = vkms_config_create_encoder(config);
> +	vkms_config_for_each_encoder(config, encoder_cfg) {
> +		n_encoders++;
> +		if (encoder_cfg != encoder_cfg1 && encoder_cfg != encoder_cfg2)
> +			KUNIT_FAIL(test, "Unexpected encoder");
> +	}
> +	KUNIT_ASSERT_EQ(test, n_encoders, 2);
> +	n_encoders = 0;
> +
> +	vkms_config_destroy_encoder(config, encoder_cfg2);
> +	vkms_config_for_each_encoder(config, encoder_cfg) {
> +		n_encoders++;
> +		if (encoder_cfg != encoder_cfg1)
> +			KUNIT_FAIL(test, "Unexpected encoder");
> +	}
> +	KUNIT_ASSERT_EQ(test, n_encoders, 1);
> +	n_encoders = 0;
> +
> +	vkms_config_destroy(config);
> +}
> +
>  static void vkms_config_test_invalid_plane_number(struct kunit *test)
>  {
>  	struct vkms_config *config;
> @@ -319,6 +367,29 @@ static void vkms_config_test_invalid_crtc_number(struct kunit *test)
>  	vkms_config_destroy(config);
>  }
>  
> +static void vkms_config_test_invalid_encoder_number(struct kunit *test)
> +{
> +	struct vkms_config *config;
> +	struct vkms_config_encoder *encoder_cfg;
> +	int n;
> +
> +	config = vkms_config_default_create(false, false, false);
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
> +
> +	/* Invalid: No encoders */
> +	encoder_cfg = list_first_entry(&config->encoders, typeof(*encoder_cfg), link);
> +	vkms_config_destroy_encoder(config, encoder_cfg);
> +	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
> +
> +	/* Invalid: Too many encoders */
> +	for (n = 0; n <= 32; n++)
> +		vkms_config_create_encoder(config);
> +
> +	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
> +
> +	vkms_config_destroy(config);
> +}
> +
>  static void vkms_config_test_plane_attach_crtc(struct kunit *test)
>  {
>  	struct vkms_config *config;
> @@ -450,10 +521,12 @@ static struct kunit_case vkms_config_test_cases[] = {
>  			 default_config_gen_params),
>  	KUNIT_CASE(vkms_config_test_get_planes),
>  	KUNIT_CASE(vkms_config_test_get_crtcs),
> +	KUNIT_CASE(vkms_config_test_get_encoders),
>  	KUNIT_CASE(vkms_config_test_invalid_plane_number),
>  	KUNIT_CASE(vkms_config_test_valid_plane_type),
>  	KUNIT_CASE(vkms_config_test_valid_plane_possible_crtcs),
>  	KUNIT_CASE(vkms_config_test_invalid_crtc_number),
> +	KUNIT_CASE(vkms_config_test_invalid_encoder_number),
>  	KUNIT_CASE(vkms_config_test_plane_attach_crtc),
>  	KUNIT_CASE(vkms_config_test_plane_get_possible_crtcs),
>  	{}
> diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
> index d96f9e5f9b72..0cf6105fe743 100644
> --- a/drivers/gpu/drm/vkms/vkms_config.c
> +++ b/drivers/gpu/drm/vkms/vkms_config.c
> @@ -23,6 +23,7 @@ struct vkms_config *vkms_config_create(const char *dev_name)
>  
>  	INIT_LIST_HEAD(&config->planes);
>  	INIT_LIST_HEAD(&config->crtcs);
> +	INIT_LIST_HEAD(&config->encoders);
>  
>  	return config;
>  }
> @@ -34,6 +35,7 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
>  	struct vkms_config *config;
>  	struct vkms_config_plane *plane_cfg;
>  	struct vkms_config_crtc *crtc_cfg;
> +	struct vkms_config_encoder *encoder_cfg;
>  	int n;
>  
>  	config = vkms_config_create(DEFAULT_DEVICE_NAME);
> @@ -78,6 +80,10 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
>  			goto err_alloc;
>  	}
>  
> +	encoder_cfg = vkms_config_create_encoder(config);
> +	if (IS_ERR(encoder_cfg))
> +		goto err_alloc;
> +
>  	return config;
>  
>  err_alloc:
> @@ -89,6 +95,7 @@ void vkms_config_destroy(struct vkms_config *config)
>  {
>  	struct vkms_config_plane *plane_cfg, *plane_tmp;
>  	struct vkms_config_crtc *crtc_cfg, *crtc_tmp;
> +	struct vkms_config_encoder *encoder_cfg, *encoder_tmp;
>  
>  	list_for_each_entry_safe(plane_cfg, plane_tmp, &config->planes, link)
>  		vkms_config_destroy_plane(plane_cfg);
> @@ -96,6 +103,9 @@ void vkms_config_destroy(struct vkms_config *config)
>  	list_for_each_entry_safe(crtc_cfg, crtc_tmp, &config->crtcs, link)
>  		vkms_config_destroy_crtc(config, crtc_cfg);
>  
> +	list_for_each_entry_safe(encoder_cfg, encoder_tmp, &config->encoders, link)
> +		vkms_config_destroy_encoder(config, encoder_cfg);
> +
>  	kfree_const(config->dev_name);
>  	kfree(config);
>  }
> @@ -188,6 +198,20 @@ static bool valid_crtc_number(struct vkms_config *config)
>  	return true;
>  }
>  
> +static bool valid_encoder_number(struct vkms_config *config)
> +{
> +	struct drm_device *dev = &config->dev->drm;

Ditto, dev may not be set yet.

> +	size_t n_encoders;
> +
> +	n_encoders = list_count_nodes(&config->encoders);
> +	if (n_encoders <= 0 || n_encoders >= 32) {
> +		drm_info(dev, "The number of encoders must be between 1 and 31\n");
> +		return false;
> +	}
> +
> +	return true;
> +}
> +
>  bool vkms_config_is_valid(struct vkms_config *config)
>  {
>  	struct vkms_config_crtc *crtc_cfg;
> @@ -198,6 +222,9 @@ bool vkms_config_is_valid(struct vkms_config *config)
>  	if (!valid_crtc_number(config))
>  		return false;
>  
> +	if (!valid_encoder_number(config))
> +		return false;
> +
>  	if (!valid_plane_possible_crtcs(config))
>  		return false;
>  
> @@ -217,6 +244,7 @@ static int vkms_config_show(struct seq_file *m, void *data)
>  	const char *dev_name;
>  	struct vkms_config_plane *plane_cfg;
>  	struct vkms_config_crtc *crtc_cfg;
> +	struct vkms_config_encoder *encoder_cfg;
>  
>  	dev_name = vkms_config_get_device_name((struct vkms_config *)vkmsdev->config);
>  	seq_printf(m, "dev_name=%s\n", dev_name);
> @@ -233,6 +261,9 @@ static int vkms_config_show(struct seq_file *m, void *data)
>  			   vkms_config_crtc_get_writeback(crtc_cfg));
>  	}
>  
> +	vkms_config_for_each_encoder(vkmsdev->config, encoder_cfg)
> +		seq_puts(m, "encoder\n");
> +
>  	return 0;
>  }
>  
> @@ -366,3 +397,23 @@ struct vkms_config_plane *vkms_config_crtc_cursor_plane(const struct vkms_config
>  {
>  	return vkms_config_crtc_get_plane(config, crtc_cfg, DRM_PLANE_TYPE_CURSOR);
>  }
> +
> +struct vkms_config_encoder *vkms_config_create_encoder(struct vkms_config *config)
> +{
> +	struct vkms_config_encoder *encoder_cfg;
> +
> +	encoder_cfg = kzalloc(sizeof(*encoder_cfg), GFP_KERNEL);
> +	if (!encoder_cfg)
> +		return ERR_PTR(-ENOMEM);
> +
> +	list_add_tail(&encoder_cfg->link, &config->encoders);
> +
> +	return encoder_cfg;
> +}
> +
> +void vkms_config_destroy_encoder(struct vkms_config *config,
> +				 struct vkms_config_encoder *encoder_cfg)
> +{
> +	list_del(&encoder_cfg->link);
> +	kfree(encoder_cfg);
> +}
> diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
> index 0fe0ded1d8e4..2ba80c4c9ce5 100644
> --- a/drivers/gpu/drm/vkms/vkms_config.h
> +++ b/drivers/gpu/drm/vkms/vkms_config.h
> @@ -15,12 +15,14 @@
>   * @dev_name: Name of the device
>   * @planes: List of planes configured for the device
>   * @crtcs: List of CRTCs configured for the device
> + * @encoders: List of encoders configured for the device
>   * @dev: Used to store the current VKMS device. Only set when the device is instantiated.
>   */
>  struct vkms_config {
>  	const char *dev_name;
>  	struct list_head planes;
>  	struct list_head crtcs;
> +	struct list_head encoders;
>  	struct vkms_device *dev;
>  };
>  
> @@ -65,6 +67,22 @@ struct vkms_config_crtc {
>  	struct vkms_output *crtc;
>  };
>  
> +/**
> + * struct vkms_config_encoder
> + *
> + * @link: Link to the others encoders in vkms_config
> + * @encoder: Internal usage. This pointer should never be considered as valid.
> + *           It can be used to store a temporary reference to a VKMS encoder
> + *           during device creation. This pointer is not managed by the
> + *           configuration and must be managed by other means.
> + */
> +struct vkms_config_encoder {
> +	struct list_head link;
> +
> +	/* Internal usage */
> +	struct drm_encoder *encoder;
> +};
> +
>  /**
>   * vkms_config_for_each_plane - Iterate over the vkms_config planes
>   * @config: &struct vkms_config pointer
> @@ -81,6 +99,14 @@ struct vkms_config_crtc {
>  #define vkms_config_for_each_crtc(config, crtc_cfg) \
>  	list_for_each_entry((crtc_cfg), &(config)->crtcs, link)
>  
> +/**
> + * vkms_config_for_each_encoder - Iterate over the vkms_config encoders
> + * @config: &struct vkms_config pointer
> + * @encoder_cfg: &struct vkms_config_encoder pointer used as cursor
> + */
> +#define vkms_config_for_each_encoder(config, encoder_cfg) \
> +	list_for_each_entry((encoder_cfg), &(config)->encoders, link)
> +
>  /**
>   * vkms_config_plane_for_each_possible_crtc - Iterate over the vkms_config_plane
>   * possible CRTCs
> @@ -282,4 +308,22 @@ struct vkms_config_plane *vkms_config_crtc_primary_plane(const struct vkms_confi
>  struct vkms_config_plane *vkms_config_crtc_cursor_plane(const struct vkms_config *config,
>  							struct vkms_config_crtc *crtc_cfg);
>  
> +/**
> + * vkms_config_create_encoder() - Add a new encoder configuration
> + * @config: Configuration to add the encoder to
> + *
> + * Returns:
> + * The new encoder configuration or an error. Call vkms_config_destroy_encoder()
> + * to free the returned encoder configuration.
> + */
> +struct vkms_config_encoder *vkms_config_create_encoder(struct vkms_config *config);
> +
> +/**
> + * vkms_config_destroy_encoder() - Remove and free a encoder configuration
> + * @config: Configuration to remove the encoder from
> + * @encoder_cfg: Encoder configuration to destroy
> + */
> +void vkms_config_destroy_encoder(struct vkms_config *config,
> +				 struct vkms_config_encoder *encoder_cfg);
> +
>  #endif /* _VKMS_CONFIG_H_ */
> -- 
> 2.48.1
> 

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 05/15] drm/vkms: Extract vkms_config header
  2025-02-11 11:09 ` [PATCH v2 05/15] drm/vkms: Extract vkms_config header José Expósito
@ 2025-02-13 13:59   ` Louis Chauvet
  2025-02-13 15:36     ` José Expósito
  0 siblings, 1 reply; 32+ messages in thread
From: Louis Chauvet @ 2025-02-13 13:59 UTC (permalink / raw)
  To: José Expósito
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel

On 11/02/25 - 12:09, José Expósito wrote:
> Creating a new vkms_config structure will be more complex once we
> start adding more options.
> 
> Extract the vkms_config structure to its own header and source files
> and add functions to create and delete a vkms_config and to initialize
> debugfs.
> 
> Refactor, no functional changes.
> 
> Co-developed-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: José Expósito <jose.exposito89@gmail.com>

This does not build in module, can you add this in the next version?

diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
index 152b2ecd6aef..42caa421876e 100644
--- a/drivers/gpu/drm/vkms/vkms_config.c
+++ b/drivers/gpu/drm/vkms/vkms_config.c
@@ -4,6 +4,7 @@

 #include <drm/drm_print.h>
 #include <drm/drm_debugfs.h>
+#include <kunit/visibility.h>

 #include "vkms_config.h"

@@ -17,11 +18,13 @@ struct vkms_config *vkms_config_create(void)

        return config;
 }
+EXPORT_SYMBOL_IF_KUNIT(vkms_config_create);

 void vkms_config_destroy(struct vkms_config *config)
 {
        kfree(config);
 }
+EXPORT_SYMBOL_IF_KUNIT(vkms_config_destroy);

 static int vkms_config_show(struct seq_file *m, void *data)
 {

With this:

Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 15/15] drm/vkms: Allow to attach connectors and encoders
  2025-02-11 11:09 ` [PATCH v2 15/15] drm/vkms: Allow to attach connectors and encoders José Expósito
@ 2025-02-13 13:59   ` Louis Chauvet
  0 siblings, 0 replies; 32+ messages in thread
From: Louis Chauvet @ 2025-02-13 13:59 UTC (permalink / raw)
  To: José Expósito
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel

On 11/02/25 - 12:09, José Expósito wrote:
> Add a list of possible encoders to the connector configuration and
> helpers to attach and detach them.
> 
> Now that the default configuration has its connector and encoder
> correctly, configure the output following the configuration.
> 
> Co-developed-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: José Expósito <jose.exposito89@gmail.com>

The compilation is broken as module:

diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
index 7f4e21e8ee0c..76f24bd463f6 100644
--- a/drivers/gpu/drm/vkms/vkms_config.c
+++ b/drivers/gpu/drm/vkms/vkms_config.c
@@ -610,6 +610,7 @@ int __must_check vkms_config_connector_attach_encoder(struct vkms_config_connect
        return xa_alloc(&connector_cfg->possible_encoders, &encoder_idx,
                        encoder_cfg, xa_limit_32b, GFP_KERNEL);
 }
+EXPORT_SYMBOL_IF_KUNIT(vkms_config_connector_attach_encoder);

 void vkms_config_connector_detach_encoder(struct vkms_config_connector *connector_cfg,
                                          struct vkms_config_encoder *encoder_cfg)
@@ -623,3 +624,4 @@ void vkms_config_connector_detach_encoder(struct vkms_config_connector *connecto
                        xa_erase(&connector_cfg->possible_encoders, idx);
        }
 }
+EXPORT_SYMBOL_IF_KUNIT(vkms_config_connector_detach_encoder);


> ---
>  .clang-format                                 |  1 +
>  drivers/gpu/drm/vkms/tests/vkms_config_test.c | 94 +++++++++++++++++++
>  drivers/gpu/drm/vkms/vkms_config.c            | 60 ++++++++++++
>  drivers/gpu/drm/vkms/vkms_config.h            | 29 ++++++
>  drivers/gpu/drm/vkms/vkms_output.c            | 33 ++++---
>  5 files changed, 204 insertions(+), 13 deletions(-)
> 
> diff --git a/.clang-format b/.clang-format
> index ca49832993c5..7630990aa07a 100644
> --- a/.clang-format
> +++ b/.clang-format
> @@ -694,6 +694,7 @@ ForEachMacros:
>    - 'vkms_config_for_each_crtc'
>    - 'vkms_config_for_each_encoder'
>    - 'vkms_config_for_each_plane'
> +  - 'vkms_config_connector_for_each_possible_encoder'
>    - 'vkms_config_encoder_for_each_possible_crtc'
>    - 'vkms_config_plane_for_each_possible_crtc'
>    - 'while_for_each_ftrace_op'
> diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
> index cba7e9d2fcad..2d104ecfde3b 100644
> --- a/drivers/gpu/drm/vkms/tests/vkms_config_test.c
> +++ b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
> @@ -511,6 +511,27 @@ static void vkms_config_test_invalid_connector_number(struct kunit *test)
>  	vkms_config_destroy(config);
>  }
>  
> +static void vkms_config_test_valid_connector_possible_encoders(struct kunit *test)
> +{
> +	struct vkms_config *config;
> +	struct vkms_config_encoder *encoder_cfg;
> +	struct vkms_config_connector *connector_cfg;
> +
> +	config = vkms_config_default_create(false, false, false);
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
> +
> +	encoder_cfg = list_first_entry(&config->encoders,
> +				       typeof(*encoder_cfg), link);
> +	connector_cfg = list_first_entry(&config->connectors,
> +					 typeof(*connector_cfg), link);
> +
> +	/* Invalid: Connector without a possible encoder */
> +	vkms_config_connector_detach_encoder(connector_cfg, encoder_cfg);
> +	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
> +
> +	vkms_config_destroy(config);
> +}
> +
>  static void vkms_config_test_plane_attach_crtc(struct kunit *test)
>  {
>  	struct vkms_config *config;
> @@ -700,6 +721,77 @@ static void vkms_config_test_encoder_get_possible_crtcs(struct kunit *test)
>  	vkms_config_destroy(config);
>  }
>  
> +static void vkms_config_test_connector_get_possible_encoders(struct kunit *test)
> +{
> +	struct vkms_config *config;
> +	struct vkms_config_connector *connector_cfg1, *connector_cfg2;
> +	struct vkms_config_encoder *encoder_cfg1, *encoder_cfg2;
> +	struct vkms_config_encoder *possible_encoder;
> +	unsigned long idx = 0;
> +	int n_encoders = 0;
> +	int err;
> +
> +	config = vkms_config_create("test");
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
> +
> +	connector_cfg1 = vkms_config_create_connector(config);
> +	connector_cfg2 = vkms_config_create_connector(config);
> +	encoder_cfg1 = vkms_config_create_encoder(config);
> +	encoder_cfg2 = vkms_config_create_encoder(config);
> +
> +	/* No possible encoders */
> +	vkms_config_connector_for_each_possible_encoder(connector_cfg1, idx,
> +							possible_encoder)
> +		KUNIT_FAIL(test, "Unexpected possible encoder");
> +
> +	vkms_config_connector_for_each_possible_encoder(connector_cfg2, idx,
> +							possible_encoder)
> +		KUNIT_FAIL(test, "Unexpected possible encoder");
> +
> +	/* Connector 1 attached to encoders 1 and 2 */
> +	err = vkms_config_connector_attach_encoder(connector_cfg1, encoder_cfg1);
> +	KUNIT_EXPECT_EQ(test, err, 0);
> +	err = vkms_config_connector_attach_encoder(connector_cfg1, encoder_cfg2);
> +	KUNIT_EXPECT_EQ(test, err, 0);
> +
> +	vkms_config_connector_for_each_possible_encoder(connector_cfg1, idx,
> +							possible_encoder) {
> +		n_encoders++;
> +		if (possible_encoder != encoder_cfg1 &&
> +		    possible_encoder != encoder_cfg2)
> +			KUNIT_FAIL(test, "Unexpected possible encoder");
> +	}
> +	KUNIT_ASSERT_EQ(test, n_encoders, 2);
> +	n_encoders = 0;
> +
> +	vkms_config_connector_for_each_possible_encoder(connector_cfg2, idx,
> +							possible_encoder)
> +		KUNIT_FAIL(test, "Unexpected possible encoder");
> +
> +	/* Connector 1 attached to encoder 1 and connector 2 to encoder 2 */
> +	vkms_config_connector_detach_encoder(connector_cfg1, encoder_cfg2);
> +	vkms_config_connector_for_each_possible_encoder(connector_cfg1, idx,
> +							possible_encoder) {
> +		n_encoders++;
> +		if (possible_encoder != encoder_cfg1)
> +			KUNIT_FAIL(test, "Unexpected possible encoder");
> +	}
> +	KUNIT_ASSERT_EQ(test, n_encoders, 1);
> +	n_encoders = 0;
> +
> +	err = vkms_config_connector_attach_encoder(connector_cfg2, encoder_cfg2);
> +	KUNIT_EXPECT_EQ(test, err, 0);
> +	vkms_config_connector_for_each_possible_encoder(connector_cfg2, idx,
> +							possible_encoder) {
> +		n_encoders++;
> +		if (possible_encoder != encoder_cfg2)
> +			KUNIT_FAIL(test, "Unexpected possible encoder");
> +	}
> +	KUNIT_ASSERT_EQ(test, n_encoders, 1);
> +
> +	vkms_config_destroy(config);
> +}
> +
>  static struct kunit_case vkms_config_test_cases[] = {
>  	KUNIT_CASE(vkms_config_test_empty_config),
>  	KUNIT_CASE_PARAM(vkms_config_test_default_config,
> @@ -715,9 +807,11 @@ static struct kunit_case vkms_config_test_cases[] = {
>  	KUNIT_CASE(vkms_config_test_invalid_encoder_number),
>  	KUNIT_CASE(vkms_config_test_valid_encoder_possible_crtcs),
>  	KUNIT_CASE(vkms_config_test_invalid_connector_number),
> +	KUNIT_CASE(vkms_config_test_valid_connector_possible_encoders),
>  	KUNIT_CASE(vkms_config_test_plane_attach_crtc),
>  	KUNIT_CASE(vkms_config_test_plane_get_possible_crtcs),
>  	KUNIT_CASE(vkms_config_test_encoder_get_possible_crtcs),
> +	KUNIT_CASE(vkms_config_test_connector_get_possible_encoders),
>  	{}
>  };
>  
> diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
> index d52280d3bbee..3d95dc713151 100644
> --- a/drivers/gpu/drm/vkms/vkms_config.c
> +++ b/drivers/gpu/drm/vkms/vkms_config.c
> @@ -93,6 +93,9 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
>  	if (IS_ERR(connector_cfg))
>  		goto err_alloc;
>  
> +	if (vkms_config_connector_attach_encoder(connector_cfg, encoder_cfg))
> +		goto err_alloc;
> +
>  	return config;
>  
>  err_alloc:
> @@ -275,6 +278,22 @@ static bool valid_connector_number(struct vkms_config *config)
>  	return true;
>  }
>  
> +static bool valid_connector_possible_encoders(struct vkms_config *config)
> +{
> +	struct drm_device *dev = &config->dev->drm;
> +	struct vkms_config_connector *connector_cfg;
> +
> +	vkms_config_for_each_connector(config, connector_cfg) {
> +		if (xa_empty(&connector_cfg->possible_encoders)) {
> +			drm_info(dev,
> +				 "All connectors must have at least one possible encoder\n");
> +			return false;
> +		}
> +	}
> +
> +	return true;
> +}
> +
>  bool vkms_config_is_valid(struct vkms_config *config)
>  {
>  	struct vkms_config_crtc *crtc_cfg;
> @@ -302,6 +321,9 @@ bool vkms_config_is_valid(struct vkms_config *config)
>  	if (!valid_encoder_possible_crtcs(config))
>  		return false;
>  
> +	if (!valid_connector_possible_encoders(config))
> +		return false;
> +
>  	return true;
>  }
>  
> @@ -493,6 +515,11 @@ struct vkms_config_encoder *vkms_config_create_encoder(struct vkms_config *confi
>  void vkms_config_destroy_encoder(struct vkms_config *config,
>  				 struct vkms_config_encoder *encoder_cfg)
>  {
> +	struct vkms_config_connector *connector_cfg;
> +
> +	vkms_config_for_each_connector(config, connector_cfg)
> +		vkms_config_connector_detach_encoder(connector_cfg, encoder_cfg);
> +
>  	xa_destroy(&encoder_cfg->possible_crtcs);
>  	list_del(&encoder_cfg->link);
>  	kfree(encoder_cfg);
> @@ -534,6 +561,8 @@ struct vkms_config_connector *vkms_config_create_connector(struct vkms_config *c
>  	if (!connector_cfg)
>  		return ERR_PTR(-ENOMEM);
>  
> +	xa_init_flags(&connector_cfg->possible_encoders, XA_FLAGS_ALLOC);
> +
>  	list_add_tail(&connector_cfg->link, &config->connectors);
>  
>  	return connector_cfg;
> @@ -541,6 +570,37 @@ struct vkms_config_connector *vkms_config_create_connector(struct vkms_config *c
>  
>  void vkms_config_destroy_connector(struct vkms_config_connector *connector_cfg)
>  {
> +	xa_destroy(&connector_cfg->possible_encoders);
>  	list_del(&connector_cfg->link);
>  	kfree(connector_cfg);
>  }
> +
> +int __must_check vkms_config_connector_attach_encoder(struct vkms_config_connector *connector_cfg,
> +						      struct vkms_config_encoder *encoder_cfg)
> +{
> +	struct vkms_config_encoder *possible_encoder;
> +	unsigned long idx = 0;
> +	u32 encoder_idx = 0;
> +
> +	vkms_config_connector_for_each_possible_encoder(connector_cfg, idx,
> +							possible_encoder) {
> +		if (possible_encoder == encoder_cfg)
> +			return -EINVAL;
> +	}
> +
> +	return xa_alloc(&connector_cfg->possible_encoders, &encoder_idx,
> +			encoder_cfg, xa_limit_32b, GFP_KERNEL);
> +}
> +
> +void vkms_config_connector_detach_encoder(struct vkms_config_connector *connector_cfg,
> +					  struct vkms_config_encoder *encoder_cfg)
> +{
> +	struct vkms_config_encoder *possible_encoder;
> +	unsigned long idx = 0;
> +
> +	vkms_config_connector_for_each_possible_encoder(connector_cfg, idx,
> +							possible_encoder) {
> +		if (possible_encoder == encoder_cfg)
> +			xa_erase(&connector_cfg->possible_encoders, idx);
> +	}
> +}
> diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
> index 8451c2f127b6..c87513d174f2 100644
> --- a/drivers/gpu/drm/vkms/vkms_config.h
> +++ b/drivers/gpu/drm/vkms/vkms_config.h
> @@ -92,6 +92,7 @@ struct vkms_config_encoder {
>   * struct vkms_config_connector
>   *
>   * @link: Link to the others connector in vkms_config
> + * @possible_encoders: Array of encoders that can be used with this connector
>   * @connector: Internal usage. This pointer should never be considered as valid.
>   *             It can be used to store a temporary reference to a VKMS connector
>   *             during device creation. This pointer is not managed by the
> @@ -100,6 +101,8 @@ struct vkms_config_encoder {
>  struct vkms_config_connector {
>  	struct list_head link;
>  
> +	struct xarray possible_encoders;
> +
>  	/* Internal usage */
>  	struct vkms_connector *connector;
>  };
> @@ -156,6 +159,16 @@ struct vkms_config_connector {
>  #define vkms_config_encoder_for_each_possible_crtc(encoder_cfg, idx, possible_crtc) \
>  	xa_for_each(&(encoder_cfg)->possible_crtcs, idx, (possible_crtc))
>  
> +/**
> + * vkms_config_connector_for_each_possible_encoder - Iterate over the
> + * vkms_config_connector possible encoders
> + * @connector_cfg: &struct vkms_config_connector pointer
> + * @idx: Index of the cursor
> + * @possible_encoder: &struct vkms_config_encoder pointer used as cursor
> + */
> +#define vkms_config_connector_for_each_possible_encoder(connector_cfg, idx, possible_encoder) \
> +	xa_for_each(&(connector_cfg)->possible_encoders, idx, (possible_encoder))
> +
>  /**
>   * vkms_config_create() - Create a new VKMS configuration
>   * @dev_name: Name of the device
> @@ -397,4 +410,20 @@ struct vkms_config_connector *vkms_config_create_connector(struct vkms_config *c
>   */
>  void vkms_config_destroy_connector(struct vkms_config_connector *connector_cfg);
>  
> +/**
> + * vkms_config_connector_attach_encoder - Attach a connector to an encoder
> + * @connector_cfg: Connector to attach
> + * @encoder_cfg: Encoder to attach @connector_cfg to
> + */
> +int __must_check vkms_config_connector_attach_encoder(struct vkms_config_connector *connector_cfg,
> +						      struct vkms_config_encoder *encoder_cfg);
> +
> +/**
> + * vkms_config_connector_detach_encoder - Detach a connector from an encoder
> + * @connector_cfg: Connector to detach
> + * @encoder_cfg: Encoder to detach @connector_cfg from
> + */
> +void vkms_config_connector_detach_encoder(struct vkms_config_connector *connector_cfg,
> +					  struct vkms_config_encoder *encoder_cfg);
> +
>  #endif /* _VKMS_CONFIG_H_ */
> diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
> index 8920d6b5d105..8d7ca0cdd79f 100644
> --- a/drivers/gpu/drm/vkms/vkms_output.c
> +++ b/drivers/gpu/drm/vkms/vkms_output.c
> @@ -8,10 +8,10 @@
>  int vkms_output_init(struct vkms_device *vkmsdev)
>  {
>  	struct drm_device *dev = &vkmsdev->drm;
> -	struct vkms_connector *connector;
>  	struct vkms_config_plane *plane_cfg;
>  	struct vkms_config_crtc *crtc_cfg;
>  	struct vkms_config_encoder *encoder_cfg;
> +	struct vkms_config_connector *connector_cfg;
>  	int ret;
>  	int writeback;
>  
> @@ -83,22 +83,29 @@ int vkms_output_init(struct vkms_device *vkmsdev)
>  		}
>  	}
>  
> -	connector = vkms_connector_init(vkmsdev);
> -	if (IS_ERR(connector)) {
> -		DRM_ERROR("Failed to init connector\n");
> -		return PTR_ERR(connector);
> -	}
> +	vkms_config_for_each_connector(vkmsdev->config, connector_cfg) {
> +		struct vkms_config_encoder *possible_encoder;
> +		unsigned long idx = 0;
>  
> -	/* Attach the encoder and the connector */
> -	vkms_config_for_each_encoder(vkmsdev->config, encoder_cfg) {
> -		ret = drm_connector_attach_encoder(&connector->base, encoder_cfg->encoder);
> -		if (ret) {
> -			DRM_ERROR("Failed to attach connector to encoder\n");
> -			return ret;
> +		connector_cfg->connector = vkms_connector_init(vkmsdev);
> +		if (IS_ERR(connector_cfg->connector)) {
> +			DRM_ERROR("Failed to init connector\n");
> +			return PTR_ERR(connector_cfg->connector);
> +		}
> +
> +		vkms_config_connector_for_each_possible_encoder(connector_cfg,
> +								idx,
> +								possible_encoder) {
> +			ret = drm_connector_attach_encoder(&connector_cfg->connector->base,
> +							   possible_encoder->encoder);
> +			if (ret) {
> +				DRM_ERROR("Failed to attach connector to encoder\n");
> +				return ret;
> +			}
>  		}
>  	}
>  
>  	drm_mode_config_reset(dev);
>  
> -	return ret;
> +	return 0;
>  }
> -- 
> 2.48.1
> 

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 14/15] drm/vkms: Allow to configure multiple connectors
  2025-02-11 11:09 ` [PATCH v2 14/15] drm/vkms: Allow to configure multiple connectors José Expósito
@ 2025-02-13 13:59   ` Louis Chauvet
  0 siblings, 0 replies; 32+ messages in thread
From: Louis Chauvet @ 2025-02-13 13:59 UTC (permalink / raw)
  To: José Expósito
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel

On 11/02/25 - 12:09, José Expósito wrote:
> Add a list of connectors to vkms_config and helper functions to add and
> remove as many connectors as wanted.
> 
> For backwards compatibility, add one enabled connector to the default
> configuration.
> 
> A future patch will allow to attach connectors and encoders, but for the
> moment there are no changes in the way the output is configured.
> 
> Co-developed-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: José Expósito <jose.exposito89@gmail.com>

Compilation as module is broken:

diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
index d06540ab9d15..9ba857ce1732 100644
--- a/drivers/gpu/drm/vkms/vkms_config.c
+++ b/drivers/gpu/drm/vkms/vkms_config.c
@@ -555,9 +555,11 @@ struct vkms_config_connector *vkms_config_create_connector(struct vkms_config *c

        return connector_cfg;
 }
+EXPORT_SYMBOL_IF_KUNIT(vkms_config_create_connector);

 void vkms_config_destroy_connector(struct vkms_config_connector *connector_cfg)
 {
        list_del(&connector_cfg->link);
        kfree(connector_cfg);
 }
+EXPORT_SYMBOL_IF_KUNIT(vkms_config_destroy_connector);


> ---
>  .clang-format                                 |  1 +
>  drivers/gpu/drm/vkms/tests/vkms_config_test.c | 74 +++++++++++++++++++
>  drivers/gpu/drm/vkms/vkms_config.c            | 50 +++++++++++++
>  drivers/gpu/drm/vkms/vkms_config.h            | 42 +++++++++++
>  drivers/gpu/drm/vkms/vkms_connector.c         | 11 +++
>  5 files changed, 178 insertions(+)
> 
> diff --git a/.clang-format b/.clang-format
> index 5d21c0e4edbd..ca49832993c5 100644
> --- a/.clang-format
> +++ b/.clang-format
> @@ -690,6 +690,7 @@ ForEachMacros:
>    - 'v4l2_m2m_for_each_src_buf'
>    - 'v4l2_m2m_for_each_src_buf_safe'
>    - 'virtio_device_for_each_vq'
> +  - 'vkms_config_for_each_connector'
>    - 'vkms_config_for_each_crtc'
>    - 'vkms_config_for_each_encoder'
>    - 'vkms_config_for_each_plane'
> diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
> index 7458d175acb6..cba7e9d2fcad 100644
> --- a/drivers/gpu/drm/vkms/tests/vkms_config_test.c
> +++ b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
> @@ -27,6 +27,7 @@ static void vkms_config_test_empty_config(struct kunit *test)
>  	KUNIT_EXPECT_TRUE(test, list_empty(&config->planes));
>  	KUNIT_EXPECT_TRUE(test, list_empty(&config->crtcs));
>  	KUNIT_EXPECT_TRUE(test, list_empty(&config->encoders));
> +	KUNIT_EXPECT_TRUE(test, list_empty(&config->connectors));
>  
>  	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
>  
> @@ -103,6 +104,9 @@ static void vkms_config_test_default_config(struct kunit *test)
>  	/* Encoders */
>  	KUNIT_EXPECT_EQ(test, list_count_nodes(&config->encoders), 1);
>  
> +	/* Connectors */
> +	KUNIT_EXPECT_EQ(test, list_count_nodes(&config->connectors), 1);
> +
>  	KUNIT_EXPECT_TRUE(test, vkms_config_is_valid(config));
>  
>  	vkms_config_destroy(config);
> @@ -232,6 +236,51 @@ static void vkms_config_test_get_encoders(struct kunit *test)
>  	vkms_config_destroy(config);
>  }
>  
> +static void vkms_config_test_get_connectors(struct kunit *test)
> +{
> +	struct vkms_config *config;
> +	struct vkms_config_connector *connector_cfg;
> +	struct vkms_config_connector *connector_cfg1, *connector_cfg2;
> +	int n_connectors = 0;
> +
> +	config = vkms_config_create("test");
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
> +
> +	vkms_config_for_each_connector(config, connector_cfg)
> +		n_connectors++;
> +	KUNIT_ASSERT_EQ(test, n_connectors, 0);
> +
> +	connector_cfg1 = vkms_config_create_connector(config);
> +	vkms_config_for_each_connector(config, connector_cfg) {
> +		n_connectors++;
> +		if (connector_cfg != connector_cfg1)
> +			KUNIT_FAIL(test, "Unexpected connector");
> +	}
> +	KUNIT_ASSERT_EQ(test, n_connectors, 1);
> +	n_connectors = 0;
> +
> +	connector_cfg2 = vkms_config_create_connector(config);
> +	vkms_config_for_each_connector(config, connector_cfg) {
> +		n_connectors++;
> +		if (connector_cfg != connector_cfg1 &&
> +		    connector_cfg != connector_cfg2)
> +			KUNIT_FAIL(test, "Unexpected connector");
> +	}
> +	KUNIT_ASSERT_EQ(test, n_connectors, 2);
> +	n_connectors = 0;
> +
> +	vkms_config_destroy_connector(connector_cfg2);
> +	vkms_config_for_each_connector(config, connector_cfg) {
> +		n_connectors++;
> +		if (connector_cfg != connector_cfg1)
> +			KUNIT_FAIL(test, "Unexpected connector");
> +	}
> +	KUNIT_ASSERT_EQ(test, n_connectors, 1);
> +	n_connectors = 0;
> +
> +	vkms_config_destroy(config);
> +}
> +
>  static void vkms_config_test_invalid_plane_number(struct kunit *test)
>  {
>  	struct vkms_config *config;
> @@ -439,6 +488,29 @@ static void vkms_config_test_valid_encoder_possible_crtcs(struct kunit *test)
>  	vkms_config_destroy(config);
>  }
>  
> +static void vkms_config_test_invalid_connector_number(struct kunit *test)
> +{
> +	struct vkms_config *config;
> +	struct vkms_config_connector *connector_cfg;
> +	int n;
> +
> +	config = vkms_config_default_create(false, false, false);
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
> +
> +	/* Invalid: No connectors */
> +	connector_cfg = list_first_entry(&config->connectors, typeof(*connector_cfg), link);
> +	vkms_config_destroy_connector(connector_cfg);
> +	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
> +
> +	/* Invalid: Too many connectors */
> +	for (n = 0; n <= 32; n++)
> +		connector_cfg = vkms_config_create_connector(config);
> +
> +	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
> +
> +	vkms_config_destroy(config);
> +}
> +
>  static void vkms_config_test_plane_attach_crtc(struct kunit *test)
>  {
>  	struct vkms_config *config;
> @@ -635,12 +707,14 @@ static struct kunit_case vkms_config_test_cases[] = {
>  	KUNIT_CASE(vkms_config_test_get_planes),
>  	KUNIT_CASE(vkms_config_test_get_crtcs),
>  	KUNIT_CASE(vkms_config_test_get_encoders),
> +	KUNIT_CASE(vkms_config_test_get_connectors),
>  	KUNIT_CASE(vkms_config_test_invalid_plane_number),
>  	KUNIT_CASE(vkms_config_test_valid_plane_type),
>  	KUNIT_CASE(vkms_config_test_valid_plane_possible_crtcs),
>  	KUNIT_CASE(vkms_config_test_invalid_crtc_number),
>  	KUNIT_CASE(vkms_config_test_invalid_encoder_number),
>  	KUNIT_CASE(vkms_config_test_valid_encoder_possible_crtcs),
> +	KUNIT_CASE(vkms_config_test_invalid_connector_number),
>  	KUNIT_CASE(vkms_config_test_plane_attach_crtc),
>  	KUNIT_CASE(vkms_config_test_plane_get_possible_crtcs),
>  	KUNIT_CASE(vkms_config_test_encoder_get_possible_crtcs),
> diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
> index f727c0009489..d52280d3bbee 100644
> --- a/drivers/gpu/drm/vkms/vkms_config.c
> +++ b/drivers/gpu/drm/vkms/vkms_config.c
> @@ -24,6 +24,7 @@ struct vkms_config *vkms_config_create(const char *dev_name)
>  	INIT_LIST_HEAD(&config->planes);
>  	INIT_LIST_HEAD(&config->crtcs);
>  	INIT_LIST_HEAD(&config->encoders);
> +	INIT_LIST_HEAD(&config->connectors);
>  
>  	return config;
>  }
> @@ -36,6 +37,7 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
>  	struct vkms_config_plane *plane_cfg;
>  	struct vkms_config_crtc *crtc_cfg;
>  	struct vkms_config_encoder *encoder_cfg;
> +	struct vkms_config_connector *connector_cfg;
>  	int n;
>  
>  	config = vkms_config_create(DEFAULT_DEVICE_NAME);
> @@ -87,6 +89,10 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
>  	if (vkms_config_encoder_attach_crtc(encoder_cfg, crtc_cfg))
>  		goto err_alloc;
>  
> +	connector_cfg = vkms_config_create_connector(config);
> +	if (IS_ERR(connector_cfg))
> +		goto err_alloc;
> +
>  	return config;
>  
>  err_alloc:
> @@ -99,6 +105,7 @@ void vkms_config_destroy(struct vkms_config *config)
>  	struct vkms_config_plane *plane_cfg, *plane_tmp;
>  	struct vkms_config_crtc *crtc_cfg, *crtc_tmp;
>  	struct vkms_config_encoder *encoder_cfg, *encoder_tmp;
> +	struct vkms_config_connector *connector_cfg, *connector_tmp;
>  
>  	list_for_each_entry_safe(plane_cfg, plane_tmp, &config->planes, link)
>  		vkms_config_destroy_plane(plane_cfg);
> @@ -109,6 +116,9 @@ void vkms_config_destroy(struct vkms_config *config)
>  	list_for_each_entry_safe(encoder_cfg, encoder_tmp, &config->encoders, link)
>  		vkms_config_destroy_encoder(config, encoder_cfg);
>  
> +	list_for_each_entry_safe(connector_cfg, connector_tmp, &config->connectors, link)
> +		vkms_config_destroy_connector(connector_cfg);
> +
>  	kfree_const(config->dev_name);
>  	kfree(config);
>  }
> @@ -251,6 +261,20 @@ static bool valid_encoder_possible_crtcs(struct vkms_config *config)
>  	return true;
>  }
>  
> +static bool valid_connector_number(struct vkms_config *config)
> +{
> +	struct drm_device *dev = &config->dev->drm;
> +	size_t n_connectors;
> +
> +	n_connectors = list_count_nodes(&config->connectors);
> +	if (n_connectors <= 0 || n_connectors >= 32) {
> +		drm_info(dev, "The number of connectors must be between 1 and 31\n");
> +		return false;
> +	}
> +
> +	return true;
> +}
> +
>  bool vkms_config_is_valid(struct vkms_config *config)
>  {
>  	struct vkms_config_crtc *crtc_cfg;
> @@ -264,6 +288,9 @@ bool vkms_config_is_valid(struct vkms_config *config)
>  	if (!valid_encoder_number(config))
>  		return false;
>  
> +	if (!valid_connector_number(config))
> +		return false;
> +
>  	if (!valid_plane_possible_crtcs(config))
>  		return false;
>  
> @@ -287,6 +314,7 @@ static int vkms_config_show(struct seq_file *m, void *data)
>  	struct vkms_config_plane *plane_cfg;
>  	struct vkms_config_crtc *crtc_cfg;
>  	struct vkms_config_encoder *encoder_cfg;
> +	struct vkms_config_connector *connector_cfg;
>  
>  	dev_name = vkms_config_get_device_name((struct vkms_config *)vkmsdev->config);
>  	seq_printf(m, "dev_name=%s\n", dev_name);
> @@ -306,6 +334,9 @@ static int vkms_config_show(struct seq_file *m, void *data)
>  	vkms_config_for_each_encoder(vkmsdev->config, encoder_cfg)
>  		seq_puts(m, "encoder\n");
>  
> +	vkms_config_for_each_connector(vkmsdev->config, connector_cfg)
> +		seq_puts(m, "connector\n");
> +
>  	return 0;
>  }
>  
> @@ -494,3 +525,22 @@ void vkms_config_encoder_detach_crtc(struct vkms_config_encoder *encoder_cfg,
>  			xa_erase(&encoder_cfg->possible_crtcs, idx);
>  	}
>  }
> +
> +struct vkms_config_connector *vkms_config_create_connector(struct vkms_config *config)
> +{
> +	struct vkms_config_connector *connector_cfg;
> +
> +	connector_cfg = kzalloc(sizeof(*connector_cfg), GFP_KERNEL);
> +	if (!connector_cfg)
> +		return ERR_PTR(-ENOMEM);
> +
> +	list_add_tail(&connector_cfg->link, &config->connectors);
> +
> +	return connector_cfg;
> +}
> +
> +void vkms_config_destroy_connector(struct vkms_config_connector *connector_cfg)
> +{
> +	list_del(&connector_cfg->link);
> +	kfree(connector_cfg);
> +}
> diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
> index 28c24afebe1e..8451c2f127b6 100644
> --- a/drivers/gpu/drm/vkms/vkms_config.h
> +++ b/drivers/gpu/drm/vkms/vkms_config.h
> @@ -16,6 +16,7 @@
>   * @planes: List of planes configured for the device
>   * @crtcs: List of CRTCs configured for the device
>   * @encoders: List of encoders configured for the device
> + * @connectors: List of connectors configured for the device
>   * @dev: Used to store the current VKMS device. Only set when the device is instantiated.
>   */
>  struct vkms_config {
> @@ -23,6 +24,7 @@ struct vkms_config {
>  	struct list_head planes;
>  	struct list_head crtcs;
>  	struct list_head encoders;
> +	struct list_head connectors;
>  	struct vkms_device *dev;
>  };
>  
> @@ -86,6 +88,22 @@ struct vkms_config_encoder {
>  	struct drm_encoder *encoder;
>  };
>  
> +/**
> + * struct vkms_config_connector
> + *
> + * @link: Link to the others connector in vkms_config
> + * @connector: Internal usage. This pointer should never be considered as valid.
> + *             It can be used to store a temporary reference to a VKMS connector
> + *             during device creation. This pointer is not managed by the
> + *             configuration and must be managed by other means.
> + */
> +struct vkms_config_connector {
> +	struct list_head link;
> +
> +	/* Internal usage */
> +	struct vkms_connector *connector;
> +};
> +
>  /**
>   * vkms_config_for_each_plane - Iterate over the vkms_config planes
>   * @config: &struct vkms_config pointer
> @@ -110,6 +128,14 @@ struct vkms_config_encoder {
>  #define vkms_config_for_each_encoder(config, encoder_cfg) \
>  	list_for_each_entry((encoder_cfg), &(config)->encoders, link)
>  
> +/**
> + * vkms_config_for_each_connector - Iterate over the vkms_config connectors
> + * @config: &struct vkms_config pointer
> + * @connector_cfg: &struct vkms_config_connector pointer used as cursor
> + */
> +#define vkms_config_for_each_connector(config, connector_cfg) \
> +	list_for_each_entry((connector_cfg), &(config)->connectors, link)
> +
>  /**
>   * vkms_config_plane_for_each_possible_crtc - Iterate over the vkms_config_plane
>   * possible CRTCs
> @@ -355,4 +381,20 @@ int __must_check vkms_config_encoder_attach_crtc(struct vkms_config_encoder *enc
>  void vkms_config_encoder_detach_crtc(struct vkms_config_encoder *encoder_cfg,
>  				     struct vkms_config_crtc *crtc_cfg);
>  
> +/**
> + * vkms_config_create_connector() - Add a new connector configuration
> + * @config: Configuration to add the connector to
> + *
> + * Returns:
> + * The new connector configuration or an error. Call
> + * vkms_config_destroy_connector() to free the returned connector configuration.
> + */
> +struct vkms_config_connector *vkms_config_create_connector(struct vkms_config *config);
> +
> +/**
> + * vkms_config_destroy_connector() - Remove and free a connector configuration
> + * @connector_cfg: Connector configuration to destroy
> + */
> +void vkms_config_destroy_connector(struct vkms_config_connector *connector_cfg);
> +
>  #endif /* _VKMS_CONFIG_H_ */
> diff --git a/drivers/gpu/drm/vkms/vkms_connector.c b/drivers/gpu/drm/vkms/vkms_connector.c
> index ab8b52a84151..48b10cba322a 100644
> --- a/drivers/gpu/drm/vkms/vkms_connector.c
> +++ b/drivers/gpu/drm/vkms/vkms_connector.c
> @@ -25,8 +25,19 @@ static int vkms_conn_get_modes(struct drm_connector *connector)
>  	return count;
>  }
>  
> +static struct drm_encoder *vkms_conn_best_encoder(struct drm_connector *connector)
> +{
> +	struct drm_encoder *encoder;
> +
> +	drm_connector_for_each_possible_encoder(connector, encoder)
> +		return encoder;
> +
> +	return NULL;
> +}
> +
>  static const struct drm_connector_helper_funcs vkms_conn_helper_funcs = {
>  	.get_modes    = vkms_conn_get_modes,
> +	.best_encoder = vkms_conn_best_encoder,
>  };
>  
>  struct vkms_connector *vkms_connector_init(struct vkms_device *vkmsdev)
> -- 
> 2.48.1
> 

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 09/15] drm/vkms: Allow to configure multiple planes
  2025-02-11 11:09 ` [PATCH v2 09/15] drm/vkms: Allow to configure multiple planes José Expósito
@ 2025-02-13 13:59   ` Louis Chauvet
  0 siblings, 0 replies; 32+ messages in thread
From: Louis Chauvet @ 2025-02-13 13:59 UTC (permalink / raw)
  To: José Expósito
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel

On 11/02/25 - 12:09, José Expósito wrote:
> Add a list of planes to vkms_config and create as many planes as
> configured during output initialization.
> 
> For backwards compatibility, add one primary plane and, if configured,
> one cursor plane and NUM_OVERLAY_PLANES planes to the default
> configuration.
> 
> Co-developed-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: José Expósito <jose.exposito89@gmail.com>

Build is broken as module:

diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
index 4a0205685a31..b8a4e86b7cd1 100644
--- a/drivers/gpu/drm/vkms/vkms_config.c
+++ b/drivers/gpu/drm/vkms/vkms_config.c
@@ -192,9 +192,11 @@ struct vkms_config_plane *vkms_config_create_plane(struct vkms_config *config)

        return plane_cfg;
 }
+EXPORT_SYMBOL_IF_KUNIT(vkms_config_create_plane);

 void vkms_config_destroy_plane(struct vkms_config_plane *plane_cfg)
 {
        list_del(&plane_cfg->link);
        kfree(plane_cfg);
 }
+EXPORT_SYMBOL_IF_KUNIT(vkms_config_destroy_plane);


> ---
>  .clang-format                                 |   1 +
>  drivers/gpu/drm/vkms/tests/vkms_config_test.c | 140 +++++++++++++++++-
>  drivers/gpu/drm/vkms/vkms_config.c            | 124 +++++++++++++++-
>  drivers/gpu/drm/vkms/vkms_config.h            |  73 ++++++++-
>  drivers/gpu/drm/vkms/vkms_output.c            |  42 ++----
>  5 files changed, 344 insertions(+), 36 deletions(-)
> 
> diff --git a/.clang-format b/.clang-format
> index fe1aa1a30d40..c585d2a5b395 100644
> --- a/.clang-format
> +++ b/.clang-format
> @@ -690,6 +690,7 @@ ForEachMacros:
>    - 'v4l2_m2m_for_each_src_buf'
>    - 'v4l2_m2m_for_each_src_buf_safe'
>    - 'virtio_device_for_each_vq'
> +  - 'vkms_config_for_each_plane'
>    - 'while_for_each_ftrace_op'
>    - 'xa_for_each'
>    - 'xa_for_each_marked'

I did not know about this, perfect!

> diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
> index 6e07139d261c..38ec456d9610 100644
> --- a/drivers/gpu/drm/vkms/tests/vkms_config_test.c
> +++ b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
> @@ -24,6 +24,10 @@ static void vkms_config_test_empty_config(struct kunit *test)
>  	dev_name = NULL;
>  	KUNIT_EXPECT_STREQ(test, vkms_config_get_device_name(config), "test");
>  
> +	KUNIT_EXPECT_TRUE(test, list_empty(&config->planes));
> +
> +	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
> +
>  	vkms_config_destroy(config);
>  }
>  
> @@ -44,16 +48,145 @@ static void vkms_config_test_default_config(struct kunit *test)
>  {
>  	const struct default_config_case *params = test->param_value;
>  	struct vkms_config *config;
> +	struct vkms_config_plane *plane_cfg;
> +	int n_primaries = 0;
> +	int n_cursors = 0;
> +	int n_overlays = 0;
>  
>  	config = vkms_config_default_create(params->enable_cursor,
>  					    params->enable_writeback,
>  					    params->enable_overlay);
>  	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
>  
> -	KUNIT_EXPECT_EQ(test, config->cursor, params->enable_cursor);
>  	KUNIT_EXPECT_EQ(test, config->writeback, params->enable_writeback);
> -	KUNIT_EXPECT_EQ(test, config->overlay, params->enable_overlay);
>  
> +	/* Planes */
> +	list_for_each_entry(plane_cfg, &config->planes, link) {

Why you don't use vkms_config_for_each_plane here?

> +		switch (vkms_config_plane_get_type(plane_cfg)) {
> +		case DRM_PLANE_TYPE_PRIMARY:
> +			n_primaries++;
> +			break;
> +		case DRM_PLANE_TYPE_CURSOR:
> +			n_cursors++;
> +			break;
> +		case DRM_PLANE_TYPE_OVERLAY:
> +			n_overlays++;
> +			break;
> +		default:
> +			KUNIT_FAIL_AND_ABORT(test, "Unknown plane type");
> +		}
> +	}
> +	KUNIT_EXPECT_EQ(test, n_primaries, 1);
> +	KUNIT_EXPECT_EQ(test, n_cursors, params->enable_cursor ? 1 : 0);
> +	KUNIT_EXPECT_EQ(test, n_overlays, params->enable_overlay ? 8 : 0);
> +
> +	KUNIT_EXPECT_TRUE(test, vkms_config_is_valid(config));
> +
> +	vkms_config_destroy(config);
> +}
> +
> +static void vkms_config_test_get_planes(struct kunit *test)
> +{
> +	struct vkms_config *config;
> +	struct vkms_config_plane *plane_cfg;
> +	struct vkms_config_plane *plane_cfg1, *plane_cfg2;
> +	int n_planes = 0;
> +
> +	config = vkms_config_create("test");
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
> +
> +	vkms_config_for_each_plane(config, plane_cfg)
> +		n_planes++;
> +	KUNIT_ASSERT_EQ(test, n_planes, 0);
> +
> +	plane_cfg1 = vkms_config_create_plane(config);
> +	vkms_config_for_each_plane(config, plane_cfg) {
> +		n_planes++;
> +		if (plane_cfg != plane_cfg1)
> +			KUNIT_FAIL(test, "Unexpected plane");
> +	}
> +	KUNIT_ASSERT_EQ(test, n_planes, 1);
> +	n_planes = 0;
> +
> +	plane_cfg2 = vkms_config_create_plane(config);
> +	vkms_config_for_each_plane(config, plane_cfg) {
> +		n_planes++;
> +		if (plane_cfg != plane_cfg1 && plane_cfg != plane_cfg2)
> +			KUNIT_FAIL(test, "Unexpected plane");
> +	}
> +	KUNIT_ASSERT_EQ(test, n_planes, 2);
> +	n_planes = 0;
> +
> +	vkms_config_destroy_plane(plane_cfg1);
> +	vkms_config_for_each_plane(config, plane_cfg) {
> +		n_planes++;
> +		if (plane_cfg != plane_cfg2)
> +			KUNIT_FAIL(test, "Unexpected plane");
> +	}
> +	KUNIT_ASSERT_EQ(test, n_planes, 1);
> +
> +	vkms_config_destroy(config);
> +}
> +
> +static void vkms_config_test_invalid_plane_number(struct kunit *test)
> +{
> +	struct vkms_config *config;
> +	struct vkms_config_plane *plane_cfg;
> +	int n;
> +
> +	config = vkms_config_default_create(false, false, false);
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
> +
> +	/* Invalid: No planes */
> +	plane_cfg = list_first_entry(&config->planes, typeof(*plane_cfg), link);
> +	vkms_config_destroy_plane(plane_cfg);
> +	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
> +
> +	/* Invalid: Too many planes */
> +	for (n = 0; n <= 32; n++)
> +		vkms_config_create_plane(config);
> +
> +	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
> +
> +	vkms_config_destroy(config);
> +}
> +
> +static void vkms_config_test_valid_plane_type(struct kunit *test)
> +{
> +	struct vkms_config *config;
> +	struct vkms_config_plane *plane_cfg;
> +
> +	config = vkms_config_default_create(false, false, false);
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
> +
> +	plane_cfg = list_first_entry(&config->planes, typeof(*plane_cfg), link);
> +	vkms_config_destroy_plane(plane_cfg);
> +
> +	/* Invalid: No primary plane */
> +	plane_cfg = vkms_config_create_plane(config);
> +	vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_OVERLAY);
> +	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
> +
> +	/* Invalid: Multiple primary planes */
> +	plane_cfg = vkms_config_create_plane(config);
> +	vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_PRIMARY);
> +	plane_cfg = vkms_config_create_plane(config);
> +	vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_PRIMARY);
> +	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
> +
> +	/* Valid: One primary plane */
> +	vkms_config_destroy_plane(plane_cfg);
> +	KUNIT_EXPECT_TRUE(test, vkms_config_is_valid(config));
> +
> +	/* Invalid: Multiple cursor planes */
> +	plane_cfg = vkms_config_create_plane(config);
> +	vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_CURSOR);
> +	plane_cfg = vkms_config_create_plane(config);
> +	vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_CURSOR);
> +	KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
> +
> +	/* Valid: One primary and one cursor plane */
> +	vkms_config_destroy_plane(plane_cfg);
>  	KUNIT_EXPECT_TRUE(test, vkms_config_is_valid(config));
>  
>  	vkms_config_destroy(config);
> @@ -63,6 +196,9 @@ static struct kunit_case vkms_config_test_cases[] = {
>  	KUNIT_CASE(vkms_config_test_empty_config),
>  	KUNIT_CASE_PARAM(vkms_config_test_default_config,
>  			 default_config_gen_params),
> +	KUNIT_CASE(vkms_config_test_get_planes),
> +	KUNIT_CASE(vkms_config_test_invalid_plane_number),
> +	KUNIT_CASE(vkms_config_test_valid_plane_type),
>  	{}
>  };
>  
> diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
> index 67f71d29596e..8feacfc155be 100644
> --- a/drivers/gpu/drm/vkms/vkms_config.c
> +++ b/drivers/gpu/drm/vkms/vkms_config.c
> @@ -21,6 +21,8 @@ struct vkms_config *vkms_config_create(const char *dev_name)
>  		return ERR_PTR(-ENOMEM);
>  	}
>  
> +	INIT_LIST_HEAD(&config->planes);
> +
>  	return config;
>  }
>  
> @@ -29,26 +31,114 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
>  					       bool enable_overlay)
>  {
>  	struct vkms_config *config;
> +	struct vkms_config_plane *plane_cfg;
> +	int n;
>  
>  	config = vkms_config_create(DEFAULT_DEVICE_NAME);
>  	if (IS_ERR(config))
>  		return config;
>  
> -	config->cursor = enable_cursor;
>  	config->writeback = enable_writeback;
> -	config->overlay = enable_overlay;
> +
> +	plane_cfg = vkms_config_create_plane(config);
> +	if (IS_ERR(plane_cfg))
> +		goto err_alloc;
> +	vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_PRIMARY);
> +
> +	if (enable_overlay) {
> +		for (n = 0; n < NUM_OVERLAY_PLANES; n++) {
> +			plane_cfg = vkms_config_create_plane(config);
> +			if (IS_ERR(plane_cfg))
> +				goto err_alloc;
> +			vkms_config_plane_set_type(plane_cfg,
> +						   DRM_PLANE_TYPE_OVERLAY);
> +		}
> +	}
> +
> +	if (enable_cursor) {
> +		plane_cfg = vkms_config_create_plane(config);
> +		if (IS_ERR(plane_cfg))
> +			goto err_alloc;
> +		vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_CURSOR);
> +	}
>  
>  	return config;
> +
> +err_alloc:
> +	vkms_config_destroy(config);
> +	return ERR_PTR(-ENOMEM);
>  }
>  
>  void vkms_config_destroy(struct vkms_config *config)
>  {
> +	struct vkms_config_plane *plane_cfg, *plane_tmp;
> +
> +	list_for_each_entry_safe(plane_cfg, plane_tmp, &config->planes, link)
> +		vkms_config_destroy_plane(plane_cfg);
> +
>  	kfree_const(config->dev_name);
>  	kfree(config);
>  }
>  
> +static bool valid_plane_number(struct vkms_config *config)
> +{
> +	struct drm_device *dev = &config->dev->drm;

I think config->dev is not always set at this point. Can you change to 

	config->dev ? config->dev->drm : NULL

> +	size_t n_planes;
> +
> +	n_planes = list_count_nodes(&config->planes);
> +	if (n_planes <= 0 || n_planes >= 32) {
> +		drm_info(dev, "The number of planes must be between 1 and 31\n");
> +		return false;
> +	}
> +
> +	return true;
> +}
> +
> +static bool valid_plane_type(struct vkms_config *config)
> +{
> +	struct drm_device *dev = &config->dev->drm;

ditto

> +	struct vkms_config_plane *plane_cfg;
> +	bool has_primary_plane = false;
> +	bool has_cursor_plane = false;
> +
> +	vkms_config_for_each_plane(config, plane_cfg) {
> +		enum drm_plane_type type;
> +
> +		type = vkms_config_plane_get_type(plane_cfg);
> +
> +		if (type == DRM_PLANE_TYPE_PRIMARY) {
> +			if (has_primary_plane) {
> +				drm_info(dev, "Multiple primary planes\n");
> +				return false;
> +			}
> +
> +			has_primary_plane = true;
> +		} else if (type == DRM_PLANE_TYPE_CURSOR) {
> +			if (has_cursor_plane) {
> +				drm_info(dev, "Multiple cursor planes\n");
> +				return false;
> +			}
> +
> +			has_cursor_plane = true;
> +		}
> +	}
> +
> +	if (!has_primary_plane) {
> +		drm_info(dev, "Primary plane not found\n");
> +		return false;
> +	}
> +
> +	return true;
> +}
> +
>  bool vkms_config_is_valid(struct vkms_config *config)
>  {
> +	if (!valid_plane_number(config))
> +		return false;
> +
> +	if (!valid_plane_type(config))
> +		return false;
> +
>  	return true;
>  }
>  
> @@ -58,12 +148,17 @@ static int vkms_config_show(struct seq_file *m, void *data)
>  	struct drm_device *dev = entry->dev;
>  	struct vkms_device *vkmsdev = drm_device_to_vkms_device(dev);
>  	const char *dev_name;
> +	struct vkms_config_plane *plane_cfg;
>  
>  	dev_name = vkms_config_get_device_name((struct vkms_config *)vkmsdev->config);
>  	seq_printf(m, "dev_name=%s\n", dev_name);
>  	seq_printf(m, "writeback=%d\n", vkmsdev->config->writeback);
> -	seq_printf(m, "cursor=%d\n", vkmsdev->config->cursor);
> -	seq_printf(m, "overlay=%d\n", vkmsdev->config->overlay);
> +
> +	vkms_config_for_each_plane(vkmsdev->config, plane_cfg) {
> +		seq_puts(m, "plane:\n");
> +		seq_printf(m, "\ttype=%d\n",
> +			   vkms_config_plane_get_type(plane_cfg));
> +	}
>  
>  	return 0;
>  }
> @@ -77,3 +172,24 @@ void vkms_config_register_debugfs(struct vkms_device *vkms_device)
>  	drm_debugfs_add_files(&vkms_device->drm, vkms_config_debugfs_list,
>  			      ARRAY_SIZE(vkms_config_debugfs_list));
>  }
> +
> +struct vkms_config_plane *vkms_config_create_plane(struct vkms_config *config)
> +{
> +	struct vkms_config_plane *plane_cfg;
> +
> +	plane_cfg = kzalloc(sizeof(*plane_cfg), GFP_KERNEL);
> +	if (!plane_cfg)
> +		return ERR_PTR(-ENOMEM);
> +
> +	vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_OVERLAY);
> +
> +	list_add_tail(&plane_cfg->link, &config->planes);
> +
> +	return plane_cfg;
> +}
> +
> +void vkms_config_destroy_plane(struct vkms_config_plane *plane_cfg)
> +{
> +	list_del(&plane_cfg->link);
> +	kfree(plane_cfg);
> +}
> diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
> index 0376dceaf6be..1c7ffc27f2a8 100644
> --- a/drivers/gpu/drm/vkms/vkms_config.h
> +++ b/drivers/gpu/drm/vkms/vkms_config.h
> @@ -3,6 +3,7 @@
>  #ifndef _VKMS_CONFIG_H_
>  #define _VKMS_CONFIG_H_
>  
> +#include <linux/list.h>
>  #include <linux/types.h>
>  
>  #include "vkms_drv.h"
> @@ -12,18 +13,44 @@
>   *
>   * @dev_name: Name of the device
>   * @writeback: If true, a writeback buffer can be attached to the CRTC
> - * @cursor: If true, a cursor plane is created in the VKMS device
> - * @overlay: If true, NUM_OVERLAY_PLANES will be created for the VKMS device
> + * @planes: List of planes configured for the device
>   * @dev: Used to store the current VKMS device. Only set when the device is instantiated.
>   */
>  struct vkms_config {
>  	const char *dev_name;
>  	bool writeback;
> -	bool cursor;
> -	bool overlay;
> +	struct list_head planes;
>  	struct vkms_device *dev;
>  };
>  
> +/**
> + * struct vkms_config_plane
> + *
> + * @link: Link to the others planes in vkms_config
> + * @type: Type of the plane. The creator of configuration needs to ensures that
> + *        at least one primary plane is present.
> + * @plane: Internal usage. This pointer should never be considered as valid.
> + *         It can be used to store a temporary reference to a VKMS plane during
> + *         device creation. This pointer is not managed by the configuration and
> + *         must be managed by other means.
> + */
> +struct vkms_config_plane {
> +	struct list_head link;
> +
> +	enum drm_plane_type type;
> +
> +	/* Internal usage */
> +	struct vkms_plane *plane;
> +};
> +
> +/**
> + * vkms_config_for_each_plane - Iterate over the vkms_config planes
> + * @config: &struct vkms_config pointer
> + * @plane_cfg: &struct vkms_config_plane pointer used as cursor
> + */
> +#define vkms_config_for_each_plane(config, plane_cfg) \
> +	list_for_each_entry((plane_cfg), &(config)->planes, link)
> +
>  /**
>   * vkms_config_create() - Create a new VKMS configuration
>   * @dev_name: Name of the device
> @@ -84,4 +111,42 @@ bool vkms_config_is_valid(struct vkms_config *config);
>   */
>  void vkms_config_register_debugfs(struct vkms_device *vkms_device);
>  
> +/**
> + * vkms_config_create_plane() - Add a new plane configuration
> + * @config: Configuration to add the plane to
> + *
> + * Returns:
> + * The new plane configuration or an error. Call vkms_config_destroy_plane() to
> + * free the returned plane configuration.
> + */
> +struct vkms_config_plane *vkms_config_create_plane(struct vkms_config *config);
> +
> +/**
> + * vkms_config_destroy_plane() - Remove and free a plane configuration
> + * @plane_cfg: Plane configuration to destroy
> + */
> +void vkms_config_destroy_plane(struct vkms_config_plane *plane_cfg);
> +
> +/**
> + * vkms_config_plane_type() - Return the plane type
> + * @plane_cfg: Plane to get the type from
> + */
> +static inline enum drm_plane_type
> +vkms_config_plane_get_type(struct vkms_config_plane *plane_cfg)
> +{
> +	return plane_cfg->type;
> +}
> +
> +/**
> + * vkms_config_plane_set_type() - Set the plane type
> + * @plane_cfg: Plane to set the type to
> + * @type: New plane type
> + */
> +static inline void
> +vkms_config_plane_set_type(struct vkms_config_plane *plane_cfg,
> +			   enum drm_plane_type type)
> +{
> +	plane_cfg->type = type;
> +}
> +
>  #endif /* _VKMS_CONFIG_H_ */
> diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
> index 414cc933af41..08ea691db299 100644
> --- a/drivers/gpu/drm/vkms/vkms_output.c
> +++ b/drivers/gpu/drm/vkms/vkms_output.c
> @@ -11,28 +11,29 @@ int vkms_output_init(struct vkms_device *vkmsdev)
>  	struct vkms_connector *connector;
>  	struct drm_encoder *encoder;
>  	struct vkms_output *output;
> -	struct vkms_plane *primary, *overlay, *cursor = NULL;
> +	struct vkms_plane *primary = NULL, *cursor = NULL;
> +	struct vkms_config_plane *plane_cfg;
>  	int ret;
>  	int writeback;
> -	unsigned int n;
>  
>  	if (!vkms_config_is_valid(vkmsdev->config))
>  		return -EINVAL;
>  
> -	/*
> -	 * Initialize used plane. One primary plane is required to perform the composition.
> -	 *
> -	 * The overlay and cursor planes are not mandatory, but can be used to perform complex
> -	 * composition.
> -	 */
> -	primary = vkms_plane_init(vkmsdev, DRM_PLANE_TYPE_PRIMARY);
> -	if (IS_ERR(primary))
> -		return PTR_ERR(primary);
> +	vkms_config_for_each_plane(vkmsdev->config, plane_cfg) {
> +		enum drm_plane_type type;
>  
> -	if (vkmsdev->config->cursor) {
> -		cursor = vkms_plane_init(vkmsdev, DRM_PLANE_TYPE_CURSOR);
> -		if (IS_ERR(cursor))
> -			return PTR_ERR(cursor);
> +		type = vkms_config_plane_get_type(plane_cfg);
> +
> +		plane_cfg->plane = vkms_plane_init(vkmsdev, type);
> +		if (IS_ERR(plane_cfg->plane)) {
> +			DRM_DEV_ERROR(dev->dev, "Failed to init vkms plane\n");
> +			return PTR_ERR(plane_cfg->plane);
> +		}
> +
> +		if (type == DRM_PLANE_TYPE_PRIMARY)
> +			primary = plane_cfg->plane;
> +		else if (type == DRM_PLANE_TYPE_CURSOR)
> +			cursor = plane_cfg->plane;
>  	}
>  
>  	output = vkms_crtc_init(dev, &primary->base,
> @@ -42,17 +43,6 @@ int vkms_output_init(struct vkms_device *vkmsdev)
>  		return PTR_ERR(output);
>  	}
>  
> -	if (vkmsdev->config->overlay) {
> -		for (n = 0; n < NUM_OVERLAY_PLANES; n++) {
> -			overlay = vkms_plane_init(vkmsdev, DRM_PLANE_TYPE_OVERLAY);
> -			if (IS_ERR(overlay)) {
> -				DRM_DEV_ERROR(dev->dev, "Failed to init vkms plane\n");
> -				return PTR_ERR(overlay);
> -			}
> -			overlay->base.possible_crtcs = drm_crtc_mask(&output->crtc);
> -		}
> -	}
> -
>  	connector = vkms_connector_init(vkmsdev);
>  	if (IS_ERR(connector)) {
>  		DRM_ERROR("Failed to init connector\n");
> -- 
> 2.48.1
> 

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 04/15] drm/vkms: Add KUnit test scaffolding
  2025-02-11 11:09 ` [PATCH v2 04/15] drm/vkms: Add KUnit test scaffolding José Expósito
@ 2025-02-13 13:59   ` Louis Chauvet
  0 siblings, 0 replies; 32+ messages in thread
From: Louis Chauvet @ 2025-02-13 13:59 UTC (permalink / raw)
  To: José Expósito
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel, Arthur Grillo

On 11/02/25 - 12:09, José Expósito wrote:
> Add the required boilerplate to start creating KUnit test.
> 
> To run the tests:
> 
>     $ ./tools/testing/kunit/kunit.py run \
>       --kunitconfig=drivers/gpu/drm/vkms/tests
> 
> Co-developed-by: Arthur Grillo <arthurgrillo@riseup.net>
> Signed-off-by: Arthur Grillo <arthurgrillo@riseup.net>
> Co-developed-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
> ---
>  drivers/gpu/drm/vkms/Kconfig                  | 15 +++++++++++++++
>  drivers/gpu/drm/vkms/Makefile                 |  1 +
>  drivers/gpu/drm/vkms/tests/.kunitconfig       |  4 ++++
>  drivers/gpu/drm/vkms/tests/Makefile           |  3 +++
>  drivers/gpu/drm/vkms/tests/vkms_config_test.c | 19 +++++++++++++++++++
>  5 files changed, 42 insertions(+)
>  create mode 100644 drivers/gpu/drm/vkms/tests/.kunitconfig
>  create mode 100644 drivers/gpu/drm/vkms/tests/Makefile
>  create mode 100644 drivers/gpu/drm/vkms/tests/vkms_config_test.c
> 
> diff --git a/drivers/gpu/drm/vkms/Kconfig b/drivers/gpu/drm/vkms/Kconfig
> index 9def079f685b..d4665e913de7 100644
> --- a/drivers/gpu/drm/vkms/Kconfig
> +++ b/drivers/gpu/drm/vkms/Kconfig
> @@ -14,3 +14,18 @@ config DRM_VKMS
>  	  a VKMS.
>  
>  	  If M is selected the module will be called vkms.
> +
> +config DRM_VKMS_KUNIT_TESTS
> +	tristate "KUnit tests for VKMS." if !KUNIT_ALL_TESTS

If there is a v3, can you remove the dot?

With or without this:

Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>

Thanks,
Louis Chauvet

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 08/15] drm/vkms: Add a validation function for VKMS configuration
  2025-02-11 11:09 ` [PATCH v2 08/15] drm/vkms: Add a validation function for VKMS configuration José Expósito
@ 2025-02-13 13:59   ` Louis Chauvet
  2025-02-14 15:53     ` José Expósito
  0 siblings, 1 reply; 32+ messages in thread
From: Louis Chauvet @ 2025-02-13 13:59 UTC (permalink / raw)
  To: José Expósito
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel

On 11/02/25 - 12:09, José Expósito wrote:
> From: Louis Chauvet <louis.chauvet@bootlin.com>
> 
> As the configuration will be used by userspace, add a validator to avoid
> creating a broken DRM device.
> 
> For the moment, the function always returns true, but rules will be
> added in future patches.
> 
> Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Co-developed-by: José Expósito <jose.exposito89@gmail.com>
> Signed-off-by: José Expósito <jose.exposito89@gmail.com>

The compilation is broken when building as module:


diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
index b9267aef4804..82335006c94a 100644
--- a/drivers/gpu/drm/vkms/vkms_config.c
+++ b/drivers/gpu/drm/vkms/vkms_config.c
@@ -55,6 +55,7 @@ bool vkms_config_is_valid(struct vkms_config *config)
 {
        return true;
 }
+EXPORT_SYMBOL_IF_KUNIT(vkms_config_is_valid);

 static int vkms_config_show(struct seq_file *m, void *data)
 {


> ---
>  drivers/gpu/drm/vkms/tests/vkms_config_test.c |  2 ++
>  drivers/gpu/drm/vkms/vkms_config.c            |  5 +++++
>  drivers/gpu/drm/vkms/vkms_config.h            | 10 ++++++++++
>  drivers/gpu/drm/vkms/vkms_drv.h               |  2 +-
>  drivers/gpu/drm/vkms/vkms_output.c            |  3 +++
>  5 files changed, 21 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
> index 92798590051b..6e07139d261c 100644
> --- a/drivers/gpu/drm/vkms/tests/vkms_config_test.c
> +++ b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
> @@ -54,6 +54,8 @@ static void vkms_config_test_default_config(struct kunit *test)
>  	KUNIT_EXPECT_EQ(test, config->writeback, params->enable_writeback);
>  	KUNIT_EXPECT_EQ(test, config->overlay, params->enable_overlay);
>  
> +	KUNIT_EXPECT_TRUE(test, vkms_config_is_valid(config));
> +
>  	vkms_config_destroy(config);
>  }
>  
> diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
> index 11b0e539920b..67f71d29596e 100644
> --- a/drivers/gpu/drm/vkms/vkms_config.c
> +++ b/drivers/gpu/drm/vkms/vkms_config.c
> @@ -47,6 +47,11 @@ void vkms_config_destroy(struct vkms_config *config)
>  	kfree(config);
>  }
>  
> +bool vkms_config_is_valid(struct vkms_config *config)
> +{
> +	return true;
> +}
> +
>  static int vkms_config_show(struct seq_file *m, void *data)
>  {
>  	struct drm_debugfs_entry *entry = m->private;
> diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
> index fcaa909fb2e0..0376dceaf6be 100644
> --- a/drivers/gpu/drm/vkms/vkms_config.h
> +++ b/drivers/gpu/drm/vkms/vkms_config.h
> @@ -67,6 +67,16 @@ vkms_config_get_device_name(struct vkms_config *config)
>  	return config->dev_name;
>  }
>  
> +/**
> + * vkms_config_is_valid() - Validate a configuration
> + * @config: Configuration to validate
> + *
> + * Returns:
> + * Whether the configuration is valid or not.
> + * For example, a configuration without primary planes is not valid.
> + */
> +bool vkms_config_is_valid(struct vkms_config *config);
> +

I think here we can take a const pointer.

>  /**
>   * vkms_config_register_debugfs() - Register a debugfs file to show the device's
>   * configuration
> diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h
> index a74a7fc3a056..95afc39ce985 100644
> --- a/drivers/gpu/drm/vkms/vkms_drv.h
> +++ b/drivers/gpu/drm/vkms/vkms_drv.h
> @@ -204,7 +204,7 @@ struct vkms_config;
>  struct vkms_device {
>  	struct drm_device drm;
>  	struct platform_device *platform;
> -	const struct vkms_config *config;
> +	struct vkms_config *config;

So we can keep a const pointer here (for me the device should never modify 
vkms_config)

>  };
>  
>  /*
> diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
> index 068a7f87ecec..414cc933af41 100644
> --- a/drivers/gpu/drm/vkms/vkms_output.c
> +++ b/drivers/gpu/drm/vkms/vkms_output.c
> @@ -16,6 +16,9 @@ int vkms_output_init(struct vkms_device *vkmsdev)
>  	int writeback;
>  	unsigned int n;
>  
> +	if (!vkms_config_is_valid(vkmsdev->config))
> +		return -EINVAL;
> +
>  	/*
>  	 * Initialize used plane. One primary plane is required to perform the composition.
>  	 *
> -- 
> 2.48.1
> 

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 05/15] drm/vkms: Extract vkms_config header
  2025-02-13 13:59   ` Louis Chauvet
@ 2025-02-13 15:36     ` José Expósito
  2025-02-14 17:03       ` Louis Chauvet
  0 siblings, 1 reply; 32+ messages in thread
From: José Expósito @ 2025-02-13 15:36 UTC (permalink / raw)
  To: Louis Chauvet
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel

On Thu, Feb 13, 2025 at 02:59:25PM +0100, Louis Chauvet wrote:
> On 11/02/25 - 12:09, José Expósito wrote:
> > Creating a new vkms_config structure will be more complex once we
> > start adding more options.
> > 
> > Extract the vkms_config structure to its own header and source files
> > and add functions to create and delete a vkms_config and to initialize
> > debugfs.
> > 
> > Refactor, no functional changes.
> > 
> > Co-developed-by: Louis Chauvet <louis.chauvet@bootlin.com>
> > Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> > Signed-off-by: José Expósito <jose.exposito89@gmail.com>
> 
> This does not build in module, can you add this in the next version?

Interesting. I have "CONFIG_DRM_VKMS=m" in my config and it compiles here.

What do you have as module? CONFIG_DRM_VKMS_KUNIT_TESTS=m?

I'll rebuild every patch to make sure there are no errors.

Thanks for the heads up!
Jose
 
> diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
> index 152b2ecd6aef..42caa421876e 100644
> --- a/drivers/gpu/drm/vkms/vkms_config.c
> +++ b/drivers/gpu/drm/vkms/vkms_config.c
> @@ -4,6 +4,7 @@
> 
>  #include <drm/drm_print.h>
>  #include <drm/drm_debugfs.h>
> +#include <kunit/visibility.h>
> 
>  #include "vkms_config.h"
> 
> @@ -17,11 +18,13 @@ struct vkms_config *vkms_config_create(void)
> 
>         return config;
>  }
> +EXPORT_SYMBOL_IF_KUNIT(vkms_config_create);
> 
>  void vkms_config_destroy(struct vkms_config *config)
>  {
>         kfree(config);
>  }
> +EXPORT_SYMBOL_IF_KUNIT(vkms_config_destroy);
> 
>  static int vkms_config_show(struct seq_file *m, void *data)
>  {
> 
> With this:
> 
> Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 08/15] drm/vkms: Add a validation function for VKMS configuration
  2025-02-13 13:59   ` Louis Chauvet
@ 2025-02-14 15:53     ` José Expósito
  2025-02-14 18:20       ` Louis Chauvet
  0 siblings, 1 reply; 32+ messages in thread
From: José Expósito @ 2025-02-14 15:53 UTC (permalink / raw)
  To: Louis Chauvet
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel

On Thu, Feb 13, 2025 at 02:59:52PM +0100, Louis Chauvet wrote:
> On 11/02/25 - 12:09, José Expósito wrote:
> > From: Louis Chauvet <louis.chauvet@bootlin.com>
> > 
> > As the configuration will be used by userspace, add a validator to avoid
> > creating a broken DRM device.
> > 
> > For the moment, the function always returns true, but rules will be
> > added in future patches.
> > 
> > Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
> > Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> > Co-developed-by: José Expósito <jose.exposito89@gmail.com>
> > Signed-off-by: José Expósito <jose.exposito89@gmail.com>
> 
> The compilation is broken when building as module:
> 
> 
> diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
> index b9267aef4804..82335006c94a 100644
> --- a/drivers/gpu/drm/vkms/vkms_config.c
> +++ b/drivers/gpu/drm/vkms/vkms_config.c
> @@ -55,6 +55,7 @@ bool vkms_config_is_valid(struct vkms_config *config)
>  {
>         return true;
>  }
> +EXPORT_SYMBOL_IF_KUNIT(vkms_config_is_valid);

Fixed the issue in all patches, thanks!
 
> > [...]
> > 
> > diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
> > index fcaa909fb2e0..0376dceaf6be 100644
> > --- a/drivers/gpu/drm/vkms/vkms_config.h
> > +++ b/drivers/gpu/drm/vkms/vkms_config.h
> > @@ -67,6 +67,16 @@ vkms_config_get_device_name(struct vkms_config *config)
> >  	return config->dev_name;
> >  }
> >  
> > +/**
> > + * vkms_config_is_valid() - Validate a configuration
> > + * @config: Configuration to validate
> > + *
> > + * Returns:
> > + * Whether the configuration is valid or not.
> > + * For example, a configuration without primary planes is not valid.
> > + */
> > +bool vkms_config_is_valid(struct vkms_config *config);
> > +
> 
> I think here we can take a const pointer.
> 
> >  /**
> >   * vkms_config_register_debugfs() - Register a debugfs file to show the device's
> >   * configuration
> > diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h
> > index a74a7fc3a056..95afc39ce985 100644
> > --- a/drivers/gpu/drm/vkms/vkms_drv.h
> > +++ b/drivers/gpu/drm/vkms/vkms_drv.h
> > @@ -204,7 +204,7 @@ struct vkms_config;
> >  struct vkms_device {
> >  	struct drm_device drm;
> >  	struct platform_device *platform;
> > -	const struct vkms_config *config;
> > +	struct vkms_config *config;
> 
> So we can keep a const pointer here (for me the device should never modify 
> vkms_config)

I tryed keeping the const pointer, but, since list_count_nodes() is used in
several valid_* functions and it takes a non-const pointer, it causes
warnings.

We can fix them with a cast:

  n_planes = list_count_nodes((struct list_head *)&config->planes);

But I feel that keeping the "const" creates more issues than it solves.

Anyway, if you prefer this pointer to be const, I will change it in v3.

Jose
 
> >  };
> >  
> >  /*
> > diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
> > index 068a7f87ecec..414cc933af41 100644
> > --- a/drivers/gpu/drm/vkms/vkms_output.c
> > +++ b/drivers/gpu/drm/vkms/vkms_output.c
> > @@ -16,6 +16,9 @@ int vkms_output_init(struct vkms_device *vkmsdev)
> >  	int writeback;
> >  	unsigned int n;
> >  
> > +	if (!vkms_config_is_valid(vkmsdev->config))
> > +		return -EINVAL;
> > +
> >  	/*
> >  	 * Initialize used plane. One primary plane is required to perform the composition.
> >  	 *
> > -- 
> > 2.48.1
> > 

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 05/15] drm/vkms: Extract vkms_config header
  2025-02-13 15:36     ` José Expósito
@ 2025-02-14 17:03       ` Louis Chauvet
  0 siblings, 0 replies; 32+ messages in thread
From: Louis Chauvet @ 2025-02-14 17:03 UTC (permalink / raw)
  To: José Expósito
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel



Le 13/02/2025 à 16:36, José Expósito a écrit :
> On Thu, Feb 13, 2025 at 02:59:25PM +0100, Louis Chauvet wrote:
>> On 11/02/25 - 12:09, José Expósito wrote:
>>> Creating a new vkms_config structure will be more complex once we
>>> start adding more options.
>>>
>>> Extract the vkms_config structure to its own header and source files
>>> and add functions to create and delete a vkms_config and to initialize
>>> debugfs.
>>>
>>> Refactor, no functional changes.
>>>
>>> Co-developed-by: Louis Chauvet <louis.chauvet@bootlin.com>
>>> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
>>> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
>>
>> This does not build in module, can you add this in the next version?
> 
> Interesting. I have "CONFIG_DRM_VKMS=m" in my config and it compiles here.
> 
> What do you have as module? CONFIG_DRM_VKMS_KUNIT_TESTS=m?

Yes sorry, I forgot to say it was for kunit tests. I just saw your other 
mail, thank you for the fix on all patches!

Louis Chauvet

> I'll rebuild every patch to make sure there are no errors.
> 
> Thanks for the heads up!
> Jose
>   
>> diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
>> index 152b2ecd6aef..42caa421876e 100644
>> --- a/drivers/gpu/drm/vkms/vkms_config.c
>> +++ b/drivers/gpu/drm/vkms/vkms_config.c
>> @@ -4,6 +4,7 @@
>>
>>   #include <drm/drm_print.h>
>>   #include <drm/drm_debugfs.h>
>> +#include <kunit/visibility.h>
>>
>>   #include "vkms_config.h"
>>
>> @@ -17,11 +18,13 @@ struct vkms_config *vkms_config_create(void)
>>
>>          return config;
>>   }
>> +EXPORT_SYMBOL_IF_KUNIT(vkms_config_create);
>>
>>   void vkms_config_destroy(struct vkms_config *config)
>>   {
>>          kfree(config);
>>   }
>> +EXPORT_SYMBOL_IF_KUNIT(vkms_config_destroy);
>>
>>   static int vkms_config_show(struct seq_file *m, void *data)
>>   {
>>
>> With this:
>>
>> Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>

-- 
Louis Chauvet, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 08/15] drm/vkms: Add a validation function for VKMS configuration
  2025-02-14 15:53     ` José Expósito
@ 2025-02-14 18:20       ` Louis Chauvet
  0 siblings, 0 replies; 32+ messages in thread
From: Louis Chauvet @ 2025-02-14 18:20 UTC (permalink / raw)
  To: José Expósito
  Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
	tzimmermann, airlied, dri-devel, linux-kernel



Le 14/02/2025 à 16:53, José Expósito a écrit :
> On Thu, Feb 13, 2025 at 02:59:52PM +0100, Louis Chauvet wrote:
>> On 11/02/25 - 12:09, José Expósito wrote:
>>> From: Louis Chauvet <louis.chauvet@bootlin.com>
>>>
>>> As the configuration will be used by userspace, add a validator to avoid
>>> creating a broken DRM device.
>>>
>>> For the moment, the function always returns true, but rules will be
>>> added in future patches.
>>>
>>> Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
>>> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
>>> Co-developed-by: José Expósito <jose.exposito89@gmail.com>
>>> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
>>
>> The compilation is broken when building as module:
>>
>>
>> diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
>> index b9267aef4804..82335006c94a 100644
>> --- a/drivers/gpu/drm/vkms/vkms_config.c
>> +++ b/drivers/gpu/drm/vkms/vkms_config.c
>> @@ -55,6 +55,7 @@ bool vkms_config_is_valid(struct vkms_config *config)
>>   {
>>          return true;
>>   }
>> +EXPORT_SYMBOL_IF_KUNIT(vkms_config_is_valid);
> 
> Fixed the issue in all patches, thanks!
>   
>>> [...]
>>>
>>> diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
>>> index fcaa909fb2e0..0376dceaf6be 100644
>>> --- a/drivers/gpu/drm/vkms/vkms_config.h
>>> +++ b/drivers/gpu/drm/vkms/vkms_config.h
>>> @@ -67,6 +67,16 @@ vkms_config_get_device_name(struct vkms_config *config)
>>>   	return config->dev_name;
>>>   }
>>>   
>>> +/**
>>> + * vkms_config_is_valid() - Validate a configuration
>>> + * @config: Configuration to validate
>>> + *
>>> + * Returns:
>>> + * Whether the configuration is valid or not.
>>> + * For example, a configuration without primary planes is not valid.
>>> + */
>>> +bool vkms_config_is_valid(struct vkms_config *config);
>>> +
>>
>> I think here we can take a const pointer.
>>
>>>   /**
>>>    * vkms_config_register_debugfs() - Register a debugfs file to show the device's
>>>    * configuration
>>> diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h
>>> index a74a7fc3a056..95afc39ce985 100644
>>> --- a/drivers/gpu/drm/vkms/vkms_drv.h
>>> +++ b/drivers/gpu/drm/vkms/vkms_drv.h
>>> @@ -204,7 +204,7 @@ struct vkms_config;
>>>   struct vkms_device {
>>>   	struct drm_device drm;
>>>   	struct platform_device *platform;
>>> -	const struct vkms_config *config;
>>> +	struct vkms_config *config;
>>
>> So we can keep a const pointer here (for me the device should never modify
>> vkms_config)
> 
> I tryed keeping the const pointer, but, since list_count_nodes() is used in
> several valid_* functions and it takes a non-const pointer, it causes
> warnings.

I did not see this issue, and obviously no casting is better!

But I don't understand why list_count_node needs a non-const pointer. I 
just compile-tested an allyesconfig with a const parameter, it seems to 
works without issue (no warning, at least on x86_64, I just started some 
build on other architectures to check most of the drivers).

I will submit a patch next week to make the list_count_nodes parameter 
const.

> We can fix them with a cast:
> 
>    n_planes = list_count_nodes((struct list_head *)&config->planes);
> 
> But I feel that keeping the "const" creates more issues than it solves.
> 
> Anyway, if you prefer this pointer to be const, I will change it in v3.

Please use a const pointer + cast, and we will remove it once the 
list_count_nodes patch is merged.

Thanks,
Louis Chauvet

> Jose
>   
>>>   };
>>>   
>>>   /*
>>> diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
>>> index 068a7f87ecec..414cc933af41 100644
>>> --- a/drivers/gpu/drm/vkms/vkms_output.c
>>> +++ b/drivers/gpu/drm/vkms/vkms_output.c
>>> @@ -16,6 +16,9 @@ int vkms_output_init(struct vkms_device *vkmsdev)
>>>   	int writeback;
>>>   	unsigned int n;
>>>   
>>> +	if (!vkms_config_is_valid(vkmsdev->config))
>>> +		return -EINVAL;
>>> +
>>>   	/*
>>>   	 * Initialize used plane. One primary plane is required to perform the composition.
>>>   	 *
>>> -- 
>>> 2.48.1
>>>

-- 
Louis Chauvet, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com


^ permalink raw reply	[flat|nested] 32+ messages in thread

end of thread, other threads:[~2025-02-14 18:21 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-02-11 11:08 [PATCH v2 00/15] drm/vkms: Allow to configure device José Expósito
2025-02-11 11:08 ` [PATCH v2 01/15] drm/vkms: Fix use after free and double free on init error José Expósito
2025-02-11 14:33   ` Thomas Zimmermann
2025-02-12  8:52     ` José Expósito
2025-02-11 11:08 ` [PATCH v2 02/15] drm/vkms: Extract vkms_connector header José Expósito
2025-02-11 11:09 ` [PATCH v2 03/15] drm/vkms: Create vkms_connector struct José Expósito
2025-02-11 11:09 ` [PATCH v2 04/15] drm/vkms: Add KUnit test scaffolding José Expósito
2025-02-13 13:59   ` Louis Chauvet
2025-02-11 11:09 ` [PATCH v2 05/15] drm/vkms: Extract vkms_config header José Expósito
2025-02-13 13:59   ` Louis Chauvet
2025-02-13 15:36     ` José Expósito
2025-02-14 17:03       ` Louis Chauvet
2025-02-11 11:09 ` [PATCH v2 06/15] drm/vkms: Move default_config creation to its own function José Expósito
2025-02-13 13:59   ` Louis Chauvet
2025-02-11 11:09 ` [PATCH v2 07/15] drm/vkms: Set device name from vkms_config José Expósito
2025-02-11 11:09 ` [PATCH v2 08/15] drm/vkms: Add a validation function for VKMS configuration José Expósito
2025-02-13 13:59   ` Louis Chauvet
2025-02-14 15:53     ` José Expósito
2025-02-14 18:20       ` Louis Chauvet
2025-02-11 11:09 ` [PATCH v2 09/15] drm/vkms: Allow to configure multiple planes José Expósito
2025-02-13 13:59   ` Louis Chauvet
2025-02-11 11:09 ` [PATCH v2 10/15] drm/vkms: Allow to configure multiple CRTCs José Expósito
2025-02-13 13:59   ` Louis Chauvet
2025-02-11 11:09 ` [PATCH v2 11/15] drm/vkms: Allow to attach planes and CRTCs José Expósito
2025-02-11 11:09 ` [PATCH v2 12/15] drm/vkms: Allow to configure multiple encoders José Expósito
2025-02-13 13:59   ` Louis Chauvet
2025-02-11 11:09 ` [PATCH v2 13/15] drm/vkms: Allow to attach encoders and CRTCs José Expósito
2025-02-13 13:59   ` Louis Chauvet
2025-02-11 11:09 ` [PATCH v2 14/15] drm/vkms: Allow to configure multiple connectors José Expósito
2025-02-13 13:59   ` Louis Chauvet
2025-02-11 11:09 ` [PATCH v2 15/15] drm/vkms: Allow to attach connectors and encoders José Expósito
2025-02-13 13:59   ` Louis Chauvet

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.