* [PATCH 00/13] drm/vkms: Allow to configure device
@ 2025-01-29 11:00 José Expósito
2025-01-29 11:00 ` [PATCH 01/13] drm/vkms: Extract vkms_connector header José Expósito
` (13 more replies)
0 siblings, 14 replies; 41+ messages in thread
From: José Expósito @ 2025-01-29 11:00 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'm still polishing the ConfigFS code [3] and its IGT tests [4] (connector
hot-add/remove bugs) but 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
José Expósito (12):
drm/vkms: Extract vkms_connector header
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
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 | 782 +++++++++++++++++
drivers/gpu/drm/vkms/vkms_config.c | 784 ++++++++++++++++++
drivers/gpu/drm/vkms/vkms_config.h | 479 +++++++++++
drivers/gpu/drm/vkms/vkms_connector.c | 61 ++
drivers/gpu/drm/vkms/vkms_connector.h | 26 +
drivers/gpu/drm/vkms/vkms_drv.c | 45 +-
drivers/gpu/drm/vkms/vkms_drv.h | 17 +-
drivers/gpu/drm/vkms/vkms_output.c | 255 ++++--
12 files changed, 2337 insertions(+), 139 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] 41+ messages in thread
* [PATCH 01/13] drm/vkms: Extract vkms_connector header
2025-01-29 11:00 [PATCH 00/13] drm/vkms: Allow to configure device José Expósito
@ 2025-01-29 11:00 ` José Expósito
2025-01-30 13:48 ` Louis Chauvet
2025-01-29 11:00 ` [PATCH 02/13] drm/vkms: Add KUnit test scaffolding José Expósito
` (12 subsequent siblings)
13 siblings, 1 reply; 41+ messages in thread
From: José Expósito @ 2025-01-29 11:00 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.
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 | 26 ++++++++++++++
drivers/gpu/drm/vkms/vkms_output.c | 45 ++++--------------------
4 files changed, 84 insertions(+), 40 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..ab8b52a84151
--- /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 vkms_connector *vkms_connector_init(struct vkms_device *vkmsdev)
+{
+ struct drm_device *dev = &vkmsdev->drm;
+ 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->base, &vkms_connector_funcs,
+ DRM_MODE_CONNECTOR_VIRTUAL, NULL);
+ if (ret)
+ return ERR_PTR(ret);
+
+ 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
new file mode 100644
index 000000000000..c9149c1b7af0
--- /dev/null
+++ b/drivers/gpu/drm/vkms/vkms_connector.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef _VKMS_CONNECTOR_H_
+#define _VKMS_CONNECTOR_H_
+
+#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
+ *
+ * Returns:
+ * The connector or an error on failure.
+ */
+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 22f0d678af3a..4b5abe159add 100644
--- a/drivers/gpu/drm/vkms/vkms_output.c
+++ b/drivers/gpu/drm/vkms/vkms_output.c
@@ -1,37 +1,13 @@
// 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)
{
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;
@@ -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");
@@ -102,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] 41+ messages in thread
* [PATCH 02/13] drm/vkms: Add KUnit test scaffolding
2025-01-29 11:00 [PATCH 00/13] drm/vkms: Allow to configure device José Expósito
2025-01-29 11:00 ` [PATCH 01/13] drm/vkms: Extract vkms_connector header José Expósito
@ 2025-01-29 11:00 ` José Expósito
2025-01-30 13:48 ` Louis Chauvet
2025-01-29 11:00 ` [PATCH 03/13] drm/vkms: Extract vkms_config header José Expósito
` (11 subsequent siblings)
13 siblings, 1 reply; 41+ messages in thread
From: José Expósito @ 2025-01-29 11:00 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
Signed-off-by: Arthur Grillo <arthurgrillo@riseup.net>
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 | 21 +++++++++++++++++++
5 files changed, 44 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..ba93d864d57f
--- /dev/null
+++ b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <kunit/test.h>
+
+#include "../vkms_config.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] 41+ messages in thread
* [PATCH 03/13] drm/vkms: Extract vkms_config header
2025-01-29 11:00 [PATCH 00/13] drm/vkms: Allow to configure device José Expósito
2025-01-29 11:00 ` [PATCH 01/13] drm/vkms: Extract vkms_connector header José Expósito
2025-01-29 11:00 ` [PATCH 02/13] drm/vkms: Add KUnit test scaffolding José Expósito
@ 2025-01-29 11:00 ` José Expósito
2025-01-30 13:48 ` Louis Chauvet
2025-01-29 11:00 ` [PATCH 04/13] drm/vkms: Move default_config creation to its own function José Expósito
` (10 subsequent siblings)
13 siblings, 1 reply; 41+ messages in thread
From: José Expósito @ 2025-01-29 11:00 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.
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 | 11 +++++
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, 116 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 ba93d864d57f..a7060504f3dc 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_config_test.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
@@ -6,7 +6,18 @@
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 7c142bfc3bd9..b19f76f902b3 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);
default_config = config;
@@ -243,7 +223,7 @@ static int __init vkms_init(void)
ret = vkms_create(config);
if (ret)
- kfree(config);
+ vkms_config_destroy(config);
return ret;
}
@@ -272,7 +252,7 @@ static void __exit vkms_exit(void)
if (default_config->dev)
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] 41+ messages in thread
* [PATCH 04/13] drm/vkms: Move default_config creation to its own function
2025-01-29 11:00 [PATCH 00/13] drm/vkms: Allow to configure device José Expósito
` (2 preceding siblings ...)
2025-01-29 11:00 ` [PATCH 03/13] drm/vkms: Extract vkms_config header José Expósito
@ 2025-01-29 11:00 ` José Expósito
2025-01-30 13:48 ` Louis Chauvet
2025-01-29 11:00 ` [PATCH 05/13] drm/vkms: Set device name from vkms_config José Expósito
` (9 subsequent siblings)
13 siblings, 1 reply; 41+ messages in thread
From: José Expósito @ 2025-01-29 11:00 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.
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 b19f76f902b3..700cf43389b7 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.c
+++ b/drivers/gpu/drm/vkms/vkms_drv.c
@@ -211,16 +211,12 @@ 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);
default_config = 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] 41+ messages in thread
* [PATCH 05/13] drm/vkms: Set device name from vkms_config
2025-01-29 11:00 [PATCH 00/13] drm/vkms: Allow to configure device José Expósito
` (3 preceding siblings ...)
2025-01-29 11:00 ` [PATCH 04/13] drm/vkms: Move default_config creation to its own function José Expósito
@ 2025-01-29 11:00 ` José Expósito
2025-01-30 13:48 ` Louis Chauvet
2025-01-29 11:00 ` [PATCH 06/13] drm/vkms: Add a validation function for VKMS configuration José Expósito
` (8 subsequent siblings)
13 siblings, 1 reply; 41+ messages in thread
From: José Expósito @ 2025-01-29 11:00 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.
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 | 15 ++++++++++++++-
drivers/gpu/drm/vkms/vkms_drv.c | 4 +++-
drivers/gpu/drm/vkms/vkms_drv.h | 2 ++
5 files changed, 37 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..526800b1afb4 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 live times */
+ 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..3e4f39cbe2e2 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,16 @@ 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
+ */
+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 700cf43389b7..f23ee1a053e9 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] 41+ messages in thread
* [PATCH 06/13] drm/vkms: Add a validation function for VKMS configuration
2025-01-29 11:00 [PATCH 00/13] drm/vkms: Allow to configure device José Expósito
` (4 preceding siblings ...)
2025-01-29 11:00 ` [PATCH 05/13] drm/vkms: Set device name from vkms_config José Expósito
@ 2025-01-29 11:00 ` José Expósito
2025-01-30 13:48 ` Louis Chauvet
2025-01-29 11:00 ` [PATCH 07/13] drm/vkms: Allow to configure multiple planes José Expósito
` (7 subsequent siblings)
13 siblings, 1 reply; 41+ messages in thread
From: José Expósito @ 2025-01-29 11:00 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.
Signed-off-by: José Expósito <jose.exposito89@gmail.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.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 ++++++++++
3 files changed, 17 insertions(+)
diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
index 526800b1afb4..66c2c6cb4a0e 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 3e4f39cbe2e2..963f22b9b4bd 100644
--- a/drivers/gpu/drm/vkms/vkms_config.h
+++ b/drivers/gpu/drm/vkms/vkms_config.h
@@ -64,6 +64,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
--
2.48.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH 07/13] drm/vkms: Allow to configure multiple planes
2025-01-29 11:00 [PATCH 00/13] drm/vkms: Allow to configure device José Expósito
` (5 preceding siblings ...)
2025-01-29 11:00 ` [PATCH 06/13] drm/vkms: Add a validation function for VKMS configuration José Expósito
@ 2025-01-29 11:00 ` José Expósito
2025-01-30 13:48 ` Louis Chauvet
2025-01-29 11:00 ` [PATCH 08/13] drm/vkms: Allow to configure multiple CRTCs José Expósito
` (6 subsequent siblings)
13 siblings, 1 reply; 41+ messages in thread
From: José Expósito @ 2025-01-29 11:00 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.
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 | 133 +++++++++++++++-
drivers/gpu/drm/vkms/vkms_config.c | 149 +++++++++++++++++-
drivers/gpu/drm/vkms/vkms_config.h | 78 ++++++++-
drivers/gpu/drm/vkms/vkms_output.c | 67 ++++----
4 files changed, 386 insertions(+), 41 deletions(-)
diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
index 66c2c6cb4a0e..7d9cb3956741 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,138 @@ 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_cfg1, *plane_cfg2;
+ struct vkms_config_plane **array;
+ size_t length;
+
+ config = vkms_config_create("test");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
+
+ array = vkms_config_get_planes(config, &length);
+ KUNIT_ASSERT_EQ(test, length, 0);
+ KUNIT_ASSERT_NULL(test, array);
+
+ plane_cfg1 = vkms_config_add_plane(config);
+ array = vkms_config_get_planes(config, &length);
+ KUNIT_ASSERT_EQ(test, length, 1);
+ KUNIT_ASSERT_PTR_EQ(test, array[0], plane_cfg1);
+ kfree(array);
+
+ plane_cfg2 = vkms_config_add_plane(config);
+ array = vkms_config_get_planes(config, &length);
+ KUNIT_ASSERT_EQ(test, length, 2);
+ KUNIT_ASSERT_PTR_EQ(test, array[0], plane_cfg1);
+ KUNIT_ASSERT_PTR_EQ(test, array[1], plane_cfg2);
+ kfree(array);
+
+ vkms_config_destroy_plane(plane_cfg1);
+ array = vkms_config_get_planes(config, &length);
+ KUNIT_ASSERT_EQ(test, length, 1);
+ KUNIT_ASSERT_PTR_EQ(test, array[0], plane_cfg2);
+ kfree(array);
+
+ vkms_config_destroy(config);
+}
+
+static void vkms_config_test_valid_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_add_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_add_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_add_plane(config);
+ vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_PRIMARY);
+ plane_cfg = vkms_config_add_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_add_plane(config);
+ vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_CURSOR);
+ plane_cfg = vkms_config_add_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 +189,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_valid_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..a5a77612645f 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,139 @@ 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_add_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_add_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_add_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);
}
+struct vkms_config_plane **vkms_config_get_planes(const struct vkms_config *config,
+ size_t *out_length)
+{
+ struct vkms_config_plane **array;
+ struct vkms_config_plane *plane_cfg;
+ size_t length;
+ int n = 0;
+
+ length = list_count_nodes((struct list_head *)&config->planes);
+ if (length == 0) {
+ *out_length = length;
+ return NULL;
+ }
+
+ array = kmalloc_array(length, sizeof(*array), GFP_KERNEL);
+ if (!array)
+ return ERR_PTR(-ENOMEM);
+
+ list_for_each_entry(plane_cfg, &config->planes, link) {
+ array[n] = plane_cfg;
+ n++;
+ }
+
+ *out_length = length;
+ return array;
+}
+
+static bool valid_plane_number(struct vkms_config *config)
+{
+ size_t n_planes;
+
+ n_planes = list_count_nodes(&config->planes);
+ if (n_planes <= 0 || n_planes >= 32) {
+ pr_err("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 vkms_config_plane *plane_cfg;
+ bool has_primary_plane = false;
+ bool has_cursor_plane = false;
+
+ list_for_each_entry(plane_cfg, &config->planes, link) {
+ enum drm_plane_type type;
+
+ type = vkms_config_plane_get_type(plane_cfg);
+
+ if (type == DRM_PLANE_TYPE_PRIMARY) {
+ if (has_primary_plane) {
+ pr_err("Multiple primary planes\n");
+ return false;
+ }
+
+ has_primary_plane = true;
+ } else if (type == DRM_PLANE_TYPE_CURSOR) {
+ if (has_cursor_plane) {
+ pr_err("Multiple cursor planes\n");
+ return false;
+ }
+
+ has_cursor_plane = true;
+ }
+ }
+
+ if (!has_primary_plane) {
+ pr_err("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 +173,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);
+
+ list_for_each_entry(plane_cfg, &vkmsdev->config->planes, link) {
+ seq_puts(m, "plane:\n");
+ seq_printf(m, "\ttype=%d\n",
+ vkms_config_plane_get_type(plane_cfg));
+ }
return 0;
}
@@ -77,3 +197,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_add_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 963f22b9b4bd..d9aeba1762d1 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,36 @@
*
* @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_create() - Create a new VKMS configuration
* @dev_name: Name of the device
@@ -64,6 +83,19 @@ vkms_config_get_device_name(struct vkms_config *config)
return config->dev_name;
}
+/**
+ * vkms_config_planes() - Return the array of planes of the device
+ * @config: Configuration to get the planes from
+ * @out_length: Length of the returned array
+ *
+ * Returns:
+ * A list of pointers to the configurations. On success, the caller is
+ * responsible to free the returned array, but not its contents. On error,
+ * it returns an error and @out_length is invalid.
+ */
+struct vkms_config_plane **vkms_config_get_planes(const struct vkms_config *config,
+ size_t *out_length);
+
/**
* vkms_config_is_valid() - Validate a configuration
* @config: Configuration to validate
@@ -81,4 +113,42 @@ bool vkms_config_is_valid(struct vkms_config *config);
*/
void vkms_config_register_debugfs(struct vkms_device *vkms_device);
+/**
+ * vkms_config_add_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_add_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 068a7f87ecec..b2ae269e5827 100644
--- a/drivers/gpu/drm/vkms/vkms_output.c
+++ b/drivers/gpu/drm/vkms/vkms_output.c
@@ -11,61 +11,63 @@ 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;
- int ret;
+ struct vkms_plane *primary = NULL, *cursor = NULL;
+ struct vkms_config_plane **plane_cfgs = NULL;
+ size_t n_planes;
+ int ret = 0;
int writeback;
unsigned int n;
- /*
- * 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);
+ plane_cfgs = vkms_config_get_planes(vkmsdev->config, &n_planes);
+ if (IS_ERR(plane_cfgs))
+ return PTR_ERR(plane_cfgs);
- if (vkmsdev->config->cursor) {
- cursor = vkms_plane_init(vkmsdev, DRM_PLANE_TYPE_CURSOR);
- if (IS_ERR(cursor))
- return PTR_ERR(cursor);
+ for (n = 0; n < n_planes; n++) {
+ struct vkms_config_plane *plane_cfg;
+ enum drm_plane_type type;
+
+ plane_cfg = plane_cfgs[n];
+ 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");
+ ret = PTR_ERR(plane_cfg->plane);
+ goto err_free;
+ }
+
+ 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,
cursor ? &cursor->base : NULL);
if (IS_ERR(output)) {
DRM_ERROR("Failed to allocate CRTC\n");
- 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);
- }
+ ret = PTR_ERR(output);
+ goto err_free;
}
connector = vkms_connector_init(vkmsdev);
if (IS_ERR(connector)) {
DRM_ERROR("Failed to init connector\n");
- return PTR_ERR(connector);
+ ret = PTR_ERR(connector);
+ goto err_free;
}
encoder = drmm_kzalloc(dev, sizeof(*encoder), GFP_KERNEL);
if (!encoder) {
DRM_ERROR("Failed to allocate encoder\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err_free;
}
ret = drmm_encoder_init(dev, encoder, NULL,
DRM_MODE_ENCODER_VIRTUAL, NULL);
if (ret) {
DRM_ERROR("Failed to init encoder\n");
- return ret;
+ goto err_free;
}
encoder->possible_crtcs = drm_crtc_mask(&output->crtc);
@@ -73,7 +75,7 @@ int vkms_output_init(struct vkms_device *vkmsdev)
ret = drm_connector_attach_encoder(&connector->base, encoder);
if (ret) {
DRM_ERROR("Failed to attach connector to encoder\n");
- return ret;
+ goto err_free;
}
/* Initialize the writeback component */
@@ -85,5 +87,8 @@ int vkms_output_init(struct vkms_device *vkmsdev)
drm_mode_config_reset(dev);
+err_free:
+ kfree(plane_cfgs);
+
return ret;
}
--
2.48.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH 08/13] drm/vkms: Allow to configure multiple CRTCs
2025-01-29 11:00 [PATCH 00/13] drm/vkms: Allow to configure device José Expósito
` (6 preceding siblings ...)
2025-01-29 11:00 ` [PATCH 07/13] drm/vkms: Allow to configure multiple planes José Expósito
@ 2025-01-29 11:00 ` José Expósito
2025-01-30 13:48 ` Louis Chauvet
2025-01-29 11:00 ` [PATCH 09/13] drm/vkms: Allow to attach planes and CRTCs José Expósito
` (5 subsequent siblings)
13 siblings, 1 reply; 41+ messages in thread
From: José Expósito @ 2025-01-29 11:00 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.
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 | 72 +++++++++++++++-
drivers/gpu/drm/vkms/vkms_config.c | 86 ++++++++++++++++++-
drivers/gpu/drm/vkms/vkms_config.h | 83 ++++++++++++++++++
drivers/gpu/drm/vkms/vkms_drv.c | 3 +-
4 files changed, 238 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
index 7d9cb3956741..b5907b060a5c 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);
@@ -121,6 +128,42 @@ 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_cfg1, *crtc_cfg2;
+ struct vkms_config_crtc **array;
+ size_t length;
+
+ config = vkms_config_create("test");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
+
+ array = vkms_config_get_crtcs(config, &length);
+ KUNIT_ASSERT_EQ(test, length, 0);
+ KUNIT_ASSERT_NULL(test, array);
+
+ crtc_cfg1 = vkms_config_add_crtc(config);
+ array = vkms_config_get_crtcs(config, &length);
+ KUNIT_ASSERT_EQ(test, length, 1);
+ KUNIT_ASSERT_PTR_EQ(test, array[0], crtc_cfg1);
+ kfree(array);
+
+ crtc_cfg2 = vkms_config_add_crtc(config);
+ array = vkms_config_get_crtcs(config, &length);
+ KUNIT_ASSERT_EQ(test, length, 2);
+ KUNIT_ASSERT_PTR_EQ(test, array[0], crtc_cfg1);
+ KUNIT_ASSERT_PTR_EQ(test, array[1], crtc_cfg2);
+ kfree(array);
+
+ vkms_config_destroy_crtc(config, crtc_cfg2);
+ array = vkms_config_get_crtcs(config, &length);
+ KUNIT_ASSERT_EQ(test, length, 1);
+ KUNIT_ASSERT_PTR_EQ(test, array[0], crtc_cfg1);
+ kfree(array);
+
+ vkms_config_destroy(config);
+}
+
static void vkms_config_test_valid_plane_number(struct kunit *test)
{
struct vkms_config *config;
@@ -185,13 +228,38 @@ static void vkms_config_test_valid_plane_type(struct kunit *test)
vkms_config_destroy(config);
}
+static void vkms_config_test_valid_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_add_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_valid_plane_number),
KUNIT_CASE(vkms_config_test_valid_plane_type),
+ KUNIT_CASE(vkms_config_test_valid_crtc_number),
{}
};
diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
index a5a77612645f..4128892836d7 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_add_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_add_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_add_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);
}
@@ -107,6 +116,33 @@ struct vkms_config_plane **vkms_config_get_planes(const struct vkms_config *conf
return array;
}
+struct vkms_config_crtc **vkms_config_get_crtcs(const struct vkms_config *config,
+ size_t *out_length)
+{
+ struct vkms_config_crtc **array;
+ struct vkms_config_crtc *crtc_cfg;
+ size_t length;
+ int n = 0;
+
+ length = list_count_nodes((struct list_head *)&config->crtcs);
+ if (length == 0) {
+ *out_length = length;
+ return NULL;
+ }
+
+ array = kmalloc_array(length, sizeof(*array), GFP_KERNEL);
+ if (!array)
+ return ERR_PTR(-ENOMEM);
+
+ list_for_each_entry(crtc_cfg, &config->crtcs, link) {
+ array[n] = crtc_cfg;
+ n++;
+ }
+
+ *out_length = length;
+ return array;
+}
+
static bool valid_plane_number(struct vkms_config *config)
{
size_t n_planes;
@@ -156,11 +192,27 @@ static bool valid_plane_type(struct vkms_config *config)
return true;
}
+static bool valid_crtc_number(struct vkms_config *config)
+{
+ size_t n_crtcs;
+
+ n_crtcs = list_count_nodes(&config->crtcs);
+ if (n_crtcs <= 0 || n_crtcs >= 32) {
+ pr_err("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;
@@ -174,10 +226,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);
list_for_each_entry(plane_cfg, &vkmsdev->config->planes, link) {
seq_puts(m, "plane:\n");
@@ -185,6 +237,12 @@ static int vkms_config_show(struct seq_file *m, void *data)
vkms_config_plane_get_type(plane_cfg));
}
+ list_for_each_entry(crtc_cfg, &vkmsdev->config->crtcs, link) {
+ seq_puts(m, "crtc:\n");
+ seq_printf(m, "\twriteback=%d\n",
+ vkms_config_crtc_get_writeback(crtc_cfg));
+ }
+
return 0;
}
@@ -218,3 +276,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_add_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 d9aeba1762d1..a7828fe0c4b2 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_create() - Create a new VKMS configuration
* @dev_name: Name of the device
@@ -96,6 +117,28 @@ vkms_config_get_device_name(struct vkms_config *config)
struct vkms_config_plane **vkms_config_get_planes(const struct vkms_config *config,
size_t *out_length);
+/**
+ * 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_get_crtcs() - Return the array of CRTCs of the device
+ * @config: Configuration to get the CRTCs from
+ * @out_length: Length of the returned array
+ *
+ * Returns:
+ * A list of pointers to the configurations. On success, the caller is
+ * responsible to free the returned array, but not its contents. On error,
+ * it returns an error and @out_length is invalid.
+ */
+struct vkms_config_crtc **vkms_config_get_crtcs(const struct vkms_config *config,
+ size_t *out_length);
+
/**
* vkms_config_is_valid() - Validate a configuration
* @config: Configuration to validate
@@ -151,4 +194,44 @@ vkms_config_plane_set_type(struct vkms_config_plane *plane_cfg,
plane_cfg->type = type;
}
+/**
+ * vkms_config_add_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_add_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 f23ee1a053e9..77f24b802849 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] 41+ messages in thread
* [PATCH 09/13] drm/vkms: Allow to attach planes and CRTCs
2025-01-29 11:00 [PATCH 00/13] drm/vkms: Allow to configure device José Expósito
` (7 preceding siblings ...)
2025-01-29 11:00 ` [PATCH 08/13] drm/vkms: Allow to configure multiple CRTCs José Expósito
@ 2025-01-29 11:00 ` José Expósito
2025-01-30 13:48 ` Louis Chauvet
2025-01-29 11:00 ` [PATCH 10/13] drm/vkms: Allow to configure multiple encoders José Expósito
` (4 subsequent siblings)
13 siblings, 1 reply; 41+ messages in thread
From: José Expósito @ 2025-01-29 11:00 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.
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 | 185 ++++++++++++++++++
drivers/gpu/drm/vkms/vkms_config.c | 166 ++++++++++++++--
drivers/gpu/drm/vkms/vkms_config.h | 55 +++++-
drivers/gpu/drm/vkms/vkms_output.c | 77 ++++++--
4 files changed, 446 insertions(+), 37 deletions(-)
diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
index b5907b060a5c..5e698616491a 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);
@@ -191,6 +203,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);
@@ -198,16 +212,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_add_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_add_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_add_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 */
@@ -217,14 +241,50 @@ static void vkms_config_test_valid_plane_type(struct kunit *test)
/* Invalid: Multiple cursor planes */
plane_cfg = vkms_config_add_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_add_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_add_crtc(config);
+ KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
+
+ /* Valid: Second CRTC with a primary plane */
+ plane_cfg = vkms_config_add_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);
}
@@ -251,6 +311,128 @@ static void vkms_config_test_valid_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_add_plane(config);
+ vkms_config_plane_set_type(overlay_cfg, DRM_PLANE_TYPE_OVERLAY);
+ primary_cfg = vkms_config_add_plane(config);
+ vkms_config_plane_set_type(primary_cfg, DRM_PLANE_TYPE_PRIMARY);
+ cursor_cfg = vkms_config_add_plane(config);
+ vkms_config_plane_set_type(cursor_cfg, DRM_PLANE_TYPE_CURSOR);
+
+ crtc_cfg = vkms_config_add_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 **array;
+ size_t length;
+ int err;
+
+ config = vkms_config_create("test");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
+
+ plane_cfg1 = vkms_config_add_plane(config);
+ plane_cfg2 = vkms_config_add_plane(config);
+ crtc_cfg1 = vkms_config_add_crtc(config);
+ crtc_cfg2 = vkms_config_add_crtc(config);
+
+ /* No possible CRTCs */
+ array = vkms_config_plane_get_possible_crtcs(plane_cfg1, &length);
+ KUNIT_ASSERT_EQ(test, length, 0);
+ KUNIT_ASSERT_NULL(test, array);
+
+ array = vkms_config_plane_get_possible_crtcs(plane_cfg2, &length);
+ KUNIT_ASSERT_EQ(test, length, 0);
+ KUNIT_ASSERT_NULL(test, array);
+
+ /* 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);
+
+ array = vkms_config_plane_get_possible_crtcs(plane_cfg1, &length);
+ KUNIT_ASSERT_EQ(test, length, 2);
+ KUNIT_ASSERT_PTR_EQ(test, array[0], crtc_cfg1);
+ KUNIT_ASSERT_PTR_EQ(test, array[1], crtc_cfg2);
+ kfree(array);
+
+ array = vkms_config_plane_get_possible_crtcs(plane_cfg2, &length);
+ KUNIT_ASSERT_EQ(test, length, 0);
+ KUNIT_ASSERT_NULL(test, array);
+
+ /* Plane 1 attached to CRTC 1 and plane 2 to CRTC 2 */
+ vkms_config_plane_detach_crtc(plane_cfg1, crtc_cfg2);
+
+ array = vkms_config_plane_get_possible_crtcs(plane_cfg1, &length);
+ KUNIT_ASSERT_EQ(test, length, 1);
+ KUNIT_ASSERT_PTR_EQ(test, array[0], crtc_cfg1);
+ kfree(array);
+
+ err = vkms_config_plane_attach_crtc(plane_cfg2, crtc_cfg2);
+ KUNIT_EXPECT_EQ(test, err, 0);
+
+ array = vkms_config_plane_get_possible_crtcs(plane_cfg2, &length);
+ KUNIT_ASSERT_EQ(test, length, 1);
+ KUNIT_ASSERT_PTR_EQ(test, array[0], crtc_cfg2);
+ kfree(array);
+
+ 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,
@@ -259,7 +441,10 @@ static struct kunit_case vkms_config_test_cases[] = {
KUNIT_CASE(vkms_config_test_get_crtcs),
KUNIT_CASE(vkms_config_test_valid_plane_number),
KUNIT_CASE(vkms_config_test_valid_plane_type),
+ KUNIT_CASE(vkms_config_test_valid_plane_possible_crtcs),
KUNIT_CASE(vkms_config_test_valid_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 4128892836d7..a2ce4905589b 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_add_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_add_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;
@@ -156,31 +167,39 @@ static bool valid_plane_number(struct vkms_config *config)
return true;
}
-static bool valid_plane_type(struct vkms_config *config)
+static bool valid_plane_type(struct vkms_config *config,
+ struct vkms_config_crtc *crtc_cfg)
{
struct vkms_config_plane *plane_cfg;
bool has_primary_plane = false;
bool has_cursor_plane = false;
list_for_each_entry(plane_cfg, &config->planes, link) {
+ 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) {
- pr_err("Multiple primary planes\n");
- return false;
- }
+ xa_for_each(&plane_cfg->possible_crtcs, idx, possible_crtc) {
+ if (possible_crtc != crtc_cfg)
+ continue;
- has_primary_plane = true;
- } else if (type == DRM_PLANE_TYPE_CURSOR) {
- if (has_cursor_plane) {
- pr_err("Multiple cursor planes\n");
- return false;
- }
+ if (type == DRM_PLANE_TYPE_PRIMARY) {
+ if (has_primary_plane) {
+ pr_err("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) {
+ pr_err("Multiple cursor planes\n");
+ return false;
+ }
+
+ has_cursor_plane = true;
+ }
}
}
@@ -192,6 +211,20 @@ static bool valid_plane_type(struct vkms_config *config)
return true;
}
+static bool valid_plane_possible_crtcs(struct vkms_config *config)
+{
+ struct vkms_config_plane *plane_cfg;
+
+ list_for_each_entry(plane_cfg, &config->planes, link) {
+ if (xa_empty(&plane_cfg->possible_crtcs)) {
+ pr_err("All planes must have at least one possible CRTC\n");
+ return false;
+ }
+ }
+
+ return true;
+}
+
static bool valid_crtc_number(struct vkms_config *config)
{
size_t n_crtcs;
@@ -207,15 +240,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;
+ list_for_each_entry(crtc_cfg, &config->crtcs, link) {
+ if (!valid_plane_type(config, crtc_cfg))
+ return false;
+ }
+
return true;
}
@@ -265,6 +305,7 @@ struct vkms_config_plane *vkms_config_add_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);
@@ -273,10 +314,69 @@ struct vkms_config_plane *vkms_config_add_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;
+
+ xa_for_each(&plane_cfg->possible_crtcs, 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;
+
+ xa_for_each(&plane_cfg->possible_crtcs, idx, possible_crtc) {
+ if (possible_crtc == crtc_cfg)
+ xa_erase(&plane_cfg->possible_crtcs, idx);
+ }
+}
+
+struct vkms_config_crtc **vkms_config_plane_get_possible_crtcs(struct vkms_config_plane *plane_cfg,
+ size_t *out_length)
+{
+ struct vkms_config_crtc **array;
+ struct vkms_config_crtc *possible_crtc;
+ unsigned long idx;
+ size_t length = 0;
+ int n = 0;
+
+ xa_for_each(&plane_cfg->possible_crtcs, idx, possible_crtc)
+ length++;
+
+ if (length == 0) {
+ *out_length = length;
+ return NULL;
+ }
+
+ array = kmalloc_array(length, sizeof(*array), GFP_KERNEL);
+ if (!array)
+ return ERR_PTR(-ENOMEM);
+
+ xa_for_each(&plane_cfg->possible_crtcs, idx, possible_crtc) {
+ array[n] = possible_crtc;
+ n++;
+ }
+
+ *out_length = length;
+ return array;
+}
+
struct vkms_config_crtc *vkms_config_add_crtc(struct vkms_config *config)
{
struct vkms_config_crtc *crtc_cfg;
@@ -295,6 +395,44 @@ struct vkms_config_crtc *vkms_config_add_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;
+
+ list_for_each_entry(plane_cfg, &config->planes, link)
+ vkms_config_plane_detach_crtc(plane_cfg, crtc_cfg);
+
list_del(&crtc_cfg->link);
kfree(crtc_cfg);
}
+
+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;
+
+ list_for_each_entry(plane_cfg, &config->planes, link) {
+ current_type = vkms_config_plane_get_type(plane_cfg);
+
+ xa_for_each(&plane_cfg->possible_crtcs, 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 a7828fe0c4b2..1f2e6c485d08 100644
--- a/drivers/gpu/drm/vkms/vkms_config.h
+++ b/drivers/gpu/drm/vkms/vkms_config.h
@@ -12,14 +12,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 +29,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 +39,7 @@ struct vkms_config_plane {
struct list_head link;
enum drm_plane_type type;
+ struct xarray possible_crtcs;
/* Internal usage */
struct vkms_plane *plane;
@@ -194,6 +194,35 @@ 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_plane_get_possible_crtcs() - Return the array of possible CRTCs
+ * @plane_cfg: Plane to get the possible CRTCs from
+ * @out_length: Length of the returned array
+ *
+ * Returns:
+ * A list of pointers to the configurations. On success, the caller is
+ * responsible to free the returned array, but not its contents. On error,
+ * it returns an error and @out_length is invalid.
+ */
+struct vkms_config_crtc **vkms_config_plane_get_possible_crtcs(struct vkms_config_plane *plane_cfg,
+ size_t *out_length);
+
/**
* vkms_config_add_crtc() - Add a new CRTC configuration
* @config: Configuration to add the CRTC to
@@ -234,4 +263,26 @@ 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
+ *
+ * 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
+ *
+ * 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 b2ae269e5827..427d0aad8901 100644
--- a/drivers/gpu/drm/vkms/vkms_output.c
+++ b/drivers/gpu/drm/vkms/vkms_output.c
@@ -10,18 +10,24 @@ 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_cfgs = NULL;
size_t n_planes;
+ struct vkms_config_crtc **crtc_cfgs = NULL;
+ size_t n_crtcs;
int ret = 0;
int writeback;
- unsigned int n;
+ unsigned int n, i;
plane_cfgs = vkms_config_get_planes(vkmsdev->config, &n_planes);
if (IS_ERR(plane_cfgs))
return PTR_ERR(plane_cfgs);
+ crtc_cfgs = vkms_config_get_crtcs(vkmsdev->config, &n_crtcs);
+ if (IS_ERR(crtc_cfgs)) {
+ ret = PTR_ERR(crtc_cfgs);
+ goto err_free;
+ }
+
for (n = 0; n < n_planes; n++) {
struct vkms_config_plane *plane_cfg;
enum drm_plane_type type;
@@ -35,19 +41,54 @@ int vkms_output_init(struct vkms_device *vkmsdev)
ret = PTR_ERR(plane_cfg->plane);
goto err_free;
}
+ }
+
+ for (n = 0; n < n_crtcs; n++) {
+ struct vkms_config_crtc *crtc_cfg;
+ struct vkms_config_plane *primary, *cursor;
+
+ crtc_cfg = crtc_cfgs[n];
+ primary = vkms_config_crtc_primary_plane(vkmsdev->config, crtc_cfg);
+ cursor = vkms_config_crtc_cursor_plane(vkmsdev->config, crtc_cfg);
+
+ 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");
+ ret = PTR_ERR(crtc_cfg->crtc);
+ goto err_free;
+ }
- if (type == DRM_PLANE_TYPE_PRIMARY)
- primary = plane_cfg->plane;
- else if (type == DRM_PLANE_TYPE_CURSOR)
- cursor = plane_cfg->plane;
+ /* 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");
- ret = PTR_ERR(output);
- goto err_free;
+ for (n = 0; n < n_planes; n++) {
+ struct vkms_config_plane *plane_cfg;
+ struct vkms_config_crtc **possible_crtcs;
+ size_t n_possible_crtcs;
+
+ plane_cfg = plane_cfgs[n];
+ possible_crtcs = vkms_config_plane_get_possible_crtcs(plane_cfg,
+ &n_possible_crtcs);
+ if (IS_ERR(possible_crtcs)) {
+ ret = PTR_ERR(possible_crtcs);
+ goto err_free;
+ }
+
+ for (i = 0; i < n_possible_crtcs; i++) {
+ struct vkms_config_crtc *possible_crtc;
+
+ possible_crtc = possible_crtcs[i];
+ plane_cfg->plane->base.possible_crtcs |=
+ drm_crtc_mask(&possible_crtc->crtc->crtc);
+ }
+
+ kfree(possible_crtcs);
}
connector = vkms_connector_init(vkmsdev);
@@ -69,7 +110,7 @@ int vkms_output_init(struct vkms_device *vkmsdev)
DRM_ERROR("Failed to init encoder\n");
goto err_free;
}
- encoder->possible_crtcs = drm_crtc_mask(&output->crtc);
+ encoder->possible_crtcs = drm_crtc_mask(&crtc_cfgs[0]->crtc->crtc);
/* Attach the encoder and the connector */
ret = drm_connector_attach_encoder(&connector->base, encoder);
@@ -78,17 +119,11 @@ int vkms_output_init(struct vkms_device *vkmsdev)
goto err_free;
}
- /* 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);
err_free:
kfree(plane_cfgs);
+ kfree(crtc_cfgs);
return ret;
}
--
2.48.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH 10/13] drm/vkms: Allow to configure multiple encoders
2025-01-29 11:00 [PATCH 00/13] drm/vkms: Allow to configure device José Expósito
` (8 preceding siblings ...)
2025-01-29 11:00 ` [PATCH 09/13] drm/vkms: Allow to attach planes and CRTCs José Expósito
@ 2025-01-29 11:00 ` José Expósito
2025-01-30 13:48 ` Louis Chauvet
2025-01-29 11:00 ` [PATCH 11/13] drm/vkms: Allow to attach encoders and CRTCs José Expósito
` (3 subsequent siblings)
13 siblings, 1 reply; 41+ messages in thread
From: José Expósito @ 2025-01-29 11:00 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.
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 | 65 ++++++++++++++++
drivers/gpu/drm/vkms/vkms_config.c | 78 +++++++++++++++++++
drivers/gpu/drm/vkms/vkms_config.h | 49 ++++++++++++
3 files changed, 192 insertions(+)
diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
index 5e698616491a..6c09363d9a96 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);
@@ -176,6 +180,42 @@ 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_cfg1, *encoder_cfg2;
+ struct vkms_config_encoder **array;
+ size_t length;
+
+ config = vkms_config_create("test");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
+
+ array = vkms_config_get_encoders(config, &length);
+ KUNIT_ASSERT_EQ(test, length, 0);
+ KUNIT_ASSERT_NULL(test, array);
+
+ encoder_cfg1 = vkms_config_add_encoder(config);
+ array = vkms_config_get_encoders(config, &length);
+ KUNIT_ASSERT_EQ(test, length, 1);
+ KUNIT_ASSERT_PTR_EQ(test, array[0], encoder_cfg1);
+ kfree(array);
+
+ encoder_cfg2 = vkms_config_add_encoder(config);
+ array = vkms_config_get_encoders(config, &length);
+ KUNIT_ASSERT_EQ(test, length, 2);
+ KUNIT_ASSERT_PTR_EQ(test, array[0], encoder_cfg1);
+ KUNIT_ASSERT_PTR_EQ(test, array[1], encoder_cfg2);
+ kfree(array);
+
+ vkms_config_destroy_encoder(config, encoder_cfg2);
+ array = vkms_config_get_encoders(config, &length);
+ KUNIT_ASSERT_EQ(test, length, 1);
+ KUNIT_ASSERT_PTR_EQ(test, array[0], encoder_cfg1);
+ kfree(array);
+
+ vkms_config_destroy(config);
+}
+
static void vkms_config_test_valid_plane_number(struct kunit *test)
{
struct vkms_config *config;
@@ -311,6 +351,29 @@ static void vkms_config_test_valid_crtc_number(struct kunit *test)
vkms_config_destroy(config);
}
+static void vkms_config_test_valid_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_add_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;
@@ -439,10 +502,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_valid_plane_number),
KUNIT_CASE(vkms_config_test_valid_plane_type),
KUNIT_CASE(vkms_config_test_valid_plane_possible_crtcs),
KUNIT_CASE(vkms_config_test_valid_crtc_number),
+ KUNIT_CASE(vkms_config_test_valid_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 a2ce4905589b..75e72f9b5972 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_add_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);
}
@@ -154,6 +164,33 @@ struct vkms_config_crtc **vkms_config_get_crtcs(const struct vkms_config *config
return array;
}
+struct vkms_config_encoder **vkms_config_get_encoders(const struct vkms_config *config,
+ size_t *out_length)
+{
+ struct vkms_config_encoder **array;
+ struct vkms_config_encoder *encoder_cfg;
+ size_t length;
+ int n = 0;
+
+ length = list_count_nodes((struct list_head *)&config->encoders);
+ if (length == 0) {
+ *out_length = length;
+ return NULL;
+ }
+
+ array = kmalloc_array(length, sizeof(*array), GFP_KERNEL);
+ if (!array)
+ return ERR_PTR(-ENOMEM);
+
+ list_for_each_entry(encoder_cfg, &config->encoders, link) {
+ array[n] = encoder_cfg;
+ n++;
+ }
+
+ *out_length = length;
+ return array;
+}
+
static bool valid_plane_number(struct vkms_config *config)
{
size_t n_planes;
@@ -238,6 +275,19 @@ static bool valid_crtc_number(struct vkms_config *config)
return true;
}
+static bool valid_encoder_number(struct vkms_config *config)
+{
+ size_t n_encoders;
+
+ n_encoders = list_count_nodes(&config->encoders);
+ if (n_encoders <= 0 || n_encoders >= 32) {
+ pr_err("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;
@@ -248,6 +298,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;
@@ -267,6 +320,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);
@@ -283,6 +337,10 @@ static int vkms_config_show(struct seq_file *m, void *data)
vkms_config_crtc_get_writeback(crtc_cfg));
}
+ list_for_each_entry(encoder_cfg, &vkmsdev->config->encoders, link) {
+ seq_puts(m, "encoder\n");
+ }
+
return 0;
}
@@ -436,3 +494,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_add_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 1f2e6c485d08..40774d0daeac 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
* @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;
};
@@ -64,6 +66,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_create() - Create a new VKMS configuration
* @dev_name: Name of the device
@@ -139,6 +157,19 @@ static inline size_t vkms_config_get_num_crtcs(struct vkms_config *config)
struct vkms_config_crtc **vkms_config_get_crtcs(const struct vkms_config *config,
size_t *out_length);
+/**
+ * vkms_config_get_encoders() - Return the array of encoders of the device
+ * @config: Configuration to get the encoders from
+ * @out_length: Length of the returned array
+ *
+ * Returns:
+ * A list of pointers to the configurations. On success, the caller is
+ * responsible to free the returned array, but not its contents. On error,
+ * it returns an error and @out_length is invalid.
+ */
+struct vkms_config_encoder **vkms_config_get_encoders(const struct vkms_config *config,
+ size_t *out_length);
+
/**
* vkms_config_is_valid() - Validate a configuration
* @config: Configuration to validate
@@ -285,4 +316,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_add_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_add_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] 41+ messages in thread
* [PATCH 11/13] drm/vkms: Allow to attach encoders and CRTCs
2025-01-29 11:00 [PATCH 00/13] drm/vkms: Allow to configure device José Expósito
` (9 preceding siblings ...)
2025-01-29 11:00 ` [PATCH 10/13] drm/vkms: Allow to configure multiple encoders José Expósito
@ 2025-01-29 11:00 ` José Expósito
2025-01-30 13:48 ` Louis Chauvet
2025-01-29 11:00 ` [PATCH 12/13] drm/vkms: Allow to configure multiple connectors José Expósito
` (2 subsequent siblings)
13 siblings, 1 reply; 41+ messages in thread
From: José Expósito @ 2025-01-29 11:00 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.
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 | 112 ++++++++++++++++++
drivers/gpu/drm/vkms/vkms_config.c | 106 +++++++++++++++++
drivers/gpu/drm/vkms/vkms_config.h | 33 ++++++
drivers/gpu/drm/vkms/vkms_output.c | 64 +++++++---
4 files changed, 299 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
index 6c09363d9a96..27d44315c2de 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_config_test.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
@@ -244,6 +244,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);
@@ -297,6 +298,9 @@ static void vkms_config_test_valid_plane_type(struct kunit *test)
/* Invalid: Second CRTC without primary plane */
crtc_cfg = vkms_config_add_crtc(config);
+ encoder_cfg = vkms_config_add_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 */
@@ -374,6 +378,51 @@ static void vkms_config_test_valid_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_add_encoder(config);
+ KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
+
+ /* Valid: Second CRTC with shared encoder */
+ crtc_cfg2 = vkms_config_add_crtc(config);
+
+ plane_cfg = vkms_config_add_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;
@@ -496,6 +545,67 @@ 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 **array;
+ size_t length;
+ int err;
+
+ config = vkms_config_create("test");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
+
+ encoder_cfg1 = vkms_config_add_encoder(config);
+ encoder_cfg2 = vkms_config_add_encoder(config);
+ crtc_cfg1 = vkms_config_add_crtc(config);
+ crtc_cfg2 = vkms_config_add_crtc(config);
+
+ /* No possible CRTCs */
+ array = vkms_config_encoder_get_possible_crtcs(encoder_cfg1, &length);
+ KUNIT_ASSERT_EQ(test, length, 0);
+ KUNIT_ASSERT_NULL(test, array);
+
+ array = vkms_config_encoder_get_possible_crtcs(encoder_cfg2, &length);
+ KUNIT_ASSERT_EQ(test, length, 0);
+ KUNIT_ASSERT_NULL(test, array);
+
+ /* 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);
+
+ array = vkms_config_encoder_get_possible_crtcs(encoder_cfg1, &length);
+ KUNIT_ASSERT_EQ(test, length, 2);
+ KUNIT_ASSERT_PTR_EQ(test, array[0], crtc_cfg1);
+ KUNIT_ASSERT_PTR_EQ(test, array[1], crtc_cfg2);
+ kfree(array);
+
+ array = vkms_config_encoder_get_possible_crtcs(encoder_cfg2, &length);
+ KUNIT_ASSERT_EQ(test, length, 0);
+ KUNIT_ASSERT_NULL(test, array);
+
+ /* Encoder 1 attached to CRTC 1 and encoder 2 to CRTC 2 */
+ vkms_config_encoder_detach_crtc(encoder_cfg1, crtc_cfg2);
+
+ array = vkms_config_encoder_get_possible_crtcs(encoder_cfg1, &length);
+ KUNIT_ASSERT_EQ(test, length, 1);
+ KUNIT_ASSERT_PTR_EQ(test, array[0], crtc_cfg1);
+ kfree(array);
+
+ err = vkms_config_encoder_attach_crtc(encoder_cfg2, crtc_cfg2);
+ KUNIT_EXPECT_EQ(test, err, 0);
+
+ array = vkms_config_encoder_get_possible_crtcs(encoder_cfg2, &length);
+ KUNIT_ASSERT_EQ(test, length, 1);
+ KUNIT_ASSERT_PTR_EQ(test, array[0], crtc_cfg2);
+ kfree(array);
+
+ 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,
@@ -508,8 +618,10 @@ static struct kunit_case vkms_config_test_cases[] = {
KUNIT_CASE(vkms_config_test_valid_plane_possible_crtcs),
KUNIT_CASE(vkms_config_test_valid_crtc_number),
KUNIT_CASE(vkms_config_test_valid_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 75e72f9b5972..437a9980e9a8 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:
@@ -288,6 +291,40 @@ static bool valid_encoder_number(struct vkms_config *config)
return true;
}
+static bool valid_encoder_possible_crtcs(struct vkms_config *config)
+{
+ struct vkms_config_crtc *crtc_cfg;
+ struct vkms_config_encoder *encoder_cfg;
+
+ list_for_each_entry(encoder_cfg, &config->encoders, link) {
+ if (xa_empty(&encoder_cfg->possible_crtcs)) {
+ pr_err("All encoders must have at least one possible CRTC\n");
+ return false;
+ }
+ }
+
+ list_for_each_entry(crtc_cfg, &config->crtcs, link) {
+ bool crtc_has_encoder = false;
+
+ list_for_each_entry(encoder_cfg, &config->encoders, link) {
+ struct vkms_config_crtc *possible_crtc;
+ unsigned long idx = 0;
+
+ xa_for_each(&encoder_cfg->possible_crtcs, idx, possible_crtc) {
+ if (possible_crtc == crtc_cfg)
+ crtc_has_encoder = true;
+ }
+ }
+
+ if (!crtc_has_encoder) {
+ pr_err("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;
@@ -309,6 +346,9 @@ bool vkms_config_is_valid(struct vkms_config *config)
return false;
}
+ if (!valid_encoder_possible_crtcs(config))
+ return false;
+
return true;
}
@@ -454,10 +494,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;
list_for_each_entry(plane_cfg, &config->planes, link)
vkms_config_plane_detach_crtc(plane_cfg, crtc_cfg);
+ list_for_each_entry(encoder_cfg, &config->encoders, link)
+ vkms_config_encoder_detach_crtc(encoder_cfg, crtc_cfg);
+
list_del(&crtc_cfg->link);
kfree(crtc_cfg);
}
@@ -503,6 +547,8 @@ struct vkms_config_encoder *vkms_config_add_encoder(struct vkms_config *config)
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;
@@ -511,6 +557,66 @@ struct vkms_config_encoder *vkms_config_add_encoder(struct vkms_config *config)
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;
+
+ xa_for_each(&encoder_cfg->possible_crtcs, 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;
+
+ xa_for_each(&encoder_cfg->possible_crtcs, idx, possible_crtc) {
+ if (possible_crtc == crtc_cfg)
+ xa_erase(&encoder_cfg->possible_crtcs, idx);
+ }
+}
+
+struct vkms_config_crtc **
+vkms_config_encoder_get_possible_crtcs(struct vkms_config_encoder *encoder_cfg,
+ size_t *out_length)
+{
+ struct vkms_config_crtc **array;
+ struct vkms_config_crtc *possible_crtc;
+ unsigned long idx;
+ size_t length = 0;
+ int n = 0;
+
+ xa_for_each(&encoder_cfg->possible_crtcs, idx, possible_crtc)
+ length++;
+
+ if (length == 0) {
+ *out_length = 0;
+ return NULL;
+ }
+
+ array = kmalloc_array(length, sizeof(*array), GFP_KERNEL);
+ if (!array)
+ return ERR_PTR(-ENOMEM);
+
+ xa_for_each(&encoder_cfg->possible_crtcs, idx, possible_crtc) {
+ array[n] = possible_crtc;
+ n++;
+ }
+
+ *out_length = length;
+ return array;
+}
diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
index 40774d0daeac..5f4a33e113bf 100644
--- a/drivers/gpu/drm/vkms/vkms_config.h
+++ b/drivers/gpu/drm/vkms/vkms_config.h
@@ -70,6 +70,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
@@ -78,6 +79,8 @@ struct vkms_config_crtc {
struct vkms_config_encoder {
struct list_head link;
+ struct xarray possible_crtcs;
+
/* Internal usage */
struct drm_encoder *encoder;
};
@@ -334,4 +337,34 @@ struct vkms_config_encoder *vkms_config_add_encoder(struct vkms_config *config);
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);
+
+/**
+ * vkms_config_encoder_get_possible_crtcs() - Return the array of possible CRTCs
+ * @encoder_cfg: Encoder to get the possible CRTCs from
+ * @out_length: Length of the returned array
+ *
+ * Returns:
+ * A list of pointers to the configurations. On success, the caller is
+ * responsible to free the returned array, but not its contents. On error,
+ * it returns an error and @out_length is invalid.
+ */
+struct vkms_config_crtc **
+vkms_config_encoder_get_possible_crtcs(struct vkms_config_encoder *encoder_cfg,
+ size_t *out_length);
+
#endif /* _VKMS_CONFIG_H_ */
diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
index 427d0aad8901..9c3e00817add 100644
--- a/drivers/gpu/drm/vkms/vkms_output.c
+++ b/drivers/gpu/drm/vkms/vkms_output.c
@@ -9,11 +9,12 @@ 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_cfgs = NULL;
size_t n_planes;
struct vkms_config_crtc **crtc_cfgs = NULL;
size_t n_crtcs;
+ struct vkms_config_encoder **encoder_cfgs = NULL;
+ size_t n_encoders;
int ret = 0;
int writeback;
unsigned int n, i;
@@ -28,6 +29,12 @@ int vkms_output_init(struct vkms_device *vkmsdev)
goto err_free;
}
+ encoder_cfgs = vkms_config_get_encoders(vkmsdev->config, &n_encoders);
+ if (IS_ERR(encoder_cfgs)) {
+ ret = PTR_ERR(encoder_cfgs);
+ goto err_free;
+ }
+
for (n = 0; n < n_planes; n++) {
struct vkms_config_plane *plane_cfg;
enum drm_plane_type type;
@@ -91,6 +98,44 @@ int vkms_output_init(struct vkms_device *vkmsdev)
kfree(possible_crtcs);
}
+ for (n = 0; n < n_encoders; n++) {
+ struct vkms_config_encoder *encoder_cfg;
+ struct vkms_config_crtc **possible_crtcs;
+ size_t n_possible_crtcs;
+
+ encoder_cfg = encoder_cfgs[n];
+
+ encoder_cfg->encoder = drmm_kzalloc(dev, sizeof(*encoder_cfg->encoder), GFP_KERNEL);
+ if (!encoder_cfg->encoder) {
+ DRM_ERROR("Failed to allocate encoder\n");
+ ret = -ENOMEM;
+ goto err_free;
+ }
+ ret = drmm_encoder_init(dev, encoder_cfg->encoder, NULL,
+ DRM_MODE_ENCODER_VIRTUAL, NULL);
+ if (ret) {
+ DRM_ERROR("Failed to init encoder\n");
+ goto err_free;
+ }
+
+ possible_crtcs = vkms_config_encoder_get_possible_crtcs(encoder_cfg,
+ &n_possible_crtcs);
+ if (IS_ERR(possible_crtcs)) {
+ ret = PTR_ERR(possible_crtcs);
+ goto err_free;
+ }
+
+ for (i = 0; i < n_possible_crtcs; i++) {
+ struct vkms_config_crtc *possible_crtc;
+
+ possible_crtc = possible_crtcs[i];
+ encoder_cfg->encoder->possible_crtcs |=
+ drm_crtc_mask(&possible_crtc->crtc->crtc);
+ }
+
+ kfree(possible_crtcs);
+ }
+
connector = vkms_connector_init(vkmsdev);
if (IS_ERR(connector)) {
DRM_ERROR("Failed to init connector\n");
@@ -98,22 +143,8 @@ int vkms_output_init(struct vkms_device *vkmsdev)
goto err_free;
}
- encoder = drmm_kzalloc(dev, sizeof(*encoder), GFP_KERNEL);
- if (!encoder) {
- DRM_ERROR("Failed to allocate encoder\n");
- ret = -ENOMEM;
- goto err_free;
- }
- ret = drmm_encoder_init(dev, encoder, NULL,
- DRM_MODE_ENCODER_VIRTUAL, NULL);
- if (ret) {
- DRM_ERROR("Failed to init encoder\n");
- goto err_free;
- }
- encoder->possible_crtcs = drm_crtc_mask(&crtc_cfgs[0]->crtc->crtc);
-
/* Attach the encoder and the connector */
- ret = drm_connector_attach_encoder(&connector->base, encoder);
+ ret = drm_connector_attach_encoder(&connector->base, encoder_cfgs[0]->encoder);
if (ret) {
DRM_ERROR("Failed to attach connector to encoder\n");
goto err_free;
@@ -124,6 +155,7 @@ int vkms_output_init(struct vkms_device *vkmsdev)
err_free:
kfree(plane_cfgs);
kfree(crtc_cfgs);
+ kfree(encoder_cfgs);
return ret;
}
--
2.48.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH 12/13] drm/vkms: Allow to configure multiple connectors
2025-01-29 11:00 [PATCH 00/13] drm/vkms: Allow to configure device José Expósito
` (10 preceding siblings ...)
2025-01-29 11:00 ` [PATCH 11/13] drm/vkms: Allow to attach encoders and CRTCs José Expósito
@ 2025-01-29 11:00 ` José Expósito
2025-01-30 13:48 ` Louis Chauvet
2025-01-29 11:00 ` [PATCH 13/13] drm/vkms: Allow to attach connectors and encoders José Expósito
2025-01-30 13:48 ` [PATCH 00/13] drm/vkms: Allow to configure device Louis Chauvet
13 siblings, 1 reply; 41+ messages in thread
From: José Expósito @ 2025-01-29 11:00 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.
Unlike planes, CRTCs and encoders, connectors can be added and removed
once the device is created. To reflect it, add an "enabled" flag in the
configuration and filter disabled connectors in the
vkms_config_get_connectors() function.
For the moment, changing the enabled status of the connector has no
effect after the device is created, but a future patch will add this
capability.
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.
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 | 84 +++++++++++++++++
drivers/gpu/drm/vkms/vkms_config.c | 93 +++++++++++++++++++
drivers/gpu/drm/vkms/vkms_config.h | 76 +++++++++++++++
drivers/gpu/drm/vkms/vkms_connector.c | 11 +++
4 files changed, 264 insertions(+)
diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
index 27d44315c2de..40c385eedc1d 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));
@@ -52,6 +53,7 @@ static void vkms_config_test_default_config(struct kunit *test)
struct vkms_config *config;
struct vkms_config_plane *plane_cfg;
struct vkms_config_crtc *crtc_cfg;
+ struct vkms_config_connector *connector_cfg;
int n_primaries = 0;
int n_cursors = 0;
int n_overlays = 0;
@@ -103,6 +105,12 @@ 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);
+ connector_cfg = list_first_entry(&config->connectors,
+ typeof(*connector_cfg), link);
+ KUNIT_EXPECT_TRUE(test, vkms_config_connector_is_enabled(connector_cfg));
+
KUNIT_EXPECT_TRUE(test, vkms_config_is_valid(config));
vkms_config_destroy(config);
@@ -216,6 +224,47 @@ 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_cfg1, *connector_cfg2;
+ struct vkms_config_connector **array;
+ size_t length;
+
+ config = vkms_config_create("test");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
+
+ array = vkms_config_get_connectors(config, &length);
+ KUNIT_ASSERT_EQ(test, length, 0);
+ KUNIT_ASSERT_NULL(test, array);
+
+ connector_cfg1 = vkms_config_add_connector(config);
+ array = vkms_config_get_connectors(config, &length);
+ KUNIT_ASSERT_EQ(test, length, 0);
+ KUNIT_ASSERT_NULL(test, array);
+
+ vkms_config_connector_set_enabled(connector_cfg1, true);
+ array = vkms_config_get_connectors(config, &length);
+ KUNIT_ASSERT_EQ(test, length, 1);
+ KUNIT_ASSERT_PTR_EQ(test, array[0], connector_cfg1);
+ kfree(array);
+
+ connector_cfg2 = vkms_config_add_connector(config);
+ vkms_config_connector_set_enabled(connector_cfg2, true);
+ array = vkms_config_get_connectors(config, &length);
+ KUNIT_ASSERT_EQ(test, length, 2);
+ KUNIT_ASSERT_PTR_EQ(test, array[0], connector_cfg1);
+ KUNIT_ASSERT_PTR_EQ(test, array[1], connector_cfg2);
+ kfree(array);
+
+ vkms_config_connector_set_enabled(connector_cfg1, false);
+ vkms_config_destroy_connector(connector_cfg2);
+ array = vkms_config_get_connectors(config, &length);
+ KUNIT_ASSERT_NULL(test, array);
+
+ vkms_config_destroy(config);
+}
+
static void vkms_config_test_valid_plane_number(struct kunit *test)
{
struct vkms_config *config;
@@ -423,6 +472,39 @@ static void vkms_config_test_valid_encoder_possible_crtcs(struct kunit *test)
vkms_config_destroy(config);
}
+static void vkms_config_test_valid_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);
+
+ /* Valid: No connectors */
+ connector_cfg = list_first_entry(&config->connectors, typeof(*connector_cfg), link);
+ vkms_config_destroy_connector(connector_cfg);
+ KUNIT_EXPECT_TRUE(test, vkms_config_is_valid(config));
+
+ /* Valid: Only a disabled connector */
+ connector_cfg = vkms_config_add_connector(config);
+ KUNIT_EXPECT_TRUE(test, vkms_config_is_valid(config));
+
+ /* Valid: The connector is enabled */
+ vkms_config_connector_set_enabled(connector_cfg, true);
+ KUNIT_EXPECT_TRUE(test, vkms_config_is_valid(config));
+
+ /* Invalid: Too many connectors */
+ for (n = 0; n <= 32; n++) {
+ connector_cfg = vkms_config_add_connector(config);
+ vkms_config_connector_set_enabled(connector_cfg, true);
+ }
+
+ 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;
@@ -613,12 +695,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_valid_plane_number),
KUNIT_CASE(vkms_config_test_valid_plane_type),
KUNIT_CASE(vkms_config_test_valid_plane_possible_crtcs),
KUNIT_CASE(vkms_config_test_valid_crtc_number),
KUNIT_CASE(vkms_config_test_valid_encoder_number),
KUNIT_CASE(vkms_config_test_valid_encoder_possible_crtcs),
+ KUNIT_CASE(vkms_config_test_valid_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 437a9980e9a8..e94e48fe3ad9 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,11 @@ 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_add_connector(config);
+ if (IS_ERR(connector_cfg))
+ goto err_alloc;
+ vkms_config_connector_set_enabled(connector_cfg, true);
+
return config;
err_alloc:
@@ -99,6 +106,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 +117,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);
}
@@ -194,6 +205,39 @@ struct vkms_config_encoder **vkms_config_get_encoders(const struct vkms_config *
return array;
}
+struct vkms_config_connector **vkms_config_get_connectors(const struct vkms_config *config,
+ size_t *out_length)
+{
+ struct vkms_config_connector **array;
+ struct vkms_config_connector *connector_cfg;
+ size_t length = 0;
+ int n = 0;
+
+ list_for_each_entry(connector_cfg, &config->connectors, link) {
+ if (vkms_config_connector_is_enabled(connector_cfg))
+ length++;
+ }
+
+ if (length == 0) {
+ *out_length = length;
+ return NULL;
+ }
+
+ array = kmalloc_array(length, sizeof(*array), GFP_KERNEL);
+ if (!array)
+ return ERR_PTR(-ENOMEM);
+
+ list_for_each_entry(connector_cfg, &config->connectors, link) {
+ if (vkms_config_connector_is_enabled(connector_cfg)) {
+ array[n] = connector_cfg;
+ n++;
+ }
+ }
+
+ *out_length = length;
+ return array;
+}
+
static bool valid_plane_number(struct vkms_config *config)
{
size_t n_planes;
@@ -325,6 +369,24 @@ static bool valid_encoder_possible_crtcs(struct vkms_config *config)
return true;
}
+static bool valid_connector_number(struct vkms_config *config)
+{
+ struct vkms_config_connector *connector_cfg;
+ size_t n_connectors = 0;
+
+ list_for_each_entry(connector_cfg, &config->connectors, link) {
+ if (vkms_config_connector_is_enabled(connector_cfg))
+ n_connectors++;
+ }
+
+ if (n_connectors >= 32) {
+ pr_err("The number of connectors must be between 0 and 31\n");
+ return false;
+ }
+
+ return true;
+}
+
bool vkms_config_is_valid(struct vkms_config *config)
{
struct vkms_config_crtc *crtc_cfg;
@@ -338,6 +400,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;
@@ -361,6 +426,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);
@@ -381,6 +447,12 @@ static int vkms_config_show(struct seq_file *m, void *data)
seq_puts(m, "encoder\n");
}
+ list_for_each_entry(connector_cfg, &vkmsdev->config->connectors, link) {
+ seq_puts(m, "connector:\n");
+ seq_printf(m, "\tenabled=%d\n",
+ vkms_config_connector_is_enabled(connector_cfg));
+ }
+
return 0;
}
@@ -620,3 +692,24 @@ vkms_config_encoder_get_possible_crtcs(struct vkms_config_encoder *encoder_cfg,
*out_length = length;
return array;
}
+
+struct vkms_config_connector *vkms_config_add_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);
+
+ vkms_config_connector_set_enabled(connector_cfg, false);
+
+ 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 5f4a33e113bf..cc32aadfda8d 100644
--- a/drivers/gpu/drm/vkms/vkms_config.h
+++ b/drivers/gpu/drm/vkms/vkms_config.h
@@ -15,6 +15,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 {
@@ -22,6 +23,7 @@ struct vkms_config {
struct list_head planes;
struct list_head crtcs;
struct list_head encoders;
+ struct list_head connectors;
struct vkms_device *dev;
};
@@ -85,6 +87,27 @@ struct vkms_config_encoder {
struct drm_encoder *encoder;
};
+/**
+ * struct vkms_config_connector
+ *
+ * @link: Link to the others connector in vkms_config
+ * @enabled: Connector are a different from planes, CRTCs and encoders because
+ * they can be added and removed once the device is created.
+ * This flag represents if they are part of the device or not.
+ * @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;
+
+ bool enabled;
+
+ /* Internal usage */
+ struct vkms_connector *connector;
+};
+
/**
* vkms_config_create() - Create a new VKMS configuration
* @dev_name: Name of the device
@@ -173,6 +196,21 @@ struct vkms_config_crtc **vkms_config_get_crtcs(const struct vkms_config *config
struct vkms_config_encoder **vkms_config_get_encoders(const struct vkms_config *config,
size_t *out_length);
+/**
+ * vkms_config_get_connectors() - Return the array of connectors of the device
+ * @config: Configuration to get the connectors from
+ * @out_length: Length of the returned array
+ *
+ * Note that only enabled connectors are returned.
+ *
+ * Returns:
+ * A list of pointers to the configurations. On success, the caller is
+ * responsible to free the returned array, but not its contents. On error,
+ * it returns an error and @out_length is invalid.
+ */
+struct vkms_config_connector **vkms_config_get_connectors(const struct vkms_config *config,
+ size_t *out_length);
+
/**
* vkms_config_is_valid() - Validate a configuration
* @config: Configuration to validate
@@ -367,4 +405,42 @@ struct vkms_config_crtc **
vkms_config_encoder_get_possible_crtcs(struct vkms_config_encoder *encoder_cfg,
size_t *out_length);
+/**
+ * vkms_config_add_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_add_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);
+
+/**
+ * vkms_config_connector_is_enabled() - If the connector is part of the device
+ * @connector_cfg: The connector
+ */
+static inline bool
+vkms_config_connector_is_enabled(struct vkms_config_connector *connector_cfg)
+{
+ return connector_cfg->enabled;
+}
+
+/**
+ * vkms_config_connector_set_enabled() - If the connector is part of the device
+ * @crtc_cfg: Target connector
+ * @enabled: Add or remove the connector
+ */
+static inline void
+vkms_config_connector_set_enabled(struct vkms_config_connector *connector_cfg,
+ bool enabled)
+{
+ connector_cfg->enabled = enabled;
+}
+
#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] 41+ messages in thread
* [PATCH 13/13] drm/vkms: Allow to attach connectors and encoders
2025-01-29 11:00 [PATCH 00/13] drm/vkms: Allow to configure device José Expósito
` (11 preceding siblings ...)
2025-01-29 11:00 ` [PATCH 12/13] drm/vkms: Allow to configure multiple connectors José Expósito
@ 2025-01-29 11:00 ` José Expósito
2025-01-30 13:48 ` Louis Chauvet
2025-01-30 13:48 ` [PATCH 00/13] drm/vkms: Allow to configure device Louis Chauvet
13 siblings, 1 reply; 41+ messages in thread
From: José Expósito @ 2025-01-29 11:00 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.
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 | 62 +++++++++++++++++
drivers/gpu/drm/vkms/vkms_config.c | 69 +++++++++++++++++++
drivers/gpu/drm/vkms/vkms_config.h | 33 +++++++++
drivers/gpu/drm/vkms/vkms_output.c | 57 +++++++++++----
4 files changed, 209 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
index 40c385eedc1d..d89acdc1a752 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_config_test.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
@@ -688,6 +688,67 @@ 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 **array;
+ size_t length;
+ int err;
+
+ config = vkms_config_create("test");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
+
+ connector_cfg1 = vkms_config_add_connector(config);
+ connector_cfg2 = vkms_config_add_connector(config);
+ encoder_cfg1 = vkms_config_add_encoder(config);
+ encoder_cfg2 = vkms_config_add_encoder(config);
+
+ /* No possible encoders */
+ array = vkms_config_connector_get_possible_encoders(connector_cfg1, &length);
+ KUNIT_ASSERT_EQ(test, length, 0);
+ KUNIT_ASSERT_NULL(test, array);
+
+ array = vkms_config_connector_get_possible_encoders(connector_cfg2, &length);
+ KUNIT_ASSERT_EQ(test, length, 0);
+ KUNIT_ASSERT_NULL(test, array);
+
+ /* 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);
+
+ array = vkms_config_connector_get_possible_encoders(connector_cfg1, &length);
+ KUNIT_ASSERT_EQ(test, length, 2);
+ KUNIT_ASSERT_PTR_EQ(test, array[0], encoder_cfg1);
+ KUNIT_ASSERT_PTR_EQ(test, array[1], encoder_cfg2);
+ kfree(array);
+
+ array = vkms_config_connector_get_possible_encoders(connector_cfg2, &length);
+ KUNIT_ASSERT_EQ(test, length, 0);
+ KUNIT_ASSERT_NULL(test, array);
+
+ /* Connector 1 attached to encoder 1 and connector 2 to encoder 2 */
+ vkms_config_connector_detach_encoder(connector_cfg1, encoder_cfg2);
+
+ array = vkms_config_connector_get_possible_encoders(connector_cfg1, &length);
+ KUNIT_ASSERT_EQ(test, length, 1);
+ KUNIT_ASSERT_PTR_EQ(test, array[0], encoder_cfg1);
+ kfree(array);
+
+ err = vkms_config_connector_attach_encoder(connector_cfg2, encoder_cfg2);
+ KUNIT_EXPECT_EQ(test, err, 0);
+
+ array = vkms_config_connector_get_possible_encoders(connector_cfg2, &length);
+ KUNIT_ASSERT_EQ(test, length, 1);
+ KUNIT_ASSERT_PTR_EQ(test, array[0], encoder_cfg2);
+ kfree(array);
+
+ 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,
@@ -706,6 +767,7 @@ static struct kunit_case vkms_config_test_cases[] = {
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 e94e48fe3ad9..db6e3c71fd68 100644
--- a/drivers/gpu/drm/vkms/vkms_config.c
+++ b/drivers/gpu/drm/vkms/vkms_config.c
@@ -94,6 +94,9 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
goto err_alloc;
vkms_config_connector_set_enabled(connector_cfg, true);
+ if (vkms_config_connector_attach_encoder(connector_cfg, encoder_cfg))
+ goto err_alloc;
+
return config;
err_alloc:
@@ -629,6 +632,11 @@ struct vkms_config_encoder *vkms_config_add_encoder(struct vkms_config *config)
void vkms_config_destroy_encoder(struct vkms_config *config,
struct vkms_config_encoder *encoder_cfg)
{
+ struct vkms_config_connector *connector_cfg;
+
+ list_for_each_entry(connector_cfg, &config->connectors, link)
+ vkms_config_connector_detach_encoder(connector_cfg, encoder_cfg);
+
xa_destroy(&encoder_cfg->possible_crtcs);
list_del(&encoder_cfg->link);
kfree(encoder_cfg);
@@ -702,6 +710,7 @@ struct vkms_config_connector *vkms_config_add_connector(struct vkms_config *conf
return ERR_PTR(-ENOMEM);
vkms_config_connector_set_enabled(connector_cfg, false);
+ xa_init_flags(&connector_cfg->possible_encoders, XA_FLAGS_ALLOC);
list_add_tail(&connector_cfg->link, &config->connectors);
@@ -710,6 +719,66 @@ struct vkms_config_connector *vkms_config_add_connector(struct vkms_config *conf
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;
+
+ xa_for_each(&connector_cfg->possible_encoders, 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;
+
+ xa_for_each(&connector_cfg->possible_encoders, idx, possible_encoder) {
+ if (possible_encoder == encoder_cfg)
+ xa_erase(&connector_cfg->possible_encoders, idx);
+ }
+}
+
+struct vkms_config_encoder **
+vkms_config_connector_get_possible_encoders(struct vkms_config_connector *connector_cfg,
+ size_t *out_length)
+{
+ struct vkms_config_encoder **array;
+ struct vkms_config_encoder *possible_encoder;
+ unsigned long idx;
+ size_t length = 0;
+ int n = 0;
+
+ xa_for_each(&connector_cfg->possible_encoders, idx, possible_encoder)
+ length++;
+
+ if (length == 0) {
+ *out_length = length;
+ return NULL;
+ }
+
+ array = kmalloc_array(length, sizeof(*array), GFP_KERNEL);
+ if (!array)
+ return ERR_PTR(-ENOMEM);
+
+ xa_for_each(&connector_cfg->possible_encoders, idx, possible_encoder) {
+ array[n] = possible_encoder;
+ n++;
+ }
+
+ *out_length = length;
+ return array;
+}
diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
index cc32aadfda8d..24052de05e98 100644
--- a/drivers/gpu/drm/vkms/vkms_config.h
+++ b/drivers/gpu/drm/vkms/vkms_config.h
@@ -94,6 +94,7 @@ struct vkms_config_encoder {
* @enabled: Connector are a different from planes, CRTCs and encoders because
* they can be added and removed once the device is created.
* This flag represents if they are part of the device or not.
+ * @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
@@ -103,6 +104,7 @@ struct vkms_config_connector {
struct list_head link;
bool enabled;
+ struct xarray possible_encoders;
/* Internal usage */
struct vkms_connector *connector;
@@ -443,4 +445,35 @@ vkms_config_connector_set_enabled(struct vkms_config_connector *connector_cfg,
connector_cfg->enabled = enabled;
}
+/**
+ * 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);
+
+/**
+ * vkms_config_connector_get_possible_encoders() - Return the array of possible
+ * encoders
+ * @connector_cfg: Connector to get the possible encoders from
+ * @out_length: Length of the returned array
+ *
+ * Returns:
+ * A list of pointers to the configurations. On success, the caller is
+ * responsible to free the returned array, but not its contents. On error,
+ * it returns an error and @out_length is invalid.
+ */
+struct vkms_config_encoder **
+vkms_config_connector_get_possible_encoders(struct vkms_config_connector *connector_cfg,
+ size_t *out_length);
+
#endif /* _VKMS_CONFIG_H_ */
diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
index 9c3e00817add..20ee8fdfa6f8 100644
--- a/drivers/gpu/drm/vkms/vkms_output.c
+++ b/drivers/gpu/drm/vkms/vkms_output.c
@@ -8,13 +8,14 @@
int vkms_output_init(struct vkms_device *vkmsdev)
{
struct drm_device *dev = &vkmsdev->drm;
- struct vkms_connector *connector;
struct vkms_config_plane **plane_cfgs = NULL;
size_t n_planes;
struct vkms_config_crtc **crtc_cfgs = NULL;
size_t n_crtcs;
struct vkms_config_encoder **encoder_cfgs = NULL;
size_t n_encoders;
+ struct vkms_config_connector **connector_cfgs = NULL;
+ size_t n_connectors;
int ret = 0;
int writeback;
unsigned int n, i;
@@ -35,6 +36,13 @@ int vkms_output_init(struct vkms_device *vkmsdev)
goto err_free;
}
+ connector_cfgs = vkms_config_get_connectors(vkmsdev->config,
+ &n_connectors);
+ if (IS_ERR(connector_cfgs)) {
+ ret = PTR_ERR(connector_cfgs);
+ goto err_free;
+ }
+
for (n = 0; n < n_planes; n++) {
struct vkms_config_plane *plane_cfg;
enum drm_plane_type type;
@@ -136,18 +144,42 @@ int vkms_output_init(struct vkms_device *vkmsdev)
kfree(possible_crtcs);
}
- connector = vkms_connector_init(vkmsdev);
- if (IS_ERR(connector)) {
- DRM_ERROR("Failed to init connector\n");
- ret = PTR_ERR(connector);
- goto err_free;
- }
+ for (n = 0; n < n_connectors; n++) {
+ struct vkms_config_connector *connector_cfg;
+ struct vkms_config_encoder **possible_encoders;
+ size_t n_possible_encoders;
- /* Attach the encoder and the connector */
- ret = drm_connector_attach_encoder(&connector->base, encoder_cfgs[0]->encoder);
- if (ret) {
- DRM_ERROR("Failed to attach connector to encoder\n");
- goto err_free;
+ connector_cfg = connector_cfgs[n];
+
+ connector_cfg->connector = vkms_connector_init(vkmsdev);
+ if (IS_ERR(connector_cfg->connector)) {
+ DRM_ERROR("Failed to init connector\n");
+ ret = PTR_ERR(connector_cfg->connector);
+ goto err_free;
+ }
+
+ possible_encoders =
+ vkms_config_connector_get_possible_encoders(connector_cfg,
+ &n_possible_encoders);
+ if (IS_ERR(possible_encoders)) {
+ ret = PTR_ERR(possible_encoders);
+ goto err_free;
+ }
+
+ for (i = 0; i < n_possible_encoders; i++) {
+ struct vkms_config_encoder *possible_encoder;
+
+ possible_encoder = possible_encoders[i];
+ ret = drm_connector_attach_encoder(&connector_cfg->connector->base,
+ possible_encoder->encoder);
+ if (ret) {
+ DRM_ERROR("Failed to attach connector to encoder\n");
+ kfree(possible_encoders);
+ goto err_free;
+ }
+ }
+
+ kfree(possible_encoders);
}
drm_mode_config_reset(dev);
@@ -156,6 +188,7 @@ int vkms_output_init(struct vkms_device *vkmsdev)
kfree(plane_cfgs);
kfree(crtc_cfgs);
kfree(encoder_cfgs);
+ kfree(connector_cfgs);
return ret;
}
--
2.48.1
^ permalink raw reply related [flat|nested] 41+ messages in thread
* Re: [PATCH 01/13] drm/vkms: Extract vkms_connector header
2025-01-29 11:00 ` [PATCH 01/13] drm/vkms: Extract vkms_connector header José Expósito
@ 2025-01-30 13:48 ` Louis Chauvet
2025-02-11 10:37 ` José Expósito
0 siblings, 1 reply; 41+ messages in thread
From: Louis Chauvet @ 2025-01-30 13:48 UTC (permalink / raw)
To: José Expósito
Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
tzimmermann, airlied, dri-devel, linux-kernel
On 29/01/25 - 12:00, José Expósito wrote:
> 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.
>
> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
Non blocking issue: Do you think it is possible to split this in two
different commits: one to create vkms_connector.h/c and one to create
struct vkms_connector?
Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
[...]
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 00/13] drm/vkms: Allow to configure device
2025-01-29 11:00 [PATCH 00/13] drm/vkms: Allow to configure device José Expósito
` (12 preceding siblings ...)
2025-01-29 11:00 ` [PATCH 13/13] drm/vkms: Allow to attach connectors and encoders José Expósito
@ 2025-01-30 13:48 ` Louis Chauvet
2025-01-31 9:31 ` José Expósito
13 siblings, 1 reply; 41+ messages in thread
From: Louis Chauvet @ 2025-01-30 13:48 UTC (permalink / raw)
To: José Expósito
Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
tzimmermann, airlied, dri-devel, linux-kernel
On 29/01/25 - 12:00, José Expósito wrote:
> 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.
Hi José,
Thanks a lot!
This series is amazing and better than mine on many points. I have few
comments:
- a "strange" naming pair: add/destroy (I expect add/remove or
create/destroy like other function in DRM)
- usage of "complex" list accessors, can't we just create iterators?
- should we use pr_err in vkms_config_valid?
> 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.
To avoid confusion, you should add the Co-developped-by tag, so it will be
clear that we worked together on this.
> I'm still polishing the ConfigFS code [3] and its IGT tests [4] (connector
> hot-add/remove bugs) but the IGT tests also exercise this series and can be used
> for additional test coverage.
I will take a look at those series. For the connector hot-add/remove, do
you have any example of usage in the kernel? I did not found anything in
the documentation explaining they are hot-addable.
Thanks again for this series,
Louis Chauvet
> 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
>
> José Expósito (12):
> drm/vkms: Extract vkms_connector header
> 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
>
> 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 | 782 +++++++++++++++++
> drivers/gpu/drm/vkms/vkms_config.c | 784 ++++++++++++++++++
> drivers/gpu/drm/vkms/vkms_config.h | 479 +++++++++++
> drivers/gpu/drm/vkms/vkms_connector.c | 61 ++
> drivers/gpu/drm/vkms/vkms_connector.h | 26 +
> drivers/gpu/drm/vkms/vkms_drv.c | 45 +-
> drivers/gpu/drm/vkms/vkms_drv.h | 17 +-
> drivers/gpu/drm/vkms/vkms_output.c | 255 ++++--
> 12 files changed, 2337 insertions(+), 139 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] 41+ messages in thread
* Re: [PATCH 02/13] drm/vkms: Add KUnit test scaffolding
2025-01-29 11:00 ` [PATCH 02/13] drm/vkms: Add KUnit test scaffolding José Expósito
@ 2025-01-30 13:48 ` Louis Chauvet
0 siblings, 0 replies; 41+ messages in thread
From: Louis Chauvet @ 2025-01-30 13:48 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 29/01/25 - 12:00, 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
>
> Signed-off-by: Arthur Grillo <arthurgrillo@riseup.net>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
Co-developped-by: Arthur Grillo <arthurgrillo@riseup.net>
Signed-off-by: Arthur Grillo <arthurgrillo@riseup.net>
Co-developped-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>
Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 03/13] drm/vkms: Extract vkms_config header
2025-01-29 11:00 ` [PATCH 03/13] drm/vkms: Extract vkms_config header José Expósito
@ 2025-01-30 13:48 ` Louis Chauvet
2025-02-11 10:39 ` José Expósito
0 siblings, 1 reply; 41+ messages in thread
From: Louis Chauvet @ 2025-01-30 13:48 UTC (permalink / raw)
To: José Expósito
Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
tzimmermann, airlied, dri-devel, linux-kernel
On 29/01/25 - 12:00, 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.
>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
Co-developped-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>
[...]
> diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c
> @@ -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);
>
> default_config = config;
>
> @@ -243,7 +223,7 @@ static int __init vkms_init(void)
>
> ret = vkms_create(config);
> if (ret)
> - kfree(config);
> + vkms_config_destroy(config);
I just have a question here: is it not a problem to kfree config (and
default_config) here? There is not risk to have a
use-after-free/double-free in vkms_exit?
> return ret;
> }
> @@ -272,7 +252,7 @@ static void __exit vkms_exit(void)
> if (default_config->dev)
The use-after-free may be here?
> vkms_destroy(default_config);
>
> - kfree(default_config);
> + vkms_config_destroy(default_config);
And maybe double-free?
> }
If this is not an issue (ie we have a garantee that vkms_exit is never
called if vkms_init fails), you can add my
Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
[...]
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 04/13] drm/vkms: Move default_config creation to its own function
2025-01-29 11:00 ` [PATCH 04/13] drm/vkms: Move default_config creation to its own function José Expósito
@ 2025-01-30 13:48 ` Louis Chauvet
0 siblings, 0 replies; 41+ messages in thread
From: Louis Chauvet @ 2025-01-30 13:48 UTC (permalink / raw)
To: José Expósito
Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
tzimmermann, airlied, dri-devel, linux-kernel
On 29/01/25 - 12:00, José Expósito wrote:
> Extract the initialization of the default configuration to a function.
> Refactor, no functional changes.
>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
Co-developped-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>
Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 05/13] drm/vkms: Set device name from vkms_config
2025-01-29 11:00 ` [PATCH 05/13] drm/vkms: Set device name from vkms_config José Expósito
@ 2025-01-30 13:48 ` Louis Chauvet
0 siblings, 0 replies; 41+ messages in thread
From: Louis Chauvet @ 2025-01-30 13:48 UTC (permalink / raw)
To: José Expósito
Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
tzimmermann, airlied, dri-devel, linux-kernel
On 29/01/25 - 12:00, José Expósito wrote:
> In order to be able to create multiple devices, the device name needs to
> be unique.
>
> Allow to set it in the VKMS configuration.
>
> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
[...]
> diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
> index d8644a1e3e18..526800b1afb4 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 live times */
s/live times/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.h b/drivers/gpu/drm/vkms/vkms_config.h
> index d0868750826a..3e4f39cbe2e2 100644
> --- a/drivers/gpu/drm/vkms/vkms_config.h
> +++ b/drivers/gpu/drm/vkms/vkms_config.h
> @@ -10,12 +10,14 @@
[...]
> +/**
> + * vkms_config_get_device_name() - Return the name of the device
> + * @config: Configuration to get the device name from
Can you add here that the pointer is only valid while vkms_config is
valid?
> + */
> +static inline const char *
> +vkms_config_get_device_name(struct vkms_config *config)
> +{
> + return config->dev_name;
> +}
> +
With those modifications:
Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 06/13] drm/vkms: Add a validation function for VKMS configuration
2025-01-29 11:00 ` [PATCH 06/13] drm/vkms: Add a validation function for VKMS configuration José Expósito
@ 2025-01-30 13:48 ` Louis Chauvet
0 siblings, 0 replies; 41+ messages in thread
From: Louis Chauvet @ 2025-01-30 13:48 UTC (permalink / raw)
To: José Expósito
Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
tzimmermann, airlied, dri-devel, linux-kernel
On 29/01/25 - 12:00, 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.
>
> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
Co-developped-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 last SoB should always be the one sending the mail)
Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 07/13] drm/vkms: Allow to configure multiple planes
2025-01-29 11:00 ` [PATCH 07/13] drm/vkms: Allow to configure multiple planes José Expósito
@ 2025-01-30 13:48 ` Louis Chauvet
2025-02-11 10:43 ` José Expósito
0 siblings, 1 reply; 41+ messages in thread
From: Louis Chauvet @ 2025-01-30 13:48 UTC (permalink / raw)
To: José Expósito
Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
tzimmermann, airlied, dri-devel, linux-kernel
On 29/01/25 - 12:00, 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.
>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
Co-developped-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>
[...]
> diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
[...]
> +static void vkms_config_test_get_planes(struct kunit *test)
> +{
> + struct vkms_config *config;
> + struct vkms_config_plane *plane_cfg1, *plane_cfg2;
> + struct vkms_config_plane **array;
> + size_t length;
> +
> + config = vkms_config_create("test");
> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
> +
> + array = vkms_config_get_planes(config, &length);
> + KUNIT_ASSERT_EQ(test, length, 0);
> + KUNIT_ASSERT_NULL(test, array);
> +
> + plane_cfg1 = vkms_config_add_plane(config);
> + array = vkms_config_get_planes(config, &length);
> + KUNIT_ASSERT_EQ(test, length, 1);
> + KUNIT_ASSERT_PTR_EQ(test, array[0], plane_cfg1);
> + kfree(array);
> +
> + plane_cfg2 = vkms_config_add_plane(config);
> + array = vkms_config_get_planes(config, &length);
> + KUNIT_ASSERT_EQ(test, length, 2);
> + KUNIT_ASSERT_PTR_EQ(test, array[0], plane_cfg1);
> + KUNIT_ASSERT_PTR_EQ(test, array[1], plane_cfg2);
> + kfree(array);
> +
> + vkms_config_destroy_plane(plane_cfg1);
> + array = vkms_config_get_planes(config, &length);
> + KUNIT_ASSERT_EQ(test, length, 1);
> + KUNIT_ASSERT_PTR_EQ(test, array[0], plane_cfg2);
> + kfree(array);
> +
> + vkms_config_destroy(config);
> +}
In this test I have the feeling that vkms_config_get_planes always returns
a predictable order. It is maybe trivial here, but I would prefer to shows
that the order is not stable, for example:
bool plane_cfg1_found = false;
bool plane_cfg2_found = false;
vkms_config_for_each_plane(config, plane_cfg) {
if (plane_cfg == plane_cfg1)
plane_cfg1_found = true;
else if (plane_cfg == plane_cfg2)
plane_cfg2_found = true;
else
KUNIT_FAILS("Unexpected plane");
}
KUNIT_ASSERT(test, plane_cfg1_found);
KUNIT_ASSERT(test, plane_cfg2_found);
[...]
> +static void vkms_config_test_valid_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_add_plane(config);
> +
> + KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
> +
> + vkms_config_destroy(config);
> +}
For this function, the naming is a bit strange, it says
"valid_plane_number", but you test only invalid plane number.
Can you rename it to vkms_config_test_invalid_plane_number?
[...]
> diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
[...]
> +struct vkms_config_plane **vkms_config_get_planes(const struct vkms_config *config,
> + size_t *out_length)
> +{
> + struct vkms_config_plane **array;
> + struct vkms_config_plane *plane_cfg;
> + size_t length;
> + int n = 0;
> +
> + length = list_count_nodes((struct list_head *)&config->planes);
> + if (length == 0) {
> + *out_length = length;
> + return NULL;
> + }
> +
> + array = kmalloc_array(length, sizeof(*array), GFP_KERNEL);
> + if (!array)
> + return ERR_PTR(-ENOMEM);
> +
> + list_for_each_entry(plane_cfg, &config->planes, link) {
> + array[n] = plane_cfg;
> + n++;
> + }
> +
> + *out_length = length;
> + return array;
> +}
To join the comment on the test, I am not a big fan of creating a new list
to return to the caller, for three reasons:
- the caller needs to manage an other pointer;
- the caller needs to understand that the content of the array is only
valid if: the config is not freed, nobody else removed anything from the
planes;
- the caller may think this list always have the same order if he looks at
the tests.
I would prefer a simple macro to do an iteration over the config->planes
list: (I did not test this macro, but you have this idea)
#define vkms_config_iter_plane(config, plane_cfg) \
list_for_each_entry((plane_cfg), &(config).planes, link)
This way:
- no new pointer to manage;
- if one day we have concurency issue, we just have to protect config, not
config+all the planes;
- there is no expected order.
[...]
> 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;
> }
I really like the idea to split the validation function, way simpler!
[...]
> +void vkms_config_destroy_plane(struct vkms_config_plane *plane_cfg)
> +{
> + list_del(&plane_cfg->link);
> + kfree(plane_cfg);
> +}
I would prefer a "standard" function pair, i.e.: add/remove or
create/destroy, not add/destroy.
For me it should be create/destroy, you create the plane by using a
config, so it is clear it will be attached to it.
If you choose add/remove, you should explains in the documentation that
remove is also doing kfree.
[...]
> diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
[...]
> @@ -11,61 +11,63 @@ 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;
> - int ret;
> + struct vkms_plane *primary = NULL, *cursor = NULL;
> + struct vkms_config_plane **plane_cfgs = NULL;
> + size_t n_planes;
> + int ret = 0;
> int writeback;
> unsigned int n;
I think it could be interesting to have a vkms_config_is_valid call here.
It will avoid raising DRM errors or create unexpected devices.
It will also garantee in a later patch that
vkms_config_crtc_get_primary_plane is a valid pointer.
> - /*
> - * 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);
> + plane_cfgs = vkms_config_get_planes(vkmsdev->config, &n_planes);
> + if (IS_ERR(plane_cfgs))
> + return PTR_ERR(plane_cfgs);
If you agree on the iterator implementation, this code could be simplified
a lot.
> - if (vkmsdev->config->cursor) {
> - cursor = vkms_plane_init(vkmsdev, DRM_PLANE_TYPE_CURSOR);
> - if (IS_ERR(cursor))
> - return PTR_ERR(cursor);
> + for (n = 0; n < n_planes; n++) {
> + struct vkms_config_plane *plane_cfg;
> + enum drm_plane_type type;
> +
> + plane_cfg = plane_cfgs[n];
> + type = vkms_config_plane_get_type(plane_cfg);
> +
> + plane_cfg->plane = vkms_plane_init(vkmsdev, type);
Can we pass plane_cfg in vkms_plane_init? This way we don't have to
touch vkms_output_init when adding new vkms_config_plane members.
> + if (IS_ERR(plane_cfg->plane)) {
> + DRM_DEV_ERROR(dev->dev, "Failed to init vkms plane\n");
> + ret = PTR_ERR(plane_cfg->plane);
> + goto err_free;
> + }
> +
> + if (type == DRM_PLANE_TYPE_PRIMARY)
> + primary = plane_cfg->plane;
> + else if (type == DRM_PLANE_TYPE_CURSOR)
> + cursor = plane_cfg->plane;
> }
[...]
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 08/13] drm/vkms: Allow to configure multiple CRTCs
2025-01-29 11:00 ` [PATCH 08/13] drm/vkms: Allow to configure multiple CRTCs José Expósito
@ 2025-01-30 13:48 ` Louis Chauvet
2025-02-11 10:44 ` José Expósito
0 siblings, 1 reply; 41+ messages in thread
From: Louis Chauvet @ 2025-01-30 13:48 UTC (permalink / raw)
To: José Expósito
Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
tzimmermann, airlied, dri-devel, linux-kernel
On 29/01/25 - 12:00, 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.
>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
Co-developped-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>
[...]
> diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
[...]
> +static void vkms_config_test_valid_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_add_crtc(config);
> +
> + KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
> +
> + vkms_config_destroy(config);
> +}
Same as before, can you rename the fonction to
vkms_config_test_invalid_crtc_number
[...]
> diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
[...]
> +struct vkms_config_crtc **vkms_config_get_crtcs(const struct vkms_config *config,
> + size_t *out_length)
> +{
> + struct vkms_config_crtc **array;
> + struct vkms_config_crtc *crtc_cfg;
> + size_t length;
> + int n = 0;
> +
> + length = list_count_nodes((struct list_head *)&config->crtcs);
> + if (length == 0) {
> + *out_length = length;
> + return NULL;
> + }
> +
> + array = kmalloc_array(length, sizeof(*array), GFP_KERNEL);
> + if (!array)
> + return ERR_PTR(-ENOMEM);
> +
> + list_for_each_entry(crtc_cfg, &config->crtcs, link) {
> + array[n] = crtc_cfg;
> + n++;
> + }
> +
> + *out_length = length;
> + return array;
> +}
Same as before, can't we use an iterator?
[...]
> +static bool valid_crtc_number(struct vkms_config *config)
> +{
> + size_t n_crtcs;
> +
> + n_crtcs = list_count_nodes(&config->crtcs);
> + if (n_crtcs <= 0 || n_crtcs >= 32) {
> + pr_err("The number of CRTCs must be between 1 and 31\n");
I agree we need some logs, but I think pr_err is too agressive (i.e may
be considered as an error by some test tools).
I think we should at least:
- lower to warn/notice/info
- use drm variants of the macro
> + return false;
> + }
> +
> + return true;
> +}
> +
[...]
> +struct vkms_config_crtc *vkms_config_add_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);
> +}
Same as before, the pair add/destroy seems strange.
> +++ 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));
At this point we only create one crtc, can you move this change in the
commit where you create multiple crtc?
> if (ret) {
> DRM_ERROR("Failed to vblank\n");
> goto out_devres;
> --
> 2.48.1
>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 09/13] drm/vkms: Allow to attach planes and CRTCs
2025-01-29 11:00 ` [PATCH 09/13] drm/vkms: Allow to attach planes and CRTCs José Expósito
@ 2025-01-30 13:48 ` Louis Chauvet
2025-02-11 10:47 ` José Expósito
0 siblings, 1 reply; 41+ messages in thread
From: Louis Chauvet @ 2025-01-30 13:48 UTC (permalink / raw)
To: José Expósito
Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
tzimmermann, airlied, dri-devel, linux-kernel
On 29/01/25 - 12:00, José Expósito wrote:
> 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.
>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
Co-developped-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>
[...]
> diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
[...]
> -static bool valid_plane_type(struct vkms_config *config)
> +static bool valid_plane_type(struct vkms_config *config,
> + struct vkms_config_crtc *crtc_cfg)
What do you think about renaming it to "valid_planes_for_crtc" to reflect
the fact you tests if a CRTC is attached to a valid combination of planes?
> {
> struct vkms_config_plane *plane_cfg;
> bool has_primary_plane = false;
> bool has_cursor_plane = false;
>
> list_for_each_entry(plane_cfg, &config->planes, link) {
> + 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) {
> - pr_err("Multiple primary planes\n");
> - return false;
> - }
> + xa_for_each(&plane_cfg->possible_crtcs, idx, possible_crtc) {
> + if (possible_crtc != crtc_cfg)
> + continue;
>
> - has_primary_plane = true;
> - } else if (type == DRM_PLANE_TYPE_CURSOR) {
> - if (has_cursor_plane) {
> - pr_err("Multiple cursor planes\n");
> - return false;
> - }
> + if (type == DRM_PLANE_TYPE_PRIMARY) {
> + if (has_primary_plane) {
> + pr_err("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) {
> + pr_err("Multiple cursor planes\n");
> + return false;
> + }
> +
> + has_cursor_plane = true;
> + }
> }
> }
[...]
> +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;
> +
> + xa_for_each(&plane_cfg->possible_crtcs, idx, possible_crtc) {
> + if (possible_crtc == crtc_cfg)
> + return -EINVAL;
Is it really an error? After this call, we expect plane and crtc to be
attached, so if the plane is already attached, I don't see any issue.
> + }
> +
> + return xa_alloc(&plane_cfg->possible_crtcs, &crtc_idx, crtc_cfg,
> + xa_limit_32b, GFP_KERNEL);
> +}
> +
[...]
> +struct vkms_config_crtc **vkms_config_plane_get_possible_crtcs(struct vkms_config_plane *plane_cfg,
> + size_t *out_length)
> +{
> + struct vkms_config_crtc **array;
> + struct vkms_config_crtc *possible_crtc;
> + unsigned long idx;
> + size_t length = 0;
> + int n = 0;
> +
> + xa_for_each(&plane_cfg->possible_crtcs, idx, possible_crtc)
> + length++;
> +
> + if (length == 0) {
> + *out_length = length;
> + return NULL;
> + }
> +
> + array = kmalloc_array(length, sizeof(*array), GFP_KERNEL);
> + if (!array)
> + return ERR_PTR(-ENOMEM);
> +
> + xa_for_each(&plane_cfg->possible_crtcs, idx, possible_crtc) {
> + array[n] = possible_crtc;
> + n++;
> + }
> +
> + *out_length = length;
> + return array;
> +}
Same as before, can we use an iterator?
> +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)
Even if this is a private function, can we add a comment explaning that
the returned value is only one of the available planes of this type?
/**
* vkms_config_crtc_get_plane() - Get the first attached plane
* found of a specific type
* @config: configuration containing the crtc and the planes
* @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.
*/
> +{
> + struct vkms_config_plane *plane_cfg;
> + struct vkms_config_crtc *possible_crtc;
> + enum drm_plane_type current_type;
> + unsigned long idx;
> +
> + list_for_each_entry(plane_cfg, &config->planes, link) {
> + current_type = vkms_config_plane_get_type(plane_cfg);
> +
> + xa_for_each(&plane_cfg->possible_crtcs, idx, possible_crtc) {
> + if (possible_crtc == crtc_cfg && current_type == type)
> + return plane_cfg;
> + }
> + }
> +
> + return NULL;
> +}
[...]
> diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
[...]
> +/**
> + * vkms_config_crtc_primary_plane() - Return the primary plane for a CRTC
> + * @config: Configuration containing the CRTC
> + * @crtc_config: Target CRTC
> + *
> + * Returns:
> + * The primary plane or NULL if none is assigned yet.
> + */
Same as above, can you speficy that it is one of the primary plane?
> +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
Ditto
[...]
> diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
[...]
> @@ -35,19 +41,54 @@ int vkms_output_init(struct vkms_device *vkmsdev)
> ret = PTR_ERR(plane_cfg->plane);
> goto err_free;
> }
> + }
> +
> + for (n = 0; n < n_crtcs; n++) {
> + struct vkms_config_crtc *crtc_cfg;
> + struct vkms_config_plane *primary, *cursor;
> +
> + crtc_cfg = crtc_cfgs[n];
> + primary = vkms_config_crtc_primary_plane(vkmsdev->config, crtc_cfg);
> + cursor = vkms_config_crtc_cursor_plane(vkmsdev->config, crtc_cfg);
Linked with a previous comment: here we have no garantee that primary is a
valid pointer, can we check it or call vkms_config_is_valid to ensure it?
> + 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");
> + ret = PTR_ERR(crtc_cfg->crtc);
> + goto err_free;
> + }
[...]
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 10/13] drm/vkms: Allow to configure multiple encoders
2025-01-29 11:00 ` [PATCH 10/13] drm/vkms: Allow to configure multiple encoders José Expósito
@ 2025-01-30 13:48 ` Louis Chauvet
0 siblings, 0 replies; 41+ messages in thread
From: Louis Chauvet @ 2025-01-30 13:48 UTC (permalink / raw)
To: José Expósito
Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
tzimmermann, airlied, dri-devel, linux-kernel
On 29/01/25 - 12:00, 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.
>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
Co-developped-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>
[...]
> +static void vkms_config_test_valid_encoder_number(struct kunit *test)
Same as before, can you rename to vkms_config_test_invalid_encoder_number
> +{
> + 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_add_encoder(config);
> +
> + KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
> +
> + vkms_config_destroy(config);
> +}
> +
[...]
> +struct vkms_config_encoder **vkms_config_get_encoders(const struct vkms_config *config,
> + size_t *out_length)
> +{
> + struct vkms_config_encoder **array;
> + struct vkms_config_encoder *encoder_cfg;
> + size_t length;
> + int n = 0;
> +
> + length = list_count_nodes((struct list_head *)&config->encoders);
> + if (length == 0) {
> + *out_length = length;
> + return NULL;
> + }
> +
> + array = kmalloc_array(length, sizeof(*array), GFP_KERNEL);
> + if (!array)
> + return ERR_PTR(-ENOMEM);
> +
> + list_for_each_entry(encoder_cfg, &config->encoders, link) {
> + array[n] = encoder_cfg;
> + n++;
> + }
> +
> + *out_length = length;
> + return array;
> +}
Same as before, can we use an iterator?
> +struct vkms_config_encoder *vkms_config_add_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);
> +}
Same as before, can we change the add/destroy naming?
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 11/13] drm/vkms: Allow to attach encoders and CRTCs
2025-01-29 11:00 ` [PATCH 11/13] drm/vkms: Allow to attach encoders and CRTCs José Expósito
@ 2025-01-30 13:48 ` Louis Chauvet
0 siblings, 0 replies; 41+ messages in thread
From: Louis Chauvet @ 2025-01-30 13:48 UTC (permalink / raw)
To: José Expósito
Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
tzimmermann, airlied, dri-devel, linux-kernel
On 29/01/25 - 12:00, 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.
>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
Co-developped-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>
[...]
> diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
[...]
> +struct vkms_config_crtc **
> +vkms_config_encoder_get_possible_crtcs(struct vkms_config_encoder *encoder_cfg,
> + size_t *out_length)
> +{
> + struct vkms_config_crtc **array;
> + struct vkms_config_crtc *possible_crtc;
> + unsigned long idx;
> + size_t length = 0;
> + int n = 0;
> +
> + xa_for_each(&encoder_cfg->possible_crtcs, idx, possible_crtc)
> + length++;
> +
> + if (length == 0) {
> + *out_length = 0;
> + return NULL;
> + }
> +
> + array = kmalloc_array(length, sizeof(*array), GFP_KERNEL);
> + if (!array)
> + return ERR_PTR(-ENOMEM);
> +
> + xa_for_each(&encoder_cfg->possible_crtcs, idx, possible_crtc) {
> + array[n] = possible_crtc;
> + n++;
> + }
> +
> + *out_length = length;
> + return array;
> +}
Same as before, can we use an iterator?
> diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
[...]
> @@ -98,22 +143,8 @@ int vkms_output_init(struct vkms_device *vkmsdev)
> goto err_free;
> }
>
> - encoder = drmm_kzalloc(dev, sizeof(*encoder), GFP_KERNEL);
> - if (!encoder) {
> - DRM_ERROR("Failed to allocate encoder\n");
> - ret = -ENOMEM;
> - goto err_free;
> - }
> - ret = drmm_encoder_init(dev, encoder, NULL,
> - DRM_MODE_ENCODER_VIRTUAL, NULL);
> - if (ret) {
> - DRM_ERROR("Failed to init encoder\n");
> - goto err_free;
> - }
> - encoder->possible_crtcs = drm_crtc_mask(&crtc_cfgs[0]->crtc->crtc);
> -
> /* Attach the encoder and the connector */
> - ret = drm_connector_attach_encoder(&connector->base, encoder);
> + ret = drm_connector_attach_encoder(&connector->base, encoder_cfgs[0]->encoder);
Why do you only attach the first encoder? Can't we attach all of them?
[...]
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 12/13] drm/vkms: Allow to configure multiple connectors
2025-01-29 11:00 ` [PATCH 12/13] drm/vkms: Allow to configure multiple connectors José Expósito
@ 2025-01-30 13:48 ` Louis Chauvet
0 siblings, 0 replies; 41+ messages in thread
From: Louis Chauvet @ 2025-01-30 13:48 UTC (permalink / raw)
To: José Expósito
Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
tzimmermann, airlied, dri-devel, linux-kernel
On 29/01/25 - 12:00, José Expósito wrote:
> Add a list of connectors to vkms_config and helper functions to add and
> remove as many connectors as wanted.
>
> Unlike planes, CRTCs and encoders, connectors can be added and removed
> once the device is created. To reflect it, add an "enabled" flag in the
> configuration and filter disabled connectors in the
> vkms_config_get_connectors() function.
>
> For the moment, changing the enabled status of the connector has no
> effect after the device is created, but a future patch will add this
> capability.
>
> 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.
>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
Co-developped-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>
Same comments as previous commits:
- I am not fan of the add/destroy naming
- I think it is better to have an iterator to use connectors
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 13/13] drm/vkms: Allow to attach connectors and encoders
2025-01-29 11:00 ` [PATCH 13/13] drm/vkms: Allow to attach connectors and encoders José Expósito
@ 2025-01-30 13:48 ` Louis Chauvet
0 siblings, 0 replies; 41+ messages in thread
From: Louis Chauvet @ 2025-01-30 13:48 UTC (permalink / raw)
To: José Expósito
Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
tzimmermann, airlied, dri-devel, linux-kernel
On 29/01/25 - 12:00, 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.
>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
Co-developped-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 | 62 +++++++++++++++++
> drivers/gpu/drm/vkms/vkms_config.c | 69 +++++++++++++++++++
> drivers/gpu/drm/vkms/vkms_config.h | 33 +++++++++
> drivers/gpu/drm/vkms/vkms_output.c | 57 +++++++++++----
> 4 files changed, 209 insertions(+), 12 deletions(-)
[...]
> +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;
> +
> + xa_for_each(&connector_cfg->possible_encoders, idx, possible_encoder) {
> + if (possible_encoder == encoder_cfg)
> + return -EINVAL;
Is this really an error? For me this should be valid: we want to attach
the connector and the encoder, it is already the case, so it is good.
> + }
> +
> + return xa_alloc(&connector_cfg->possible_encoders, &encoder_idx,
> + encoder_cfg, xa_limit_32b, GFP_KERNEL);
> +}
> +
[...]
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 00/13] drm/vkms: Allow to configure device
2025-01-30 13:48 ` [PATCH 00/13] drm/vkms: Allow to configure device Louis Chauvet
@ 2025-01-31 9:31 ` José Expósito
2025-01-31 13:02 ` Louis Chauvet
0 siblings, 1 reply; 41+ messages in thread
From: José Expósito @ 2025-01-31 9:31 UTC (permalink / raw)
To: Louis Chauvet
Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
tzimmermann, airlied, dri-devel, linux-kernel
On Thu, Jan 30, 2025 at 02:48:10PM +0100, Louis Chauvet wrote:
> On 29/01/25 - 12:00, José Expósito wrote:
> > 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.
>
> Hi José,
Hi Louis,
Thanks a lot for the quick review!
> Thanks a lot!
>
> This series is amazing and better than mine on many points. I have few
> comments:
> - a "strange" naming pair: add/destroy (I expect add/remove or
> create/destroy like other function in DRM)
I used "add" because the function creates and adds a display pipeline
items and "destroy" because the opposite function removes it and frees
its memory, so I wanted to emphasize that the action was destructive.
However, I don't have a strong preference about the naming. If you
prefer another pair of verbs, I'll be happy to change the function
names.
> - usage of "complex" list accessors, can't we just create iterators?
Yes, on the first iteration, I used the underlying structure: list
iterators for planes/CRTCs/encoders/connectors and xa_for_each for
the possible_* items.
However, I found 2 main issues that made me rewrite this code:
The first one is that, if in the future, we change the internal data
type, we'll have to change all the code using it. On this way, like
I did with all the other vkms_config_*_get_*() functions, the data is
encapsulated.
The second one is vkms_config_get_connectors(). Unlike the other
functions, this one filters by connector enabled status. If we let the
caller do the filtering, we'll duplicate that logic.
Because of these two reasons, I decided to add a getter for lists.
> - should we use pr_err in vkms_config_valid?
I think it is great to show to the user a reason why their device couldn't
be enabled in dmesg... But I'm not sure if there is a better way to do it.
> > 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.
>
> To avoid confusion, you should add the Co-developped-by tag, so it will be
> clear that we worked together on this.
Good point, I'll change it.
> > I'm still polishing the ConfigFS code [3] and its IGT tests [4] (connector
> > hot-add/remove bugs) but the IGT tests also exercise this series and can be used
> > for additional test coverage.
>
> I will take a look at those series. For the connector hot-add/remove, do
> you have any example of usage in the kernel? I did not found anything in
> the documentation explaining they are hot-addable.
I pushed a couple of WIP commits to the kernel and IGT so you can see/test
the crashes and hopefully share some ideas.
About the documentation: I didn't find much information other than a few
mentions to hot-add/remove. However, in one of my rebases, two new functions,
drm_connector_dynamic_init() and drm_connector_dynamic_register(), were added:
https://patchwork.freedesktop.org/patch/628418/
I'm still trying to make them work, but I think they are what we need.
Part of the crashes happen on the cleanup of drm_client_setup(). Adding a
connector adds modes in the DRM client, but removing the connector doesn't
remove them and, on cleanup, I get a NULL pointer.
I'm a bit stuck, so help or tips are very welcome :)
> Thanks again for this series,
> Louis Chauvet
I'll look with more time into your comments in the other patches next week.
Thanks,
Jose
> > 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
> >
> > José Expósito (12):
> > drm/vkms: Extract vkms_connector header
> > 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
> >
> > 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 | 782 +++++++++++++++++
> > drivers/gpu/drm/vkms/vkms_config.c | 784 ++++++++++++++++++
> > drivers/gpu/drm/vkms/vkms_config.h | 479 +++++++++++
> > drivers/gpu/drm/vkms/vkms_connector.c | 61 ++
> > drivers/gpu/drm/vkms/vkms_connector.h | 26 +
> > drivers/gpu/drm/vkms/vkms_drv.c | 45 +-
> > drivers/gpu/drm/vkms/vkms_drv.h | 17 +-
> > drivers/gpu/drm/vkms/vkms_output.c | 255 ++++--
> > 12 files changed, 2337 insertions(+), 139 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] 41+ messages in thread
* Re: [PATCH 00/13] drm/vkms: Allow to configure device
2025-01-31 9:31 ` José Expósito
@ 2025-01-31 13:02 ` Louis Chauvet
2025-01-31 17:13 ` José Expósito
0 siblings, 1 reply; 41+ messages in thread
From: Louis Chauvet @ 2025-01-31 13:02 UTC (permalink / raw)
To: José Expósito
Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
tzimmermann, airlied, dri-devel, linux-kernel
On 31/01/25 - 10:31, José Expósito wrote:
> On Thu, Jan 30, 2025 at 02:48:10PM +0100, Louis Chauvet wrote:
> > On 29/01/25 - 12:00, José Expósito wrote:
> > > 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.
> >
> > Hi José,
>
> Hi Louis,
>
> Thanks a lot for the quick review!
>
> > Thanks a lot!
> >
> > This series is amazing and better than mine on many points. I have few
> > comments:
> > - a "strange" naming pair: add/destroy (I expect add/remove or
> > create/destroy like other function in DRM)
>
> I used "add" because the function creates and adds a display pipeline
> items and "destroy" because the opposite function removes it and frees
> its memory, so I wanted to emphasize that the action was destructive.
>
> However, I don't have a strong preference about the naming. If you
> prefer another pair of verbs, I'll be happy to change the function
> names.
So, I think create/destroy is a bit better: `create` with the
`vkms_config` parameter is enough to tell "it allocates stuff in
`vkms_config`".
> > - usage of "complex" list accessors, can't we just create iterators?
>
> Yes, on the first iteration, I used the underlying structure: list
> iterators for planes/CRTCs/encoders/connectors and xa_for_each for
> the possible_* items.
>
> However, I found 2 main issues that made me rewrite this code:
>
> The first one is that, if in the future, we change the internal data
> type, we'll have to change all the code using it. On this way, like
> I did with all the other vkms_config_*_get_*() functions, the data is
> encapsulated.
In one of my comment I proposed a macro to help on this point. I think
this is sufficient to hide internals. (see patch 7/13)
> The second one is vkms_config_get_connectors(). Unlike the other
> functions, this one filters by connector enabled status. If we let the
> caller do the filtering, we'll duplicate that logic.
That something I missed, and a very good point.
I will try to create a macro that do the filtered iteration, if I succeed
and you agree on the previous point, I think it does not worth it to have
those huge amount of code just to iterate over a list.
> Because of these two reasons, I decided to add a getter for lists.
>
> > - should we use pr_err in vkms_config_valid?
>
> I think it is great to show to the user a reason why their device couldn't
> be enabled in dmesg... But I'm not sure if there is a better way to do it.
I was not clear: I agree we want some logs, but pr_err is too
agressive (see patch 8/13).
> > > 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.
> >
> > To avoid confusion, you should add the Co-developped-by tag, so it will be
> > clear that we worked together on this.
>
> Good point, I'll change it.
>
> > > I'm still polishing the ConfigFS code [3] and its IGT tests [4] (connector
> > > hot-add/remove bugs) but the IGT tests also exercise this series and can be used
> > > for additional test coverage.
> >
> > I will take a look at those series. For the connector hot-add/remove, do
> > you have any example of usage in the kernel? I did not found anything in
> > the documentation explaining they are hot-addable.
>
> I pushed a couple of WIP commits to the kernel and IGT so you can see/test
> the crashes and hopefully share some ideas.
>
> About the documentation: I didn't find much information other than a few
> mentions to hot-add/remove. However, in one of my rebases, two new functions,
> drm_connector_dynamic_init() and drm_connector_dynamic_register(), were added:
> https://patchwork.freedesktop.org/patch/628418/
Ho! This is exactly one issue I had when developping IGT tests, sometimes
you fetch the connector list, and when querying info about a specific
connector you get nothing!
> I'm still trying to make them work, but I think they are what we need.
After reading the patch "https://patchwork.freedesktop.org/patch/628418/",
I don't think we really need to support "dynamic connector creation" right
now:
- None of the existing driver do it (except MST, but MST need a lot of
stuff to works)
- If we want to support it later, just create a "useless"
/configfs/vkms/DEV/connectors/CON/enable that you must write 1 before
the device initialization to make the connector working. This way we
will not have to break the UAPI (the file is already there, disabled by
default)
> Part of the crashes happen on the cleanup of drm_client_setup(). Adding a
> connector adds modes in the DRM client, but removing the connector doesn't
> remove them and, on cleanup, I get a NULL pointer.
>
> I'm a bit stuck, so help or tips are very welcome :)
I will look at it next week (same repo/branch?).
Have a nice weekend,
Louis Chauvet
>
> > Thanks again for this series,
> > Louis Chauvet
>
> I'll look with more time into your comments in the other patches next week.
>
> Thanks,
> Jose
>
[...]
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 00/13] drm/vkms: Allow to configure device
2025-01-31 13:02 ` Louis Chauvet
@ 2025-01-31 17:13 ` José Expósito
0 siblings, 0 replies; 41+ messages in thread
From: José Expósito @ 2025-01-31 17:13 UTC (permalink / raw)
To: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
tzimmermann, airlied, dri-devel, linux-kernel
On Fri, Jan 31, 2025 at 02:02:14PM +0100, Louis Chauvet wrote:
> On 31/01/25 - 10:31, José Expósito wrote:
> > On Thu, Jan 30, 2025 at 02:48:10PM +0100, Louis Chauvet wrote:
> > > On 29/01/25 - 12:00, José Expósito wrote:
> > > > 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.
> > >
> > > Hi José,
> >
> > Hi Louis,
> >
> > Thanks a lot for the quick review!
> >
> > > Thanks a lot!
> > >
> > > This series is amazing and better than mine on many points. I have few
> > > comments:
> > > - a "strange" naming pair: add/destroy (I expect add/remove or
> > > create/destroy like other function in DRM)
> >
> > I used "add" because the function creates and adds a display pipeline
> > items and "destroy" because the opposite function removes it and frees
> > its memory, so I wanted to emphasize that the action was destructive.
> >
> > However, I don't have a strong preference about the naming. If you
> > prefer another pair of verbs, I'll be happy to change the function
> > names.
>
> So, I think create/destroy is a bit better: `create` with the
> `vkms_config` parameter is enough to tell "it allocates stuff in
> `vkms_config`".
ACK, I'll change in v2.
> > > - usage of "complex" list accessors, can't we just create iterators?
> >
> > Yes, on the first iteration, I used the underlying structure: list
> > iterators for planes/CRTCs/encoders/connectors and xa_for_each for
> > the possible_* items.
> >
> > However, I found 2 main issues that made me rewrite this code:
> >
> > The first one is that, if in the future, we change the internal data
> > type, we'll have to change all the code using it. On this way, like
> > I did with all the other vkms_config_*_get_*() functions, the data is
> > encapsulated.
>
> In one of my comment I proposed a macro to help on this point. I think
> this is sufficient to hide internals. (see patch 7/13)
That's a very good point, I like the vkms_config_for_each_plane()
macro you suggested. Also, it matches really well with similar macros
in drm, for example drm_connector_for_each_possible_encoder().
I'll add them in v2.
> > The second one is vkms_config_get_connectors(). Unlike the other
> > functions, this one filters by connector enabled status. If we let the
> > caller do the filtering, we'll duplicate that logic.
>
> That something I missed, and a very good point.
>
> I will try to create a macro that do the filtered iteration, if I succeed
> and you agree on the previous point, I think it does not worth it to have
> those huge amount of code just to iterate over a list.
>
> > Because of these two reasons, I decided to add a getter for lists.
> >
> > > - should we use pr_err in vkms_config_valid?
> >
> > I think it is great to show to the user a reason why their device couldn't
> > be enabled in dmesg... But I'm not sure if there is a better way to do it.
>
> I was not clear: I agree we want some logs, but pr_err is too
> agressive (see patch 8/13).
Agreed, we can lower to warn.
> > > > 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.
> > >
> > > To avoid confusion, you should add the Co-developped-by tag, so it will be
> > > clear that we worked together on this.
> >
> > Good point, I'll change it.
> >
> > > > I'm still polishing the ConfigFS code [3] and its IGT tests [4] (connector
> > > > hot-add/remove bugs) but the IGT tests also exercise this series and can be used
> > > > for additional test coverage.
> > >
> > > I will take a look at those series. For the connector hot-add/remove, do
> > > you have any example of usage in the kernel? I did not found anything in
> > > the documentation explaining they are hot-addable.
> >
> > I pushed a couple of WIP commits to the kernel and IGT so you can see/test
> > the crashes and hopefully share some ideas.
> >
> > About the documentation: I didn't find much information other than a few
> > mentions to hot-add/remove. However, in one of my rebases, two new functions,
> > drm_connector_dynamic_init() and drm_connector_dynamic_register(), were added:
> > https://patchwork.freedesktop.org/patch/628418/
>
> Ho! This is exactly one issue I had when developping IGT tests, sometimes
> you fetch the connector list, and when querying info about a specific
> connector you get nothing!
I'm not sure if it is the same issue, but when it happened to me, I was
missing a call to drm_mode_config_reset(). Credit to Maxime for helping
me with this error :)
> > I'm still trying to make them work, but I think they are what we need.
>
> After reading the patch "https://patchwork.freedesktop.org/patch/628418/",
> I don't think we really need to support "dynamic connector creation" right
> now:
> - None of the existing driver do it (except MST, but MST need a lot of
> stuff to works)
> - If we want to support it later, just create a "useless"
> /configfs/vkms/DEV/connectors/CON/enable that you must write 1 before
> the device initialization to make the connector working. This way we
> will not have to break the UAPI (the file is already there, disabled by
> default)
Fair enough, we can add it in the future. To keep it backwars compatible
though, we'll need to enable by default "static" connectors so users don't
need to change the way they create devices.
For "dynamic" connectors (connectors created once the device is enabled) we
would need to set them disabled on creation, but this would be backwards
compatible, as we won't allow creating new connectors in the first version.
I'll remove the connector hot-add/remove on v2... Meaning that configfs + IGT
will be ready for review :)
> > Part of the crashes happen on the cleanup of drm_client_setup(). Adding a
> > connector adds modes in the DRM client, but removing the connector doesn't
> > remove them and, on cleanup, I get a NULL pointer.
> >
> > I'm a bit stuck, so help or tips are very welcome :)
>
> I will look at it next week (same repo/branch?).
Yes, same repo.
Enjoy your weekend!
Jose
> Have a nice weekend,
> Louis Chauvet
>
> >
> > > Thanks again for this series,
> > > Louis Chauvet
> >
> > I'll look with more time into your comments in the other patches next week.
> >
> > Thanks,
> > Jose
> >
>
> [...]
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 01/13] drm/vkms: Extract vkms_connector header
2025-01-30 13:48 ` Louis Chauvet
@ 2025-02-11 10:37 ` José Expósito
0 siblings, 0 replies; 41+ messages in thread
From: José Expósito @ 2025-02-11 10:37 UTC (permalink / raw)
To: Louis Chauvet
Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
tzimmermann, airlied, dri-devel, linux-kernel
Hi Louis,
First of all, thanks a lot for the review.
I applied all changes you suggested and, especially thanks to the
iterators you suggested, the code is way cleaner and simpler now.
I'll send v2 in a bit, but first I'll comment on some of your
reviews to clarify some porints:
On Thu, Jan 30, 2025 at 02:48:10PM +0100, Louis Chauvet wrote:
> On 29/01/25 - 12:00, José Expósito wrote:
> > 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.
> >
> > Signed-off-by: José Expósito <jose.exposito89@gmail.com>
>
> Non blocking issue: Do you think it is possible to split this in two
> different commits: one to create vkms_connector.h/c and one to create
> struct vkms_connector?
Done, I split this patch in 2 in v2.
>
> Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
>
> [...]
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 03/13] drm/vkms: Extract vkms_config header
2025-01-30 13:48 ` Louis Chauvet
@ 2025-02-11 10:39 ` José Expósito
0 siblings, 0 replies; 41+ messages in thread
From: José Expósito @ 2025-02-11 10:39 UTC (permalink / raw)
To: Louis Chauvet
Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
tzimmermann, airlied, dri-devel, linux-kernel
On Thu, Jan 30, 2025 at 02:48:13PM +0100, Louis Chauvet wrote:
> On 29/01/25 - 12:00, 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.
> >
> > Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> > Signed-off-by: José Expósito <jose.exposito89@gmail.com>
>
> Co-developped-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>
>
> [...]
>
> > diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c
> > @@ -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);
> >
> > default_config = config;
> >
> > @@ -243,7 +223,7 @@ static int __init vkms_init(void)
> >
> > ret = vkms_create(config);
> > if (ret)
> > - kfree(config);
> > + vkms_config_destroy(config);
>
> I just have a question here: is it not a problem to kfree config (and
> default_config) here? There is not risk to have a
> use-after-free/double-free in vkms_exit?
>
> > return ret;
> > }
> > @@ -272,7 +252,7 @@ static void __exit vkms_exit(void)
> > if (default_config->dev)
>
> The use-after-free may be here?
>
> > vkms_destroy(default_config);
> >
> > - kfree(default_config);
> > + vkms_config_destroy(default_config);
>
> And maybe double-free?
>
> > }
>
> If this is not an issue (ie we have a garantee that vkms_exit is never
> called if vkms_init fails), you can add my
Good catch! This is a potential use after free/double free or, even worst,
on "if (default_config->dev)" default_config could be NULL.
Even though the bug is unrelated to this series (it was already there) I'll
include a fix in v2.
It'll be the first patch of the series and it could be merged independently.
Thanks,
Jose
>
> Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
>
> [...]
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 07/13] drm/vkms: Allow to configure multiple planes
2025-01-30 13:48 ` Louis Chauvet
@ 2025-02-11 10:43 ` José Expósito
2025-02-12 14:10 ` Louis Chauvet
0 siblings, 1 reply; 41+ messages in thread
From: José Expósito @ 2025-02-11 10:43 UTC (permalink / raw)
To: Louis Chauvet
Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
tzimmermann, airlied, dri-devel, linux-kernel
On Thu, Jan 30, 2025 at 02:48:19PM +0100, Louis Chauvet wrote:
> On 29/01/25 - 12:00, 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.
> >
> > Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> > Signed-off-by: José Expósito <jose.exposito89@gmail.com>
>
> Co-developped-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>
>
> [...]
>
> > diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
>
> [...]
>
> > +static void vkms_config_test_get_planes(struct kunit *test)
> > +{
> > + struct vkms_config *config;
> > + struct vkms_config_plane *plane_cfg1, *plane_cfg2;
> > + struct vkms_config_plane **array;
> > + size_t length;
> > +
> > + config = vkms_config_create("test");
> > + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
> > +
> > + array = vkms_config_get_planes(config, &length);
> > + KUNIT_ASSERT_EQ(test, length, 0);
> > + KUNIT_ASSERT_NULL(test, array);
> > +
> > + plane_cfg1 = vkms_config_add_plane(config);
> > + array = vkms_config_get_planes(config, &length);
> > + KUNIT_ASSERT_EQ(test, length, 1);
> > + KUNIT_ASSERT_PTR_EQ(test, array[0], plane_cfg1);
> > + kfree(array);
> > +
> > + plane_cfg2 = vkms_config_add_plane(config);
> > + array = vkms_config_get_planes(config, &length);
> > + KUNIT_ASSERT_EQ(test, length, 2);
> > + KUNIT_ASSERT_PTR_EQ(test, array[0], plane_cfg1);
> > + KUNIT_ASSERT_PTR_EQ(test, array[1], plane_cfg2);
> > + kfree(array);
> > +
> > + vkms_config_destroy_plane(plane_cfg1);
> > + array = vkms_config_get_planes(config, &length);
> > + KUNIT_ASSERT_EQ(test, length, 1);
> > + KUNIT_ASSERT_PTR_EQ(test, array[0], plane_cfg2);
> > + kfree(array);
> > +
> > + vkms_config_destroy(config);
> > +}
>
> In this test I have the feeling that vkms_config_get_planes always returns
> a predictable order. It is maybe trivial here, but I would prefer to shows
> that the order is not stable, for example:
>
> bool plane_cfg1_found = false;
> bool plane_cfg2_found = false;
>
> vkms_config_for_each_plane(config, plane_cfg) {
> if (plane_cfg == plane_cfg1)
> plane_cfg1_found = true;
> else if (plane_cfg == plane_cfg2)
> plane_cfg2_found = true;
> else
> KUNIT_FAILS("Unexpected plane");
> }
>
> KUNIT_ASSERT(test, plane_cfg1_found);
> KUNIT_ASSERT(test, plane_cfg2_found);
>
> [...]
>
> > +static void vkms_config_test_valid_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_add_plane(config);
> > +
> > + KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
> > +
> > + vkms_config_destroy(config);
> > +}
>
> For this function, the naming is a bit strange, it says
> "valid_plane_number", but you test only invalid plane number.
The reason for this naming is that it tests the valid_plane_number()
function called by vkms_config_is_valid(). The applies for the other
valid_* tests.
However, I don't mind changing its name to so it reflects the test
rather than the tested function.
Changed in v2.
>
> Can you rename it to vkms_config_test_invalid_plane_number?
>
> [...]
>
> > diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
>
> [...]
>
> > +struct vkms_config_plane **vkms_config_get_planes(const struct vkms_config *config,
> > + size_t *out_length)
> > +{
> > + struct vkms_config_plane **array;
> > + struct vkms_config_plane *plane_cfg;
> > + size_t length;
> > + int n = 0;
> > +
> > + length = list_count_nodes((struct list_head *)&config->planes);
> > + if (length == 0) {
> > + *out_length = length;
> > + return NULL;
> > + }
> > +
> > + array = kmalloc_array(length, sizeof(*array), GFP_KERNEL);
> > + if (!array)
> > + return ERR_PTR(-ENOMEM);
> > +
> > + list_for_each_entry(plane_cfg, &config->planes, link) {
> > + array[n] = plane_cfg;
> > + n++;
> > + }
> > +
> > + *out_length = length;
> > + return array;
> > +}
>
> To join the comment on the test, I am not a big fan of creating a new list
> to return to the caller, for three reasons:
> - the caller needs to manage an other pointer;
> - the caller needs to understand that the content of the array is only
> valid if: the config is not freed, nobody else removed anything from the
> planes;
> - the caller may think this list always have the same order if he looks at
> the tests.
>
> I would prefer a simple macro to do an iteration over the config->planes
> list: (I did not test this macro, but you have this idea)
>
> #define vkms_config_iter_plane(config, plane_cfg) \
> list_for_each_entry((plane_cfg), &(config).planes, link)
>
> This way:
> - no new pointer to manage;
> - if one day we have concurency issue, we just have to protect config, not
> config+all the planes;
> - there is no expected order.
>
> [...]
>
> > 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;
> > }
>
> I really like the idea to split the validation function, way simpler!
>
> [...]
>
> > +void vkms_config_destroy_plane(struct vkms_config_plane *plane_cfg)
> > +{
> > + list_del(&plane_cfg->link);
> > + kfree(plane_cfg);
> > +}
>
> I would prefer a "standard" function pair, i.e.: add/remove or
> create/destroy, not add/destroy.
>
> For me it should be create/destroy, you create the plane by using a
> config, so it is clear it will be attached to it.
>
> If you choose add/remove, you should explains in the documentation that
> remove is also doing kfree.
>
> [...]
>
> > diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
>
> [...]
>
> > @@ -11,61 +11,63 @@ 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;
> > - int ret;
> > + struct vkms_plane *primary = NULL, *cursor = NULL;
> > + struct vkms_config_plane **plane_cfgs = NULL;
> > + size_t n_planes;
> > + int ret = 0;
> > int writeback;
> > unsigned int n;
>
> I think it could be interesting to have a vkms_config_is_valid call here.
> It will avoid raising DRM errors or create unexpected devices.
>
> It will also garantee in a later patch that
> vkms_config_crtc_get_primary_plane is a valid pointer.
>
> > - /*
> > - * 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);
> > + plane_cfgs = vkms_config_get_planes(vkmsdev->config, &n_planes);
> > + if (IS_ERR(plane_cfgs))
> > + return PTR_ERR(plane_cfgs);
>
> If you agree on the iterator implementation, this code could be simplified
> a lot.
>
> > - if (vkmsdev->config->cursor) {
> > - cursor = vkms_plane_init(vkmsdev, DRM_PLANE_TYPE_CURSOR);
> > - if (IS_ERR(cursor))
> > - return PTR_ERR(cursor);
> > + for (n = 0; n < n_planes; n++) {
> > + struct vkms_config_plane *plane_cfg;
> > + enum drm_plane_type type;
> > +
> > + plane_cfg = plane_cfgs[n];
> > + type = vkms_config_plane_get_type(plane_cfg);
> > +
> > + plane_cfg->plane = vkms_plane_init(vkmsdev, type);
>
> Can we pass plane_cfg in vkms_plane_init? This way we don't have to
> touch vkms_output_init when adding new vkms_config_plane members.
While it'll be required once we allow to configure more parameters, I don't
think we need it right now. To keep things as simple as possible, I'd prefer to
delay it until required.
Thanks,
Jose
> > + if (IS_ERR(plane_cfg->plane)) {
> > + DRM_DEV_ERROR(dev->dev, "Failed to init vkms plane\n");
> > + ret = PTR_ERR(plane_cfg->plane);
> > + goto err_free;
> > + }
> > +
> > + if (type == DRM_PLANE_TYPE_PRIMARY)
> > + primary = plane_cfg->plane;
> > + else if (type == DRM_PLANE_TYPE_CURSOR)
> > + cursor = plane_cfg->plane;
> > }
>
> [...]
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 08/13] drm/vkms: Allow to configure multiple CRTCs
2025-01-30 13:48 ` Louis Chauvet
@ 2025-02-11 10:44 ` José Expósito
2025-02-12 14:12 ` Louis Chauvet
0 siblings, 1 reply; 41+ messages in thread
From: José Expósito @ 2025-02-11 10:44 UTC (permalink / raw)
To: Louis Chauvet
Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
tzimmermann, airlied, dri-devel, linux-kernel
On Thu, Jan 30, 2025 at 02:48:20PM +0100, Louis Chauvet wrote:
> On 29/01/25 - 12:00, 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.
> >
> > Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> > Signed-off-by: José Expósito <jose.exposito89@gmail.com>
>
> Co-developped-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>
>
> [...]
>
> > diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
>
> [...]
>
> > +static void vkms_config_test_valid_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_add_crtc(config);
> > +
> > + KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
> > +
> > + vkms_config_destroy(config);
> > +}
>
> Same as before, can you rename the fonction to
> vkms_config_test_invalid_crtc_number
>
> [...]
>
> > diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
>
> [...]
>
> > +struct vkms_config_crtc **vkms_config_get_crtcs(const struct vkms_config *config,
> > + size_t *out_length)
> > +{
> > + struct vkms_config_crtc **array;
> > + struct vkms_config_crtc *crtc_cfg;
> > + size_t length;
> > + int n = 0;
> > +
> > + length = list_count_nodes((struct list_head *)&config->crtcs);
> > + if (length == 0) {
> > + *out_length = length;
> > + return NULL;
> > + }
> > +
> > + array = kmalloc_array(length, sizeof(*array), GFP_KERNEL);
> > + if (!array)
> > + return ERR_PTR(-ENOMEM);
> > +
> > + list_for_each_entry(crtc_cfg, &config->crtcs, link) {
> > + array[n] = crtc_cfg;
> > + n++;
> > + }
> > +
> > + *out_length = length;
> > + return array;
> > +}
>
> Same as before, can't we use an iterator?
>
> [...]
>
> > +static bool valid_crtc_number(struct vkms_config *config)
> > +{
> > + size_t n_crtcs;
> > +
> > + n_crtcs = list_count_nodes(&config->crtcs);
> > + if (n_crtcs <= 0 || n_crtcs >= 32) {
> > + pr_err("The number of CRTCs must be between 1 and 31\n");
>
> I agree we need some logs, but I think pr_err is too agressive (i.e may
> be considered as an error by some test tools).
>
> I think we should at least:
> - lower to warn/notice/info
> - use drm variants of the macro
>
> > + return false;
> > + }
> > +
> > + return true;
> > +}
> > +
>
> [...]
>
> > +struct vkms_config_crtc *vkms_config_add_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);
> > +}
>
> Same as before, the pair add/destroy seems strange.
>
> > +++ 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));
>
> At this point we only create one crtc, can you move this change in the
> commit where you create multiple crtc?
Since the code to add more than one CRTCs is unused but technically present, I
think that this is the right patch to use this function.
Anyway, at the moment it'll always return 1, so it is a no-op.
I didn't change it in v2, let me know if it works for you.
Thanks,
Jose
> > if (ret) {
> > DRM_ERROR("Failed to vblank\n");
> > goto out_devres;
> > --
> > 2.48.1
> >
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 09/13] drm/vkms: Allow to attach planes and CRTCs
2025-01-30 13:48 ` Louis Chauvet
@ 2025-02-11 10:47 ` José Expósito
2025-02-12 14:10 ` Louis Chauvet
0 siblings, 1 reply; 41+ messages in thread
From: José Expósito @ 2025-02-11 10:47 UTC (permalink / raw)
To: Louis Chauvet
Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
tzimmermann, airlied, dri-devel, linux-kernel
On Thu, Jan 30, 2025 at 02:48:21PM +0100, Louis Chauvet wrote:
> On 29/01/25 - 12:00, José Expósito wrote:
> > 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.
> >
> > Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> > Signed-off-by: José Expósito <jose.exposito89@gmail.com>
>
> Co-developped-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>
>
> [...]
>
> > diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
>
> [...]
>
> > -static bool valid_plane_type(struct vkms_config *config)
> > +static bool valid_plane_type(struct vkms_config *config,
> > + struct vkms_config_crtc *crtc_cfg)
>
> What do you think about renaming it to "valid_planes_for_crtc" to reflect
> the fact you tests if a CRTC is attached to a valid combination of planes?
>
> > {
> > struct vkms_config_plane *plane_cfg;
> > bool has_primary_plane = false;
> > bool has_cursor_plane = false;
> >
> > list_for_each_entry(plane_cfg, &config->planes, link) {
> > + 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) {
> > - pr_err("Multiple primary planes\n");
> > - return false;
> > - }
> > + xa_for_each(&plane_cfg->possible_crtcs, idx, possible_crtc) {
> > + if (possible_crtc != crtc_cfg)
> > + continue;
> >
> > - has_primary_plane = true;
> > - } else if (type == DRM_PLANE_TYPE_CURSOR) {
> > - if (has_cursor_plane) {
> > - pr_err("Multiple cursor planes\n");
> > - return false;
> > - }
> > + if (type == DRM_PLANE_TYPE_PRIMARY) {
> > + if (has_primary_plane) {
> > + pr_err("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) {
> > + pr_err("Multiple cursor planes\n");
> > + return false;
> > + }
> > +
> > + has_cursor_plane = true;
> > + }
> > }
> > }
>
> [...]
>
> > +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;
> > +
> > + xa_for_each(&plane_cfg->possible_crtcs, idx, possible_crtc) {
> > + if (possible_crtc == crtc_cfg)
> > + return -EINVAL;
>
> Is it really an error? After this call, we expect plane and crtc to be
> attached, so if the plane is already attached, I don't see any issue.
In my opinion, this could be either handled as an error or not. I think that
there are arguments for both approaches but, for our use case, I think that it
is better to return an error.
Since the main (and for the moment only) user of this function will be configfs,
it is very convenient to return an error to avoid creating 2 links between
plane <-> crtc.
If we allow to create multiple links, and the user deletes one of them, the
items would be still linked, which is a bit unexpected.
The same applies to the other vkms_config_*_attach_* functions.
For these reasons, I didn't change it in v2, let me know your opinion.
Jose
> > + }
> > +
> > + return xa_alloc(&plane_cfg->possible_crtcs, &crtc_idx, crtc_cfg,
> > + xa_limit_32b, GFP_KERNEL);
> > +}
> > +
>
> [...]
>
> > +struct vkms_config_crtc **vkms_config_plane_get_possible_crtcs(struct vkms_config_plane *plane_cfg,
> > + size_t *out_length)
> > +{
> > + struct vkms_config_crtc **array;
> > + struct vkms_config_crtc *possible_crtc;
> > + unsigned long idx;
> > + size_t length = 0;
> > + int n = 0;
> > +
> > + xa_for_each(&plane_cfg->possible_crtcs, idx, possible_crtc)
> > + length++;
> > +
> > + if (length == 0) {
> > + *out_length = length;
> > + return NULL;
> > + }
> > +
> > + array = kmalloc_array(length, sizeof(*array), GFP_KERNEL);
> > + if (!array)
> > + return ERR_PTR(-ENOMEM);
> > +
> > + xa_for_each(&plane_cfg->possible_crtcs, idx, possible_crtc) {
> > + array[n] = possible_crtc;
> > + n++;
> > + }
> > +
> > + *out_length = length;
> > + return array;
> > +}
>
> Same as before, can we use an iterator?
>
> > +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)
>
> Even if this is a private function, can we add a comment explaning that
> the returned value is only one of the available planes of this type?
>
> /**
> * vkms_config_crtc_get_plane() - Get the first attached plane
> * found of a specific type
> * @config: configuration containing the crtc and the planes
> * @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.
> */
>
> > +{
> > + struct vkms_config_plane *plane_cfg;
> > + struct vkms_config_crtc *possible_crtc;
> > + enum drm_plane_type current_type;
> > + unsigned long idx;
> > +
> > + list_for_each_entry(plane_cfg, &config->planes, link) {
> > + current_type = vkms_config_plane_get_type(plane_cfg);
> > +
> > + xa_for_each(&plane_cfg->possible_crtcs, idx, possible_crtc) {
> > + if (possible_crtc == crtc_cfg && current_type == type)
> > + return plane_cfg;
> > + }
> > + }
> > +
> > + return NULL;
> > +}
>
> [...]
>
> > diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
>
> [...]
>
> > +/**
> > + * vkms_config_crtc_primary_plane() - Return the primary plane for a CRTC
> > + * @config: Configuration containing the CRTC
> > + * @crtc_config: Target CRTC
> > + *
> > + * Returns:
> > + * The primary plane or NULL if none is assigned yet.
> > + */
>
> Same as above, can you speficy that it is one of the primary plane?
>
> > +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
>
> Ditto
>
> [...]
>
> > diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
>
> [...]
>
> > @@ -35,19 +41,54 @@ int vkms_output_init(struct vkms_device *vkmsdev)
> > ret = PTR_ERR(plane_cfg->plane);
> > goto err_free;
> > }
> > + }
> > +
> > + for (n = 0; n < n_crtcs; n++) {
> > + struct vkms_config_crtc *crtc_cfg;
> > + struct vkms_config_plane *primary, *cursor;
> > +
> > + crtc_cfg = crtc_cfgs[n];
> > + primary = vkms_config_crtc_primary_plane(vkmsdev->config, crtc_cfg);
> > + cursor = vkms_config_crtc_cursor_plane(vkmsdev->config, crtc_cfg);
>
> Linked with a previous comment: here we have no garantee that primary is a
> valid pointer, can we check it or call vkms_config_is_valid to ensure it?
>
> > + 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");
> > + ret = PTR_ERR(crtc_cfg->crtc);
> > + goto err_free;
> > + }
>
> [...]
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 07/13] drm/vkms: Allow to configure multiple planes
2025-02-11 10:43 ` José Expósito
@ 2025-02-12 14:10 ` Louis Chauvet
0 siblings, 0 replies; 41+ messages in thread
From: Louis Chauvet @ 2025-02-12 14:10 UTC (permalink / raw)
To: José Expósito
Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
tzimmermann, airlied, dri-devel, linux-kernel
Le 11/02/2025 à 11:43, José Expósito a écrit :
> On Thu, Jan 30, 2025 at 02:48:19PM +0100, Louis Chauvet wrote:
>> On 29/01/25 - 12:00, 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.
>>>
>>> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
>>> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
>>
>> Co-developped-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>
>>
>> [...]
>>
>>> diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
>>
>> [...]
>>
>>> +static void vkms_config_test_get_planes(struct kunit *test)
>>> +{
>>> + struct vkms_config *config;
>>> + struct vkms_config_plane *plane_cfg1, *plane_cfg2;
>>> + struct vkms_config_plane **array;
>>> + size_t length;
>>> +
>>> + config = vkms_config_create("test");
>>> + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
>>> +
>>> + array = vkms_config_get_planes(config, &length);
>>> + KUNIT_ASSERT_EQ(test, length, 0);
>>> + KUNIT_ASSERT_NULL(test, array);
>>> +
>>> + plane_cfg1 = vkms_config_add_plane(config);
>>> + array = vkms_config_get_planes(config, &length);
>>> + KUNIT_ASSERT_EQ(test, length, 1);
>>> + KUNIT_ASSERT_PTR_EQ(test, array[0], plane_cfg1);
>>> + kfree(array);
>>> +
>>> + plane_cfg2 = vkms_config_add_plane(config);
>>> + array = vkms_config_get_planes(config, &length);
>>> + KUNIT_ASSERT_EQ(test, length, 2);
>>> + KUNIT_ASSERT_PTR_EQ(test, array[0], plane_cfg1);
>>> + KUNIT_ASSERT_PTR_EQ(test, array[1], plane_cfg2);
>>> + kfree(array);
>>> +
>>> + vkms_config_destroy_plane(plane_cfg1);
>>> + array = vkms_config_get_planes(config, &length);
>>> + KUNIT_ASSERT_EQ(test, length, 1);
>>> + KUNIT_ASSERT_PTR_EQ(test, array[0], plane_cfg2);
>>> + kfree(array);
>>> +
>>> + vkms_config_destroy(config);
>>> +}
>>
>> In this test I have the feeling that vkms_config_get_planes always returns
>> a predictable order. It is maybe trivial here, but I would prefer to shows
>> that the order is not stable, for example:
>>
>> bool plane_cfg1_found = false;
>> bool plane_cfg2_found = false;
>>
>> vkms_config_for_each_plane(config, plane_cfg) {
>> if (plane_cfg == plane_cfg1)
>> plane_cfg1_found = true;
>> else if (plane_cfg == plane_cfg2)
>> plane_cfg2_found = true;
>> else
>> KUNIT_FAILS("Unexpected plane");
>> }
>>
>> KUNIT_ASSERT(test, plane_cfg1_found);
>> KUNIT_ASSERT(test, plane_cfg2_found);
>>
>> [...]
>>
>>> +static void vkms_config_test_valid_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_add_plane(config);
>>> +
>>> + KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
>>> +
>>> + vkms_config_destroy(config);
>>> +}
>>
>> For this function, the naming is a bit strange, it says
>> "valid_plane_number", but you test only invalid plane number.
>
> The reason for this naming is that it tests the valid_plane_number()
> function called by vkms_config_is_valid(). The applies for the other
> valid_* tests.
Hoo, I see, okk!
> However, I don't mind changing its name to so it reflects the test
> rather than the tested function.
I prefer an "implementation independent" name, as the content of
vkms_config_is_valid may change over time.
> Changed in v2.
Perfect!
>>
>> Can you rename it to vkms_config_test_invalid_plane_number?
>>
>> [...]
>>
>>> diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
>>
>> [...]
>>
>>> +struct vkms_config_plane **vkms_config_get_planes(const struct vkms_config *config,
>>> + size_t *out_length)
>>> +{
>>> + struct vkms_config_plane **array;
>>> + struct vkms_config_plane *plane_cfg;
>>> + size_t length;
>>> + int n = 0;
>>> +
>>> + length = list_count_nodes((struct list_head *)&config->planes);
>>> + if (length == 0) {
>>> + *out_length = length;
>>> + return NULL;
>>> + }
>>> +
>>> + array = kmalloc_array(length, sizeof(*array), GFP_KERNEL);
>>> + if (!array)
>>> + return ERR_PTR(-ENOMEM);
>>> +
>>> + list_for_each_entry(plane_cfg, &config->planes, link) {
>>> + array[n] = plane_cfg;
>>> + n++;
>>> + }
>>> +
>>> + *out_length = length;
>>> + return array;
>>> +}
>>
>> To join the comment on the test, I am not a big fan of creating a new list
>> to return to the caller, for three reasons:
>> - the caller needs to manage an other pointer;
>> - the caller needs to understand that the content of the array is only
>> valid if: the config is not freed, nobody else removed anything from the
>> planes;
>> - the caller may think this list always have the same order if he looks at
>> the tests.
>>
>> I would prefer a simple macro to do an iteration over the config->planes
>> list: (I did not test this macro, but you have this idea)
>>
>> #define vkms_config_iter_plane(config, plane_cfg) \
>> list_for_each_entry((plane_cfg), &(config).planes, link)
>>
>> This way:
>> - no new pointer to manage;
>> - if one day we have concurency issue, we just have to protect config, not
>> config+all the planes;
>> - there is no expected order.
>>
>> [...]
>>
>>> 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;
>>> }
>>
>> I really like the idea to split the validation function, way simpler!
>>
>> [...]
>>
>>> +void vkms_config_destroy_plane(struct vkms_config_plane *plane_cfg)
>>> +{
>>> + list_del(&plane_cfg->link);
>>> + kfree(plane_cfg);
>>> +}
>>
>> I would prefer a "standard" function pair, i.e.: add/remove or
>> create/destroy, not add/destroy.
>>
>> For me it should be create/destroy, you create the plane by using a
>> config, so it is clear it will be attached to it.
>>
>> If you choose add/remove, you should explains in the documentation that
>> remove is also doing kfree.
>>
>> [...]
>>
>>> diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
>>
>> [...]
>>
>>> @@ -11,61 +11,63 @@ 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;
>>> - int ret;
>>> + struct vkms_plane *primary = NULL, *cursor = NULL;
>>> + struct vkms_config_plane **plane_cfgs = NULL;
>>> + size_t n_planes;
>>> + int ret = 0;
>>> int writeback;
>>> unsigned int n;
>>
>> I think it could be interesting to have a vkms_config_is_valid call here.
>> It will avoid raising DRM errors or create unexpected devices.
>>
>> It will also garantee in a later patch that
>> vkms_config_crtc_get_primary_plane is a valid pointer.
>>
>>> - /*
>>> - * 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);
>>> + plane_cfgs = vkms_config_get_planes(vkmsdev->config, &n_planes);
>>> + if (IS_ERR(plane_cfgs))
>>> + return PTR_ERR(plane_cfgs);
>>
>> If you agree on the iterator implementation, this code could be simplified
>> a lot.
>>
>>> - if (vkmsdev->config->cursor) {
>>> - cursor = vkms_plane_init(vkmsdev, DRM_PLANE_TYPE_CURSOR);
>>> - if (IS_ERR(cursor))
>>> - return PTR_ERR(cursor);
>>> + for (n = 0; n < n_planes; n++) {
>>> + struct vkms_config_plane *plane_cfg;
>>> + enum drm_plane_type type;
>>> +
>>> + plane_cfg = plane_cfgs[n];
>>> + type = vkms_config_plane_get_type(plane_cfg);
>>> +
>>> + plane_cfg->plane = vkms_plane_init(vkmsdev, type);
>>
>> Can we pass plane_cfg in vkms_plane_init? This way we don't have to
>> touch vkms_output_init when adding new vkms_config_plane members.
>
> While it'll be required once we allow to configure more parameters, I don't
> think we need it right now. To keep things as simple as possible, I'd prefer to
> delay it until required.
I understand your point, especially since your patch don't add new
parameters to vkms_plane_init.
Thanks!
> Thanks,
> Jose
>
>>> + if (IS_ERR(plane_cfg->plane)) {
>>> + DRM_DEV_ERROR(dev->dev, "Failed to init vkms plane\n");
>>> + ret = PTR_ERR(plane_cfg->plane);
>>> + goto err_free;
>>> + }
>>> +
>>> + if (type == DRM_PLANE_TYPE_PRIMARY)
>>> + primary = plane_cfg->plane;
>>> + else if (type == DRM_PLANE_TYPE_CURSOR)
>>> + cursor = plane_cfg->plane;
>>> }
>>
>> [...]
--
Louis Chauvet, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 09/13] drm/vkms: Allow to attach planes and CRTCs
2025-02-11 10:47 ` José Expósito
@ 2025-02-12 14:10 ` Louis Chauvet
2025-02-13 15:32 ` José Expósito
0 siblings, 1 reply; 41+ messages in thread
From: Louis Chauvet @ 2025-02-12 14:10 UTC (permalink / raw)
To: José Expósito
Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
tzimmermann, airlied, dri-devel, linux-kernel
Le 11/02/2025 à 11:47, José Expósito a écrit :
> On Thu, Jan 30, 2025 at 02:48:21PM +0100, Louis Chauvet wrote:
>> On 29/01/25 - 12:00, José Expósito wrote:
>>> 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.
>>>
>>> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
>>> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
>>
>> Co-developped-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>
>>
>> [...]
>>
>>> diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
>>
>> [...]
>>
>>> -static bool valid_plane_type(struct vkms_config *config)
>>> +static bool valid_plane_type(struct vkms_config *config,
>>> + struct vkms_config_crtc *crtc_cfg)
>>
>> What do you think about renaming it to "valid_planes_for_crtc" to reflect
>> the fact you tests if a CRTC is attached to a valid combination of planes?
>>
>>> {
>>> struct vkms_config_plane *plane_cfg;
>>> bool has_primary_plane = false;
>>> bool has_cursor_plane = false;
>>>
>>> list_for_each_entry(plane_cfg, &config->planes, link) {
>>> + 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) {
>>> - pr_err("Multiple primary planes\n");
>>> - return false;
>>> - }
>>> + xa_for_each(&plane_cfg->possible_crtcs, idx, possible_crtc) {
>>> + if (possible_crtc != crtc_cfg)
>>> + continue;
>>>
>>> - has_primary_plane = true;
>>> - } else if (type == DRM_PLANE_TYPE_CURSOR) {
>>> - if (has_cursor_plane) {
>>> - pr_err("Multiple cursor planes\n");
>>> - return false;
>>> - }
>>> + if (type == DRM_PLANE_TYPE_PRIMARY) {
>>> + if (has_primary_plane) {
>>> + pr_err("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) {
>>> + pr_err("Multiple cursor planes\n");
>>> + return false;
>>> + }
>>> +
>>> + has_cursor_plane = true;
>>> + }
>>> }
>>> }
>>
>> [...]
>>
>>> +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;
>>> +
>>> + xa_for_each(&plane_cfg->possible_crtcs, idx, possible_crtc) {
>>> + if (possible_crtc == crtc_cfg)
>>> + return -EINVAL;
>>
>> Is it really an error? After this call, we expect plane and crtc to be
>> attached, so if the plane is already attached, I don't see any issue.
>
> In my opinion, this could be either handled as an error or not. I think that
> there are arguments for both approaches but, for our use case, I think that it
> is better to return an error.
>
> Since the main (and for the moment only) user of this function will be configfs,
> it is very convenient to return an error to avoid creating 2 links between
> plane <-> crtc.
>
> If we allow to create multiple links, and the user deletes one of them, the
> items would be still linked, which is a bit unexpected.
>
> The same applies to the other vkms_config_*_attach_* functions.
I see your reasoning, I did not think about this issue.
We can keep the error, but can we use `EEXIST` to reflect better the status?
And I just think about it, maybe we can add here the check "is the crtc
part of the same config as the plane" (and return EINVAL if not)? It
will remove the need to do the check in configFS + avoid errors for
future users of vkms_config.
> For these reasons, I didn't change it in v2, let me know your opinion.
> Jose
>
>>> + }
>>> +
>>> + return xa_alloc(&plane_cfg->possible_crtcs, &crtc_idx, crtc_cfg,
>>> + xa_limit_32b, GFP_KERNEL);
>>> +}
>>> +
>>
>> [...]
>>
>>> +struct vkms_config_crtc **vkms_config_plane_get_possible_crtcs(struct vkms_config_plane *plane_cfg,
>>> + size_t *out_length)
>>> +{
>>> + struct vkms_config_crtc **array;
>>> + struct vkms_config_crtc *possible_crtc;
>>> + unsigned long idx;
>>> + size_t length = 0;
>>> + int n = 0;
>>> +
>>> + xa_for_each(&plane_cfg->possible_crtcs, idx, possible_crtc)
>>> + length++;
>>> +
>>> + if (length == 0) {
>>> + *out_length = length;
>>> + return NULL;
>>> + }
>>> +
>>> + array = kmalloc_array(length, sizeof(*array), GFP_KERNEL);
>>> + if (!array)
>>> + return ERR_PTR(-ENOMEM);
>>> +
>>> + xa_for_each(&plane_cfg->possible_crtcs, idx, possible_crtc) {
>>> + array[n] = possible_crtc;
>>> + n++;
>>> + }
>>> +
>>> + *out_length = length;
>>> + return array;
>>> +}
>>
>> Same as before, can we use an iterator?
>>
>>> +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)
>>
>> Even if this is a private function, can we add a comment explaning that
>> the returned value is only one of the available planes of this type?
>>
>> /**
>> * vkms_config_crtc_get_plane() - Get the first attached plane
>> * found of a specific type
>> * @config: configuration containing the crtc and the planes
>> * @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.
>> */
>>
>>> +{
>>> + struct vkms_config_plane *plane_cfg;
>>> + struct vkms_config_crtc *possible_crtc;
>>> + enum drm_plane_type current_type;
>>> + unsigned long idx;
>>> +
>>> + list_for_each_entry(plane_cfg, &config->planes, link) {
>>> + current_type = vkms_config_plane_get_type(plane_cfg);
>>> +
>>> + xa_for_each(&plane_cfg->possible_crtcs, idx, possible_crtc) {
>>> + if (possible_crtc == crtc_cfg && current_type == type)
>>> + return plane_cfg;
>>> + }
>>> + }
>>> +
>>> + return NULL;
>>> +}
>>
>> [...]
>>
>>> diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
>>
>> [...]
>>
>>> +/**
>>> + * vkms_config_crtc_primary_plane() - Return the primary plane for a CRTC
>>> + * @config: Configuration containing the CRTC
>>> + * @crtc_config: Target CRTC
>>> + *
>>> + * Returns:
>>> + * The primary plane or NULL if none is assigned yet.
>>> + */
>>
>> Same as above, can you speficy that it is one of the primary plane?
>>
>>> +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
>>
>> Ditto
>>
>> [...]
>>
>>> diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
>>
>> [...]
>>
>>> @@ -35,19 +41,54 @@ int vkms_output_init(struct vkms_device *vkmsdev)
>>> ret = PTR_ERR(plane_cfg->plane);
>>> goto err_free;
>>> }
>>> + }
>>> +
>>> + for (n = 0; n < n_crtcs; n++) {
>>> + struct vkms_config_crtc *crtc_cfg;
>>> + struct vkms_config_plane *primary, *cursor;
>>> +
>>> + crtc_cfg = crtc_cfgs[n];
>>> + primary = vkms_config_crtc_primary_plane(vkmsdev->config, crtc_cfg);
>>> + cursor = vkms_config_crtc_cursor_plane(vkmsdev->config, crtc_cfg);
>>
>> Linked with a previous comment: here we have no garantee that primary is a
>> valid pointer, can we check it or call vkms_config_is_valid to ensure it?
>>
>>> + 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");
>>> + ret = PTR_ERR(crtc_cfg->crtc);
>>> + goto err_free;
>>> + }
>>
>> [...]
--
Louis Chauvet, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 08/13] drm/vkms: Allow to configure multiple CRTCs
2025-02-11 10:44 ` José Expósito
@ 2025-02-12 14:12 ` Louis Chauvet
2025-02-13 15:26 ` José Expósito
0 siblings, 1 reply; 41+ messages in thread
From: Louis Chauvet @ 2025-02-12 14:12 UTC (permalink / raw)
To: José Expósito
Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
tzimmermann, airlied, dri-devel, linux-kernel
Le 11/02/2025 à 11:44, José Expósito a écrit :
> On Thu, Jan 30, 2025 at 02:48:20PM +0100, Louis Chauvet wrote:
>> On 29/01/25 - 12:00, 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.
>>>
>>> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
>>> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
>>
>> Co-developped-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>
>>
>> [...]
>>
>>> diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
>>
>> [...]
>>
>>> +static void vkms_config_test_valid_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_add_crtc(config);
>>> +
>>> + KUNIT_EXPECT_FALSE(test, vkms_config_is_valid(config));
>>> +
>>> + vkms_config_destroy(config);
>>> +}
>>
>> Same as before, can you rename the fonction to
>> vkms_config_test_invalid_crtc_number
>>
>> [...]
>>
>>> diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
>>
>> [...]
>>
>>> +struct vkms_config_crtc **vkms_config_get_crtcs(const struct vkms_config *config,
>>> + size_t *out_length)
>>> +{
>>> + struct vkms_config_crtc **array;
>>> + struct vkms_config_crtc *crtc_cfg;
>>> + size_t length;
>>> + int n = 0;
>>> +
>>> + length = list_count_nodes((struct list_head *)&config->crtcs);
>>> + if (length == 0) {
>>> + *out_length = length;
>>> + return NULL;
>>> + }
>>> +
>>> + array = kmalloc_array(length, sizeof(*array), GFP_KERNEL);
>>> + if (!array)
>>> + return ERR_PTR(-ENOMEM);
>>> +
>>> + list_for_each_entry(crtc_cfg, &config->crtcs, link) {
>>> + array[n] = crtc_cfg;
>>> + n++;
>>> + }
>>> +
>>> + *out_length = length;
>>> + return array;
>>> +}
>>
>> Same as before, can't we use an iterator?
>>
>> [...]
>>
>>> +static bool valid_crtc_number(struct vkms_config *config)
>>> +{
>>> + size_t n_crtcs;
>>> +
>>> + n_crtcs = list_count_nodes(&config->crtcs);
>>> + if (n_crtcs <= 0 || n_crtcs >= 32) {
>>> + pr_err("The number of CRTCs must be between 1 and 31\n");
>>
>> I agree we need some logs, but I think pr_err is too agressive (i.e may
>> be considered as an error by some test tools).
>>
>> I think we should at least:
>> - lower to warn/notice/info
>> - use drm variants of the macro
>>
>>> + return false;
>>> + }
>>> +
>>> + return true;
>>> +}
>>> +
>>
>> [...]
>>
>>> +struct vkms_config_crtc *vkms_config_add_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);
>>> +}
>>
>> Same as before, the pair add/destroy seems strange.
>>
>>> +++ 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));
>>
>> At this point we only create one crtc, can you move this change in the
>> commit where you create multiple crtc?
>
> Since the code to add more than one CRTCs is unused but technically present, I
> think that this is the right patch to use this function.
I don't totally agree: you can create multiple vkms_config_crtc, but the
code only creates one drm_crtc.
For me it is more logical to keep 1 here, as the rest of the code only
creates one CRTC. With the next patch, the code will create more than
one CRTC, so it makes sense to use vkms_config_get_num_crtcs.
It is not a strong blocking point, but if a v3 is needed, could you
change it?
> Anyway, at the moment it'll always return 1, so it is a no-op.
The current user is only default_config, so yes I agree, it is always 1.
> I didn't change it in v2, let me know if it works for you.
>
> Thanks,
> Jose
>
>>> if (ret) {
>>> DRM_ERROR("Failed to vblank\n");
>>> goto out_devres;
>>> --
>>> 2.48.1
>>>
--
Louis Chauvet, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 08/13] drm/vkms: Allow to configure multiple CRTCs
2025-02-12 14:12 ` Louis Chauvet
@ 2025-02-13 15:26 ` José Expósito
0 siblings, 0 replies; 41+ messages in thread
From: José Expósito @ 2025-02-13 15:26 UTC (permalink / raw)
To: Louis Chauvet
Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
tzimmermann, airlied, dri-devel, linux-kernel
Hey Louis,
On Wed, Feb 12, 2025 at 03:12:13PM +0100, Louis Chauvet wrote:
>
>
> Le 11/02/2025 à 11:44, José Expósito a écrit :
> > On Thu, Jan 30, 2025 at 02:48:20PM +0100, Louis Chauvet wrote:
> > > On 29/01/25 - 12:00, 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.
> > > >
> > > > Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> > > > Signed-off-by: José Expósito <jose.exposito89@gmail.com>
> > >
> > > Co-developped-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>
> > >
> > > [...]
> > >
> > > > +++ 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));
> > >
> > > At this point we only create one crtc, can you move this change in the
> > > commit where you create multiple crtc?
> >
> > Since the code to add more than one CRTCs is unused but technically present, I
> > think that this is the right patch to use this function.
>
> I don't totally agree: you can create multiple vkms_config_crtc, but the
> code only creates one drm_crtc.
>
> For me it is more logical to keep 1 here, as the rest of the code only
> creates one CRTC. With the next patch, the code will create more than one
> CRTC, so it makes sense to use vkms_config_get_num_crtcs.
>
> It is not a strong blocking point, but if a v3 is needed, could you change
> it?
Fair enough, moved to the next patch in my local branch.
I'll send it in v3.
Jose
> > Anyway, at the moment it'll always return 1, so it is a no-op.
>
> The current user is only default_config, so yes I agree, it is always 1.
>
> > I didn't change it in v2, let me know if it works for you.
> >
> > Thanks,
> > Jose
> >
> > > > if (ret) {
> > > > DRM_ERROR("Failed to vblank\n");
> > > > goto out_devres;
> > > > --
> > > > 2.48.1
> > > >
>
> --
> Louis Chauvet, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com
>
^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCH 09/13] drm/vkms: Allow to attach planes and CRTCs
2025-02-12 14:10 ` Louis Chauvet
@ 2025-02-13 15:32 ` José Expósito
0 siblings, 0 replies; 41+ messages in thread
From: José Expósito @ 2025-02-13 15:32 UTC (permalink / raw)
To: Louis Chauvet
Cc: hamohammed.sa, simona, melissa.srw, maarten.lankhorst, mripard,
tzimmermann, airlied, dri-devel, linux-kernel
On Wed, Feb 12, 2025 at 03:10:49PM +0100, Louis Chauvet wrote:
>
>
> Le 11/02/2025 à 11:47, José Expósito a écrit :
> > On Thu, Jan 30, 2025 at 02:48:21PM +0100, Louis Chauvet wrote:
> > > On 29/01/25 - 12:00, José Expósito wrote:
> > > > 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.
> > > >
> > > > Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> > > > Signed-off-by: José Expósito <jose.exposito89@gmail.com>
> > >
> > > Co-developped-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>
> > >
> > > [...]
> > >
> > > > +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;
> > > > +
> > > > + xa_for_each(&plane_cfg->possible_crtcs, idx, possible_crtc) {
> > > > + if (possible_crtc == crtc_cfg)
> > > > + return -EINVAL;
> > >
> > > Is it really an error? After this call, we expect plane and crtc to be
> > > attached, so if the plane is already attached, I don't see any issue.
> >
> > In my opinion, this could be either handled as an error or not. I think that
> > there are arguments for both approaches but, for our use case, I think that it
> > is better to return an error.
> >
> > Since the main (and for the moment only) user of this function will be configfs,
> > it is very convenient to return an error to avoid creating 2 links between
> > plane <-> crtc.
> >
> > If we allow to create multiple links, and the user deletes one of them, the
> > items would be still linked, which is a bit unexpected.
> >
> > The same applies to the other vkms_config_*_attach_* functions.
>
> I see your reasoning, I did not think about this issue.
> We can keep the error, but can we use `EEXIST` to reflect better the status?
Very good point, I'll change it to EEXIST in v3.
> And I just think about it, maybe we can add here the check "is the crtc part
> of the same config as the plane" (and return EINVAL if not)? It will remove
> the need to do the check in configFS + avoid errors for future users of
> vkms_config.
Another excellent point. Since we can not use the vkms_config_plane.plane and
vkms_config_crtc.crtc pointers to check if they belong to the same device, the
only solution I see is adding a pointer to the parent vkms_config to every
structure (vkms_config_plane, vkms_config_crtc, etc).
In this way we can do something like:
if (plane_cfg->config != crtc_cfg->config)
return -EINVAL;
I tried with container_of(), but I don't think it'll work with the current data
structure.
Unless you can think of a better solution, I'll implement this in v3.
Thanks for the great comments,
Jose
> > For these reasons, I didn't change it in v2, let me know your opinion.
> > Jose
> >
> > > > + }
> > > > +
> > > > + return xa_alloc(&plane_cfg->possible_crtcs, &crtc_idx, crtc_cfg,
> > > > + xa_limit_32b, GFP_KERNEL);
> > > > +}
> > > > +
> > >
> > > [...]
> > >
> > > > +struct vkms_config_crtc **vkms_config_plane_get_possible_crtcs(struct vkms_config_plane *plane_cfg,
> > > > + size_t *out_length)
> > > > +{
> > > > + struct vkms_config_crtc **array;
> > > > + struct vkms_config_crtc *possible_crtc;
> > > > + unsigned long idx;
> > > > + size_t length = 0;
> > > > + int n = 0;
> > > > +
> > > > + xa_for_each(&plane_cfg->possible_crtcs, idx, possible_crtc)
> > > > + length++;
> > > > +
> > > > + if (length == 0) {
> > > > + *out_length = length;
> > > > + return NULL;
> > > > + }
> > > > +
> > > > + array = kmalloc_array(length, sizeof(*array), GFP_KERNEL);
> > > > + if (!array)
> > > > + return ERR_PTR(-ENOMEM);
> > > > +
> > > > + xa_for_each(&plane_cfg->possible_crtcs, idx, possible_crtc) {
> > > > + array[n] = possible_crtc;
> > > > + n++;
> > > > + }
> > > > +
> > > > + *out_length = length;
> > > > + return array;
> > > > +}
> > >
> > > Same as before, can we use an iterator?
> > >
> > > > +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)
> > >
> > > Even if this is a private function, can we add a comment explaning that
> > > the returned value is only one of the available planes of this type?
> > >
> > > /**
> > > * vkms_config_crtc_get_plane() - Get the first attached plane
> > > * found of a specific type
> > > * @config: configuration containing the crtc and the planes
> > > * @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.
> > > */
> > >
> > > > +{
> > > > + struct vkms_config_plane *plane_cfg;
> > > > + struct vkms_config_crtc *possible_crtc;
> > > > + enum drm_plane_type current_type;
> > > > + unsigned long idx;
> > > > +
> > > > + list_for_each_entry(plane_cfg, &config->planes, link) {
> > > > + current_type = vkms_config_plane_get_type(plane_cfg);
> > > > +
> > > > + xa_for_each(&plane_cfg->possible_crtcs, idx, possible_crtc) {
> > > > + if (possible_crtc == crtc_cfg && current_type == type)
> > > > + return plane_cfg;
> > > > + }
> > > > + }
> > > > +
> > > > + return NULL;
> > > > +}
> > >
> > > [...]
> > >
> > > > diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
> > >
> > > [...]
> > >
> > > > +/**
> > > > + * vkms_config_crtc_primary_plane() - Return the primary plane for a CRTC
> > > > + * @config: Configuration containing the CRTC
> > > > + * @crtc_config: Target CRTC
> > > > + *
> > > > + * Returns:
> > > > + * The primary plane or NULL if none is assigned yet.
> > > > + */
> > >
> > > Same as above, can you speficy that it is one of the primary plane?
> > >
> > > > +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
> > >
> > > Ditto
> > >
> > > [...]
> > >
> > > > diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
> > >
> > > [...]
> > >
> > > > @@ -35,19 +41,54 @@ int vkms_output_init(struct vkms_device *vkmsdev)
> > > > ret = PTR_ERR(plane_cfg->plane);
> > > > goto err_free;
> > > > }
> > > > + }
> > > > +
> > > > + for (n = 0; n < n_crtcs; n++) {
> > > > + struct vkms_config_crtc *crtc_cfg;
> > > > + struct vkms_config_plane *primary, *cursor;
> > > > +
> > > > + crtc_cfg = crtc_cfgs[n];
> > > > + primary = vkms_config_crtc_primary_plane(vkmsdev->config, crtc_cfg);
> > > > + cursor = vkms_config_crtc_cursor_plane(vkmsdev->config, crtc_cfg);
> > >
> > > Linked with a previous comment: here we have no garantee that primary is a
> > > valid pointer, can we check it or call vkms_config_is_valid to ensure it?
> > >
> > > > + 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");
> > > > + ret = PTR_ERR(crtc_cfg->crtc);
> > > > + goto err_free;
> > > > + }
> > >
> > > [...]
>
> --
> Louis Chauvet, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com
>
^ permalink raw reply [flat|nested] 41+ messages in thread
end of thread, other threads:[~2025-02-13 15:32 UTC | newest]
Thread overview: 41+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-01-29 11:00 [PATCH 00/13] drm/vkms: Allow to configure device José Expósito
2025-01-29 11:00 ` [PATCH 01/13] drm/vkms: Extract vkms_connector header José Expósito
2025-01-30 13:48 ` Louis Chauvet
2025-02-11 10:37 ` José Expósito
2025-01-29 11:00 ` [PATCH 02/13] drm/vkms: Add KUnit test scaffolding José Expósito
2025-01-30 13:48 ` Louis Chauvet
2025-01-29 11:00 ` [PATCH 03/13] drm/vkms: Extract vkms_config header José Expósito
2025-01-30 13:48 ` Louis Chauvet
2025-02-11 10:39 ` José Expósito
2025-01-29 11:00 ` [PATCH 04/13] drm/vkms: Move default_config creation to its own function José Expósito
2025-01-30 13:48 ` Louis Chauvet
2025-01-29 11:00 ` [PATCH 05/13] drm/vkms: Set device name from vkms_config José Expósito
2025-01-30 13:48 ` Louis Chauvet
2025-01-29 11:00 ` [PATCH 06/13] drm/vkms: Add a validation function for VKMS configuration José Expósito
2025-01-30 13:48 ` Louis Chauvet
2025-01-29 11:00 ` [PATCH 07/13] drm/vkms: Allow to configure multiple planes José Expósito
2025-01-30 13:48 ` Louis Chauvet
2025-02-11 10:43 ` José Expósito
2025-02-12 14:10 ` Louis Chauvet
2025-01-29 11:00 ` [PATCH 08/13] drm/vkms: Allow to configure multiple CRTCs José Expósito
2025-01-30 13:48 ` Louis Chauvet
2025-02-11 10:44 ` José Expósito
2025-02-12 14:12 ` Louis Chauvet
2025-02-13 15:26 ` José Expósito
2025-01-29 11:00 ` [PATCH 09/13] drm/vkms: Allow to attach planes and CRTCs José Expósito
2025-01-30 13:48 ` Louis Chauvet
2025-02-11 10:47 ` José Expósito
2025-02-12 14:10 ` Louis Chauvet
2025-02-13 15:32 ` José Expósito
2025-01-29 11:00 ` [PATCH 10/13] drm/vkms: Allow to configure multiple encoders José Expósito
2025-01-30 13:48 ` Louis Chauvet
2025-01-29 11:00 ` [PATCH 11/13] drm/vkms: Allow to attach encoders and CRTCs José Expósito
2025-01-30 13:48 ` Louis Chauvet
2025-01-29 11:00 ` [PATCH 12/13] drm/vkms: Allow to configure multiple connectors José Expósito
2025-01-30 13:48 ` Louis Chauvet
2025-01-29 11:00 ` [PATCH 13/13] drm/vkms: Allow to attach connectors and encoders José Expósito
2025-01-30 13:48 ` Louis Chauvet
2025-01-30 13:48 ` [PATCH 00/13] drm/vkms: Allow to configure device Louis Chauvet
2025-01-31 9:31 ` José Expósito
2025-01-31 13:02 ` Louis Chauvet
2025-01-31 17:13 ` José Expósito
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox