blob: 89254d0cf59063beab6243d8cdeba27c04580c40 [file] [log] [blame]
From fe2114e058de69b42584475731e59fa9c5275f7e Mon Sep 17 00:00:00 2001
From: Brandon Pollack <[email protected]>
Date: Fri, 23 Jun 2023 18:23:46 -0400
Subject: [PATCH] FROMLIST: drm/vkms: Add ConfigFS scaffolding to VKMS
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This change adds the basic scaffolding for ConfigFS, including setting
up the default directories. It does not allow for the registration of
configfs-backed devices, which is complex and provided in a follow-up
commit.
This CL includes docs about using ConfigFS with VKMS, but I'll summarize
in brief here as well (assuming ConfigFS is mounted at /config/):
To create a new device, you can do so via `mkdir
/config/vkms/my-device`.
This will create a number of directories and files automatically:
/config
`-- vkms
`-- my-device
|-- connectors
|-- crtcs
|-- encoders
|-- planes
`-- enabled
You can then configure objects by mkdir'ing in each of the directories.
When you're satisfied, you can `echo 1 > /config/vkms/my-device/enabled`.
This will create a new device according to your configuration.
For now, this will fail, but the next change will add support for it.
Signed-off-by: Jim Shargo <[email protected]>
Signed-off-by: Brandon Pollack <[email protected]>
(am from https://patchwork.kernel.org/project/dri-devel/patch/[email protected]/)
The change is in the final stages of review (fifth iteration), All that
remains is some (potential) igt tests and final signoff.
Some reasons we'd like to submit in chromeOS are:
This will allow our virtual multi display tests to run in presubmit.
This real world case will be compelling evidence if/when upstream
requests some data that this ran for real in production (and avoiding
the chicken egg problem of we need this in the kernel to run the tests
but without running the tests we can't submit upstream).
Signed-off-by: Brandon Pollack <[email protected]>
BUG=241800059
BUG=283357160
TEST=new tests defined here passing https://chromium-review.googlesource.com/c/chromiumos/platform/tast-tests/+/4666669
Change-Id: I853254cbcb439ee58d6e538b51fab8a2aa842ca3
Signed-off-by: Brandon Pollack <[email protected]>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/4810101
Reviewed-by: Stéphane Marchesin <[email protected]>
---
Documentation/gpu/vkms.rst | 18 +-
drivers/gpu/drm/vkms/Kconfig | 1 +
drivers/gpu/drm/vkms/Makefile | 1 +
drivers/gpu/drm/vkms/vkms_configfs.c | 650 +++++++++++++++++++++++++++
drivers/gpu/drm/vkms/vkms_drv.c | 56 ++-
drivers/gpu/drm/vkms/vkms_drv.h | 92 +++-
drivers/gpu/drm/vkms/vkms_output.c | 5 +
7 files changed, 806 insertions(+), 17 deletions(-)
create mode 100644 drivers/gpu/drm/vkms/vkms_configfs.c
diff --git a/Documentation/gpu/vkms.rst b/Documentation/gpu/vkms.rst
index ba04ac7c2167a9d484c54c69a09a2fb8f2d9c0aa..c3875bf66dba0312eb439b6ae2f05643d29e5318 100644
--- a/Documentation/gpu/vkms.rst
+++ b/Documentation/gpu/vkms.rst
@@ -51,6 +51,12 @@ To disable the driver, use ::
sudo modprobe -r vkms
+Configuration With ConfigFS
+===========================
+
+.. kernel-doc:: drivers/gpu/drm/vkms/vkms_configfs.c
+ :doc: ConfigFS Support for VKMS
+
Testing With IGT
================
@@ -135,22 +141,16 @@ project.
Runtime Configuration
---------------------
-We want to be able to reconfigure vkms instance without having to reload the
-module. Use/Test-cases:
+We want to be able to manipulate vkms instances without having to reload the
+module. Such configuration can be added as extensions to vkms's ConfigFS
+support. Use-cases:
- Hotplug/hotremove connectors on the fly (to be able to test DP MST handling
of compositors).
-- Configure planes/crtcs/connectors (we'd need some code to have more than 1 of
- them first).
-
- Change output configuration: Plug/unplug screens, change EDID, allow changing
the refresh rate.
-The currently proposed solution is to expose vkms configuration through
-configfs. All existing module options should be supported through configfs
-too.
-
Writeback support
-----------------
diff --git a/drivers/gpu/drm/vkms/Kconfig b/drivers/gpu/drm/vkms/Kconfig
index b9ecdebecb0b6a57b638aba655bacd9d87656eaa..43b4d39f9f5965b3cb5a8f9ebdd0fe1e4a755670 100644
--- a/drivers/gpu/drm/vkms/Kconfig
+++ b/drivers/gpu/drm/vkms/Kconfig
@@ -5,6 +5,7 @@ config DRM_VKMS
depends on DRM && MMU
select DRM_KMS_HELPER
select DRM_GEM_SHMEM_HELPER
+ select CONFIGFS_FS
select CRC32
default n
help
diff --git a/drivers/gpu/drm/vkms/Makefile b/drivers/gpu/drm/vkms/Makefile
index 1b28a6a32948b557867dda51d2ccfdea687a2b62..6b83907ad5544a0695c15aad705a0fca76b36e28 100644
--- a/drivers/gpu/drm/vkms/Makefile
+++ b/drivers/gpu/drm/vkms/Makefile
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
vkms-y := \
+ vkms_configfs.o \
vkms_drv.o \
vkms_plane.o \
vkms_output.o \
diff --git a/drivers/gpu/drm/vkms/vkms_configfs.c b/drivers/gpu/drm/vkms/vkms_configfs.c
new file mode 100644
index 0000000000000000000000000000000000000000..dae2e85d83a14ac6cbda77e8d8973d417ea8a233
--- /dev/null
+++ b/drivers/gpu/drm/vkms/vkms_configfs.c
@@ -0,0 +1,650 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <linux/configfs.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+
+#include <drm/drm_plane.h>
+#include <drm/drm_print.h>
+
+#include "vkms_drv.h"
+
+/**
+ * DOC: ConfigFS Support for VKMS
+ *
+ * VKMS is instrumented with support for configuration via :doc:`ConfigFS
+ * <../filesystems/configfs>`.
+ *
+ * With VKMS installed, you can mount ConfigFS at ``/config/`` like so::
+ *
+ * mkdir -p /config/
+ * sudo mount -t configfs none /config
+ *
+ * This allows you to configure multiple virtual devices. Note
+ * that the default device which can be enabled in the module params with::
+ *
+ * modprobe vkms default_device=1
+ *
+ * is immutable because we cannot pre-populate ConfigFS directories with normal
+ * files.
+ *
+ * To set up a new device, create a new directory under the VKMS configfs
+ * directory::
+ *
+ * mkdir /config/vkms/test
+ *
+ * With your device created you'll find an new directory ready to be
+ * configured::
+ *
+ * /config
+ * `-- vkms
+ * `-- test
+ * |-- connectors
+ * |-- crtcs
+ * |-- encoders
+ * |-- planes
+ * `-- enabled
+ *
+ * Each directory you add within the connectors, crtcs, encoders, and planes
+ * directories will let you configure a new object of that type. Adding new
+ * objects will automatically create a set of default files and folders you can
+ * use to configure that object.
+ *
+ * For instance, we can set up a two-output device like so::
+ *
+ * DRM_PLANE_TYPE_PRIMARY=1
+ * DRM_PLANE_TYPE_CURSOR=2
+ * DRM_PLANE_TYPE_OVERLAY=0
+ *
+ * mkdir /config/vkms/test/planes/primary
+ * echo $DRM_PLANE_TYPE_PRIMARY > /config/vkms/test/planes/primary/type
+ *
+ * mkdir /config/vkms/test/planes/other_primary
+ * echo $DRM_PLANE_TYPE_PRIMARY > /config/vkms/test/planes/other_primary/type
+ *
+ * mkdir /config/vkms/test/crtcs/crtc
+ * mkdir /config/vkms/test/crtcs/crtc_other
+ *
+ * mkdir /config/vkms/test/encoders/encoder
+ * mkdir /config/vkms/test/encoders/encoder_other
+ *
+ * mkdir /config/vkms/test/connectors/connector
+ * mkdir /config/vkms/test/connectors/connector_other
+ *
+ * You can see that specific attributes, such as ``.../<plane>/type``, can be
+ * configured by writing into them. Associating objects together can be done via
+ * symlinks::
+ *
+ * ln -s /config/vkms/test/encoders/encoder /config/vkms/test/connectors/connector/possible_encoders
+ * ln -s /config/vkms/test/encoders/encoder_other /config/vkms/test/connectors/connector_other/possible_encoders
+ *
+ * ln -s /config/vkms/test/crtcs/crtc /config/vkms/test/planes/primary/possible_crtcs/
+ * ln -s /config/vkms/test/crtcs/crtc_other /config/vkms/test/planes/other_primary/possible_crtcs/
+ *
+ * ln -s /config/vkms/test/crtcs/crtc /config/vkms/test/encoders/encoder/possible_crtcs/
+ * ln -s /config/vkms/test/crtcs/crtc_other /config/vkms/test/encoders/encoder_other/possible_crtcs/
+ *
+ * Finally, to enable your configured device, just write 1 to the ``enabled``
+ * file::
+ *
+ * echo 1 > /config/vkms/test/enabled
+ *
+ * When you're done with the virtual device, you can clean up the device like
+ * so::
+ *
+ * echo 0 > /config/vkms/test/enabled
+ *
+ * rm /config/vkms/test/connectors/connector/possible_encoders/encoder
+ * rm /config/vkms/test/encoders/encoder/possible_crtcs/crtc
+ * rm /config/vkms/test/planes/primary/possible_crtcs/crtc
+ * rm /config/vkms/test/planes/cursor/possible_crtcs/crtc
+ * rm /config/vkms/test/planes/overlay/possible_crtcs/crtc
+ * rm /config/vkms/test/planes/overlay/possible_crtcs/crtc_other
+ * rm /config/vkms/test/planes/other_primary/possible_crtcs/crtc_other
+ *
+ * rmdir /config/vkms/test/planes/primary
+ * rmdir /config/vkms/test/planes/other_primary
+ * rmdir /config/vkms/test/planes/cursor
+ * rmdir /config/vkms/test/planes/overlay
+ * rmdir /config/vkms/test/crtcs/crtc
+ * rmdir /config/vkms/test/crtcs/crtc_other
+ * rmdir /config/vkms/test/encoders/encoder
+ * rmdir /config/vkms/test/encoders/encoder_other
+ * rmdir /config/vkms/test/connectors/connector
+ * rmdir /config/vkms/test/connectors/connector_other
+ *
+ * rmdir /config/vkms/test
+ */
+
+/*
+ * Common helpers (i.e. common sub-groups)
+ */
+
+/* Possible CRTCs, e.g. /config/vkms/device/<object>/possible_crtcs/<symlink> */
+
+static struct config_item_type crtc_type;
+
+static int possible_crtcs_allow_link(struct config_item *src,
+ struct config_item *target)
+{
+ struct vkms_config_links *links = item_to_config_links(src);
+ struct vkms_config_crtc *crtc;
+
+ if (target->ci_type != &crtc_type) {
+ DRM_ERROR("Unable to link non-CRTCs.\n");
+ return -EINVAL;
+ }
+
+ crtc = item_to_config_crtc(target);
+
+ if (links->linked_object_bitmap & BIT(crtc->crtc_config_idx)) {
+ DRM_ERROR(
+ "Tried to add two symlinks to the same CRTC from the same object\n");
+ return -EINVAL;
+ }
+
+ links->linked_object_bitmap |= BIT(crtc->crtc_config_idx);
+
+ return 0;
+}
+
+static void possible_crtcs_drop_link(struct config_item *src,
+ struct config_item *target)
+{
+ struct vkms_config_links *links = item_to_config_links(src);
+ struct vkms_config_crtc *crtc = item_to_config_crtc(target);
+
+ links->linked_object_bitmap &= ~BIT(crtc->crtc_config_idx);
+}
+
+static struct configfs_item_operations possible_crtcs_item_ops = {
+ .allow_link = &possible_crtcs_allow_link,
+ .drop_link = &possible_crtcs_drop_link,
+};
+
+static struct config_item_type possible_crtcs_group_type = {
+ .ct_item_ops = &possible_crtcs_item_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static void add_possible_crtcs(struct config_group *parent,
+ struct config_group *possible_crtcs)
+{
+ config_group_init_type_name(possible_crtcs, "possible_crtcs",
+ &possible_crtcs_group_type);
+ configfs_add_default_group(possible_crtcs, parent);
+}
+
+/* Possible encoders, e.g. /config/vkms/device/connector/possible_encoders/<symlink> */
+
+static struct config_item_type encoder_type;
+
+static int possible_encoders_allow_link(struct config_item *src,
+ struct config_item *target)
+{
+ struct vkms_config_links *links = item_to_config_links(src);
+ struct vkms_config_encoder *encoder;
+
+ if (target->ci_type != &encoder_type) {
+ DRM_ERROR("Unable to link non-encoders.\n");
+ return -EINVAL;
+ }
+
+ encoder = item_to_config_encoder(target);
+
+ if (links->linked_object_bitmap & BIT(encoder->encoder_config_idx)) {
+ DRM_ERROR(
+ "Tried to add two symlinks to the same encoder from the same object\n");
+ return -EINVAL;
+ }
+
+ links->linked_object_bitmap |= BIT(encoder->encoder_config_idx);
+
+ return 0;
+}
+
+static void possible_encoders_drop_link(struct config_item *src,
+ struct config_item *target)
+{
+ struct vkms_config_links *links = item_to_config_links(src);
+ struct vkms_config_encoder *encoder = item_to_config_encoder(target);
+
+ links->linked_object_bitmap &= ~BIT(encoder->encoder_config_idx);
+}
+
+static struct configfs_item_operations possible_encoders_item_ops = {
+ .allow_link = &possible_encoders_allow_link,
+ .drop_link = &possible_encoders_drop_link,
+};
+
+static struct config_item_type possible_encoders_group_type = {
+ .ct_item_ops = &possible_encoders_item_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static void add_possible_encoders(struct config_group *parent,
+ struct config_group *possible_encoders)
+{
+ config_group_init_type_name(possible_encoders, "possible_encoders",
+ &possible_encoders_group_type);
+ configfs_add_default_group(possible_encoders, parent);
+}
+
+/*
+ * Individual objects (connectors, crtcs, encoders, planes):
+ */
+
+/* Connector item, e.g. /config/vkms/device/connectors/ID */
+
+static struct config_item_type connector_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+/* Crtc item, e.g. /config/vkms/device/crtcs/ID */
+
+static struct config_item_type crtc_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+/* Encoder item, e.g. /config/vkms/device/encoder/ID */
+
+static struct config_item_type encoder_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+/* Plane item, e.g. /config/vkms/device/planes/ID */
+
+static ssize_t plane_type_show(struct config_item *item, char *buf)
+{
+ struct vkms_config_plane *plane = item_to_config_plane(item);
+ struct vkms_configfs *configfs = plane_item_to_configfs(item);
+ enum drm_plane_type plane_type;
+
+ mutex_lock(&configfs->lock);
+ plane_type = plane->type;
+ mutex_unlock(&configfs->lock);
+
+ return sprintf(buf, "%u", plane_type);
+}
+
+static ssize_t plane_type_store(struct config_item *item, const char *buf,
+ size_t len)
+{
+ struct vkms_config_plane *plane = item_to_config_plane(item);
+ struct vkms_configfs *configfs = plane_item_to_configfs(item);
+ int val, ret;
+
+ ret = kstrtouint(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ if (val != DRM_PLANE_TYPE_PRIMARY && val != DRM_PLANE_TYPE_CURSOR &&
+ val != DRM_PLANE_TYPE_OVERLAY)
+ return -EINVAL;
+
+ mutex_lock(&configfs->lock);
+ plane->type = val;
+ mutex_unlock(&configfs->lock);
+
+ return len;
+}
+
+CONFIGFS_ATTR(plane_, type);
+
+static struct configfs_attribute *plane_attrs[] = {
+ &plane_attr_type,
+ NULL,
+};
+
+static struct config_item_type plane_type = {
+ .ct_attrs = plane_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/*
+ * Directory groups, e.g. /config/vkms/device/{planes, crtcs, ...}
+ */
+
+/* Connectors group: /config/vkms/device/connectors/ */
+
+static struct config_group *connectors_group_make(struct config_group *group,
+ const char *name)
+{
+ struct vkms_config_connector *connector =
+ kzalloc(sizeof(*connector), GFP_KERNEL);
+ if (!connector)
+ return ERR_PTR(-ENOMEM);
+
+ config_group_init_type_name(&connector->config_group, name,
+ &connector_type);
+ add_possible_encoders(&connector->config_group,
+ &connector->possible_encoders.group);
+
+ return &connector->config_group;
+}
+
+static void connectors_group_drop(struct config_group *group,
+ struct config_item *item)
+{
+ struct vkms_config_connector *connector =
+ item_to_config_connector(item);
+ kfree(connector);
+}
+
+static struct configfs_group_operations connectors_group_ops = {
+ .make_group = &connectors_group_make,
+ .drop_item = &connectors_group_drop,
+};
+
+static struct config_item_type connectors_group_type = {
+ .ct_group_ops = &connectors_group_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+/* CRTCs group: /config/vkms/device/crtcs/ */
+
+static struct config_group *crtcs_group_make(struct config_group *group,
+ const char *name)
+{
+ struct vkms_configfs *configfs =
+ container_of(group, struct vkms_configfs, crtcs_group);
+ unsigned long next_idx;
+ struct vkms_config_crtc *crtc;
+
+ mutex_lock(&configfs->lock);
+
+ next_idx = find_first_zero_bit(&configfs->allocated_crtcs,
+ VKMS_MAX_OUTPUT_OBJECTS);
+
+ if (next_idx == VKMS_MAX_OUTPUT_OBJECTS) {
+ DRM_ERROR("Unable to allocate another CRTC.\n");
+ mutex_unlock(&configfs->lock);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ crtc = kzalloc(sizeof(*crtc), GFP_KERNEL);
+ if (!crtc) {
+ DRM_ERROR("Unable to allocate CRTC.\n");
+ mutex_unlock(&configfs->lock);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ config_group_init_type_name(&crtc->config_group, name, &crtc_type);
+ crtc->crtc_config_idx = next_idx;
+
+ set_bit(next_idx, &configfs->allocated_crtcs);
+
+ mutex_unlock(&configfs->lock);
+
+ return &crtc->config_group;
+}
+
+static void crtcs_group_drop(struct config_group *group,
+ struct config_item *item)
+{
+ struct vkms_config_crtc *crtc = item_to_config_crtc(item);
+
+ kfree(crtc);
+}
+
+static struct configfs_group_operations crtcs_group_ops = {
+ .make_group = &crtcs_group_make,
+ .drop_item = &crtcs_group_drop,
+};
+
+static struct config_item_type crtcs_group_type = {
+ .ct_group_ops = &crtcs_group_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+/* Encoders group: /config/vkms/device/encoders/ */
+
+static struct config_group *encoders_group_make(struct config_group *group,
+ const char *name)
+{
+ struct vkms_configfs *configfs =
+ container_of(group, struct vkms_configfs, encoders_group);
+ unsigned long next_idx;
+ struct vkms_config_encoder *encoder;
+
+ mutex_lock(&configfs->lock);
+
+ next_idx = find_first_zero_bit(&configfs->allocated_encoders,
+ VKMS_MAX_OUTPUT_OBJECTS);
+
+ if (next_idx == VKMS_MAX_OUTPUT_OBJECTS) {
+ DRM_ERROR("Unable to allocate another encoder.\n");
+ mutex_unlock(&configfs->lock);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ encoder = kzalloc(sizeof(*encoder), GFP_KERNEL);
+ if (!encoder) {
+ DRM_ERROR("Unable to allocate encoder.\n");
+ mutex_unlock(&configfs->lock);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ config_group_init_type_name(&encoder->config_group, name,
+ &encoder_type);
+ add_possible_crtcs(&encoder->config_group,
+ &encoder->possible_crtcs.group);
+ encoder->encoder_config_idx = next_idx;
+ set_bit(next_idx, &configfs->allocated_encoders);
+
+ mutex_unlock(&configfs->lock);
+
+ return &encoder->config_group;
+}
+
+static void encoders_group_drop(struct config_group *group,
+ struct config_item *item)
+{
+ struct vkms_config_encoder *encoder = item_to_config_encoder(item);
+
+ kfree(encoder);
+}
+
+static struct configfs_group_operations encoders_group_ops = {
+ .make_group = &encoders_group_make,
+ .drop_item = &encoders_group_drop,
+};
+
+static struct config_item_type encoders_group_type = {
+ .ct_group_ops = &encoders_group_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+/* Planes group: /config/vkms/device/planes/ */
+
+static struct config_group *make_plane_group(struct config_group *group,
+ const char *name)
+{
+ struct vkms_config_plane *plane = kzalloc(sizeof(*plane), GFP_KERNEL);
+
+ if (!plane)
+ return ERR_PTR(-ENOMEM);
+
+ config_group_init_type_name(&plane->config_group, name, &plane_type);
+ add_possible_crtcs(&plane->config_group, &plane->possible_crtcs.group);
+
+ return &plane->config_group;
+}
+
+static void drop_plane_group(struct config_group *group,
+ struct config_item *item)
+{
+ struct vkms_config_plane *plane = item_to_config_plane(item);
+
+ kfree(plane);
+}
+
+static struct configfs_group_operations plane_group_ops = {
+ .make_group = &make_plane_group,
+ .drop_item = &drop_plane_group,
+};
+
+static struct config_item_type planes_group_type = {
+ .ct_group_ops = &plane_group_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+/* Root directory group, e.g. /config/vkms/device */
+
+static ssize_t device_enabled_show(struct config_item *item, char *buf)
+{
+ struct vkms_configfs *configfs = item_to_configfs(item);
+ bool is_enabled;
+
+ mutex_lock(&configfs->lock);
+ is_enabled = configfs->vkms_device != NULL;
+ mutex_unlock(&configfs->lock);
+
+ return sprintf(buf, "%d", is_enabled);
+}
+
+static ssize_t device_enabled_store(struct config_item *item, const char *buf,
+ size_t len)
+{
+ struct vkms_configfs *configfs = item_to_configfs(item);
+ struct vkms_device *device;
+ int value, ret;
+
+ ret = kstrtoint(buf, 0, &value);
+ if (ret)
+ return ret;
+
+ if (value != 1)
+ return -EINVAL;
+
+ mutex_lock(&configfs->lock);
+
+ if (configfs->vkms_device) {
+ mutex_unlock(&configfs->lock);
+ return len;
+ }
+
+ device = vkms_add_device(configfs);
+ mutex_unlock(&configfs->lock);
+
+ if (IS_ERR(device))
+ return -PTR_ERR(device);
+
+ return len;
+}
+
+CONFIGFS_ATTR(device_, enabled);
+
+static ssize_t device_id_show(struct config_item *item, char *buf)
+{
+ struct vkms_configfs *configfs = item_to_configfs(item);
+ int id = -1;
+
+ mutex_lock(&configfs->lock);
+ if (configfs->vkms_device)
+ id = configfs->vkms_device->platform->id;
+
+ mutex_unlock(&configfs->lock);
+
+ return sprintf(buf, "%d", id);
+}
+
+CONFIGFS_ATTR_RO(device_, id);
+
+static struct configfs_attribute *device_group_attrs[] = {
+ &device_attr_id,
+ &device_attr_enabled,
+ NULL,
+};
+
+static struct config_item_type device_group_type = {
+ .ct_attrs = device_group_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static void vkms_configfs_setup_default_groups(struct vkms_configfs *configfs,
+ const char *name)
+{
+ config_group_init_type_name(&configfs->device_group, name,
+ &device_group_type);
+
+ config_group_init_type_name(&configfs->connectors_group, "connectors",
+ &connectors_group_type);
+ configfs_add_default_group(&configfs->connectors_group,
+ &configfs->device_group);
+
+ config_group_init_type_name(&configfs->crtcs_group, "crtcs",
+ &crtcs_group_type);
+ configfs_add_default_group(&configfs->crtcs_group,
+ &configfs->device_group);
+
+ config_group_init_type_name(&configfs->encoders_group, "encoders",
+ &encoders_group_type);
+ configfs_add_default_group(&configfs->encoders_group,
+ &configfs->device_group);
+
+ config_group_init_type_name(&configfs->planes_group, "planes",
+ &planes_group_type);
+ configfs_add_default_group(&configfs->planes_group,
+ &configfs->device_group);
+}
+
+/* Root directory group and subsystem, e.g. /config/vkms/ */
+
+static struct config_group *make_root_group(struct config_group *group,
+ const char *name)
+{
+ struct vkms_configfs *configfs = kzalloc(sizeof(*configfs), GFP_KERNEL);
+
+ if (!configfs)
+ return ERR_PTR(-ENOMEM);
+
+ vkms_configfs_setup_default_groups(configfs, name);
+ mutex_init(&configfs->lock);
+
+ return &configfs->device_group;
+}
+
+static void drop_root_group(struct config_group *group,
+ struct config_item *item)
+{
+ struct vkms_configfs *configfs = item_to_configfs(item);
+
+ mutex_lock(&configfs->lock);
+ if (configfs->vkms_device)
+ vkms_remove_device(configfs->vkms_device);
+ mutex_unlock(&configfs->lock);
+
+ kfree(configfs);
+}
+
+static struct configfs_group_operations root_group_ops = {
+ .make_group = &make_root_group,
+ .drop_item = &drop_root_group,
+};
+
+static struct config_item_type vkms_type = {
+ .ct_group_ops = &root_group_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct configfs_subsystem vkms_subsys = {
+ .su_group = {
+ .cg_item = {
+ .ci_name = "vkms",
+ .ci_type = &vkms_type,
+ },
+ },
+ .su_mutex = __MUTEX_INITIALIZER(vkms_subsys.su_mutex),
+};
+
+int vkms_init_configfs(void)
+{
+ config_group_init(&vkms_subsys.su_group);
+ return configfs_register_subsystem(&vkms_subsys);
+}
+
+void vkms_unregister_configfs(void)
+{
+ configfs_unregister_subsystem(&vkms_subsys);
+}
diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c
index 6c94c2b5d52922dfcf3bd987017884d9566485ec..819e880a8cf780d466b1bb71385cb42404ce5448 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.c
+++ b/drivers/gpu/drm/vkms/vkms_drv.c
@@ -9,8 +9,10 @@
* the GPU in DRM API tests.
*/
-#include "asm-generic/errno-base.h"
+#include <linux/configfs.h>
#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
@@ -172,8 +174,8 @@ static int vkms_modeset_init(struct vkms_device *vkmsdev)
dev->mode_config.preferred_depth = 0;
dev->mode_config.helper_private = &vkms_mode_config_helpers;
- return vkmsdev->is_default ? vkms_output_init_default(vkmsdev) :
- -EINVAL;
+ return vkmsdev->configfs ? vkms_output_init(vkmsdev) :
+ vkms_output_init_default(vkmsdev);
}
static int vkms_platform_probe(struct platform_device *pdev)
@@ -184,8 +186,10 @@ static int vkms_platform_probe(struct platform_device *pdev)
void *grp;
grp = devres_open_group(&pdev->dev, NULL, GFP_KERNEL);
- if (!grp)
+ if (!grp) {
+ DRM_ERROR("Could not open devres group\n");
return -ENOMEM;
+ }
vkms_device = devm_drm_dev_alloc(&pdev->dev, &vkms_driver,
struct vkms_device, drm);
@@ -198,7 +202,7 @@ static int vkms_platform_probe(struct platform_device *pdev)
vkms_device->config.cursor = enable_cursor;
vkms_device->config.writeback = enable_writeback;
vkms_device->config.overlay = enable_overlay;
- vkms_device->is_default = vkms_device_setup->is_default;
+ vkms_device->configfs = vkms_device_setup->configfs;
ret = dma_coerce_mask_and_coherent(vkms_device->drm.dev,
DMA_BIT_MASK(64));
@@ -258,12 +262,43 @@ static struct platform_driver vkms_platform_driver = {
.driver.name = DRIVER_NAME,
};
+struct vkms_device *vkms_add_device(struct vkms_configfs *configfs)
+{
+ struct device *dev = NULL;
+ struct platform_device *pdev;
+ int max_id = 1;
+ struct vkms_device_setup vkms_device_setup = {
+ .configfs = configfs,
+ };
+
+ while ((dev = platform_find_device_by_driver(
+ dev, &vkms_platform_driver.driver))) {
+ pdev = to_platform_device(dev);
+ max_id = max(max_id, pdev->id);
+ }
+
+ pdev = platform_device_register_data(NULL, DRIVER_NAME, max_id + 1,
+ &vkms_device_setup,
+ sizeof(vkms_device_setup));
+ if (IS_ERR(pdev)) {
+ DRM_ERROR("Unable to register vkms device'\n");
+ return ERR_PTR(PTR_ERR(pdev));
+ }
+
+ return platform_get_drvdata(pdev);
+}
+
+void vkms_remove_device(struct vkms_device *vkms_device)
+{
+ platform_device_unregister(vkms_device->platform);
+}
+
static int __init vkms_init(void)
{
int ret;
struct platform_device *pdev;
struct vkms_device_setup vkms_device_setup = {
- .is_default = true,
+ .configfs = NULL,
};
ret = platform_driver_register(&vkms_platform_driver);
@@ -281,6 +316,13 @@ static int __init vkms_init(void)
return PTR_ERR(pdev);
}
+ ret = vkms_init_configfs();
+ if (ret) {
+ DRM_ERROR("Unable to initialize configfs\n");
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&vkms_platform_driver);
+ }
+
return 0;
}
@@ -288,6 +330,8 @@ static void __exit vkms_exit(void)
{
struct device *dev;
+ vkms_unregister_configfs();
+
while ((dev = platform_find_device_by_driver(
NULL, &vkms_platform_driver.driver))) {
// platform_find_device_by_driver increments the refcount. Drop
diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h
index 4262dcffd7e15c68a607f3eb49d6c84a00369971..8cdd7949f6614a362466a500fc600de6449c31f5 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.h
+++ b/drivers/gpu/drm/vkms/vkms_drv.h
@@ -3,6 +3,7 @@
#ifndef _VKMS_DRV_H_
#define _VKMS_DRV_H_
+#include <linux/configfs.h>
#include <linux/hrtimer.h>
#include <drm/drm.h>
@@ -10,6 +11,7 @@
#include <drm/drm_gem.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_encoder.h>
+#include <drm/drm_plane.h>
#include <drm/drm_writeback.h>
#define XRES_MIN 10
@@ -138,14 +140,65 @@ struct vkms_config {
bool overlay;
};
+struct vkms_config_links {
+ struct config_group group;
+ unsigned long linked_object_bitmap;
+};
+
+struct vkms_config_connector {
+ struct config_group config_group;
+ struct vkms_config_links possible_encoders;
+};
+
+struct vkms_config_crtc {
+ struct config_group config_group;
+ unsigned long crtc_config_idx;
+};
+
+struct vkms_config_encoder {
+ struct config_group config_group;
+ struct vkms_config_links possible_crtcs;
+ unsigned long encoder_config_idx;
+};
+
+struct vkms_config_plane {
+ struct vkms_configfs *configfs;
+ struct config_group config_group;
+ struct vkms_config_links possible_crtcs;
+ enum drm_plane_type type;
+};
+
+struct vkms_configfs {
+ /* Directory group containing connector configs, e.g. /config/vkms/device/ */
+ struct config_group device_group;
+ /* Directory group containing connector configs, e.g. /config/vkms/device/connectors/ */
+ struct config_group connectors_group;
+ /* Directory group containing CRTC configs, e.g. /config/vkms/device/crtcs/ */
+ struct config_group crtcs_group;
+ /* Directory group containing encoder configs, e.g. /config/vkms/device/encoders/ */
+ struct config_group encoders_group;
+ /* Directory group containing plane configs, e.g. /config/vkms/device/planes/ */
+ struct config_group planes_group;
+
+ unsigned long allocated_crtcs;
+ unsigned long allocated_encoders;
+
+ struct mutex lock;
+
+ /* The platform device if this is registered, otherwise NULL */
+ struct vkms_device *vkms_device;
+};
+
struct vkms_device_setup {
- bool is_default;
+ // Is NULL in the case of the default card.
+ struct vkms_configfs *configfs;
};
struct vkms_device {
struct drm_device drm;
struct platform_device *platform;
- bool is_default;
+ // Is NULL in the case of the default card.
+ struct vkms_configfs *configfs;
struct vkms_output output;
struct vkms_config config;
};
@@ -164,11 +217,42 @@ struct vkms_device {
#define to_vkms_plane_state(target)\
container_of(target, struct vkms_plane_state, base.base)
+#define item_to_configfs(item) \
+ container_of(to_config_group(item), struct vkms_configfs, device_group)
+
+#define item_to_config_connector(item) \
+ container_of(to_config_group(item), struct vkms_config_connector, \
+ config_group)
+
+#define item_to_config_crtc(item) \
+ container_of(to_config_group(item), struct vkms_config_crtc, \
+ config_group)
+
+#define item_to_config_encoder(item) \
+ container_of(to_config_group(item), struct vkms_config_encoder, \
+ config_group)
+
+#define item_to_config_plane(item) \
+ container_of(to_config_group(item), struct vkms_config_plane, \
+ config_group)
+
+#define item_to_config_links(item) \
+ container_of(to_config_group(item), struct vkms_config_links, group)
+
+#define plane_item_to_configfs(item) \
+ container_of(to_config_group(item->ci_parent), struct vkms_configfs, \
+ planes_group)
+
+/* Devices */
+struct vkms_device *vkms_add_device(struct vkms_configfs *configfs);
+void vkms_remove_device(struct vkms_device *vkms_device);
+
/* CRTC */
struct vkms_crtc *vkms_crtc_init(struct vkms_device *vkmsdev,
struct drm_plane *primary,
struct drm_plane *cursor);
+int vkms_output_init(struct vkms_device *vkmsdev);
int vkms_output_init_default(struct vkms_device *vkmsdev);
struct vkms_plane *vkms_plane_init(struct vkms_device *vkmsdev,
@@ -191,4 +275,8 @@ void vkms_writeback_row(struct vkms_writeback_job *wb, const struct line_buffer
int vkms_enable_writeback_connector(struct vkms_device *vkmsdev,
struct vkms_crtc *vkms_crtc);
+/* ConfigFS Support */
+int vkms_init_configfs(void);
+void vkms_unregister_configfs(void);
+
#endif /* _VKMS_DRV_H_ */
diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
index bfc2e2362c6d2c0e6cf90de5d207db21bbc7ad67..dc69959c5e1d037df5ab601873ed566372ddf9f4 100644
--- a/drivers/gpu/drm/vkms/vkms_output.c
+++ b/drivers/gpu/drm/vkms/vkms_output.c
@@ -176,3 +176,8 @@ int vkms_output_init_default(struct vkms_device *vkmsdev)
return ret;
}
+
+int vkms_output_init(struct vkms_device *vkmsdev)
+{
+ return -EOPNOTSUPP;
+}
--
2.44.0.478.gd926399ef9-goog