[PATCH] media: vimc: add configfs API to configure the topology

From: Helen Koike
Date: Fri Dec 07 2018 - 13:22:22 EST


Add API to allow userspace to create any type of topology in vimc using
basic system calls such as mkdir/rmdir/read/write.

Signed-off-by: Helen Koike <helen.koike@xxxxxxxxxxxxx>

---
Hi,

This patch introduces the configufs API for configuring the topology in
vimc while it removes the hardcoded topology, so now, when you load the
module you need to create a device (no device will appear in your system
by default) using mkdir/rmdir/write/read.
Please see documentation in the patch.
I was thinking in adding a device by default, but if I do it in
configfs, userspace won't be able to delete the device (which might not
be a problem), as I need to create it as a "default" group in configfs,
or I can just not expose the default device in the configfs.
What do you think?

Thanks
Helen

Documentation/media/v4l-drivers/vimc.rst | 172 +++++
drivers/media/platform/vimc/Kconfig | 7 +-
drivers/media/platform/vimc/Makefile | 7 +-
drivers/media/platform/vimc/vimc-capture.c | 46 +-
drivers/media/platform/vimc/vimc-common.h | 58 +-
drivers/media/platform/vimc/vimc-configfs.c | 665 ++++++++++++++++++++
drivers/media/platform/vimc/vimc-configfs.h | 30 +
drivers/media/platform/vimc/vimc-core.c | 283 ++-------
drivers/media/platform/vimc/vimc-core.h | 17 +
drivers/media/platform/vimc/vimc-debayer.c | 51 +-
drivers/media/platform/vimc/vimc-scaler.c | 49 +-
drivers/media/platform/vimc/vimc-sensor.c | 43 +-
12 files changed, 1153 insertions(+), 275 deletions(-)
create mode 100644 Documentation/media/v4l-drivers/vimc.rst
create mode 100644 drivers/media/platform/vimc/vimc-configfs.c
create mode 100644 drivers/media/platform/vimc/vimc-configfs.h
create mode 100644 drivers/media/platform/vimc/vimc-core.h

diff --git a/Documentation/media/v4l-drivers/vimc.rst b/Documentation/media/v4l-drivers/vimc.rst
new file mode 100644
index 000000000000..28d3b02c7d30
--- /dev/null
+++ b/Documentation/media/v4l-drivers/vimc.rst
@@ -0,0 +1,172 @@
+The Virtual Media Controller Driver (vimc)
+=========================================
+
+This driver emulates video4linux hardware of varios media topologies. It exposes
+media devices through /dev/mediaX notes, video capture devices through
+/dev/videoX and sub-devices through /dev/v4l-subdevX.
+
+A subdevice can be a sensor, a debayer or a scaler.
+
+To configure a media device of a given topology, a ConfigFS API is provided.
+
+
+Configuring the driver through ConfigFS (Experimental)
+------------------------------------------------------
+
+.. note::
+This API is not finished yet and might change in the future.
+
+Mount configfs:
+::
+ $ mkdir /configfs
+ $ mount -t configfs none /configfs
+
+When loading the module, you see a folders name vimc
+::
+ $ tree /configfs/
+ /configfs/
+ `-- vimc
+
+1) Creating a media device
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To create a media device just create a new folder under /configfs/vimc/
+
+Example:
+::
+ $ mkdir /configfs/vimc/mdev
+ $ tree /configfs/vimc/mdev
+ /configfs/vimc/mdev/
+ |-- entities/
+ |-- hotplug
+ `-- links/
+
+ 2 directories, 1 file
+
+2) Creating entities
+~~~~~~~~~~~~~~~~~~~~
+
+To create an entity in the media device's topology, just create a folder under
+/configfs/vimc/<mdev-name>/entities/ with the following format:
+
+ <sub-module>:<name>
+
+Where <sub-module> is one of the following:
+::
+ vimc-sensor
+ vimc-scaler
+ vimc-debayer
+ vimc-capture
+
+Example:
+::
+ $ mkdir /configfs/vimc/mdev/entities/vimc-sensor:my-sensor
+ $ mkdir /configfs/vimc/mdev/entities/vimc-capture:my-capture
+ $ tree /configfs/
+ /configfs/
+ `-- vimc/
+ `-- mdev/
+ |-- entities/
+ | |-- vimc-capture:my-capture/
+ | | `-- pad:sink:0/
+ | `-- vimc-sensor:my-sensor/
+ | `-- pad:source:0/
+ |-- hotplug
+ `-- links/
+
+ 8 directories, 1 file
+
+3) Creating links
+~~~~~~~~~~~~~~~~~
+
+To create links between two entities in the topology, just create a folder under
+/configfs/vimc/<mdev-name>/links/ with the following format:
+
+ "<entity1-name><pad-source>:<entity2-name><pad-sink>"
+
+Example:
+::
+ $ mkdir "/configfs/vimc/mdev/links/my-sensor:0->my-capture:0"
+ $ tree /configfs
+ /configfs/
+ `-- vimc/
+ `-- mdev/
+ |-- entities/
+ | |-- vimc-capture:my-capture/
+ | | `-- pad:sink:0/
+ | `-- vimc-sensor:my-sensor/
+ | `-- pad:source:0/
+ |-- hotplug
+ `-- links/
+ `-- my-sensor:0->my-capture:0/
+ `-- flags
+
+ 9 directories, 2 files
+
+Change the attributes of the link by writing in the file
+"/configfs/vimc/<mdev-name>/links/<my-link>/flags"
+
+Flag values are defined in :ref:`include/uapi/linux/media.h <media_header>`
+( seek for ``MEDIA_LNK_FL_*``)
+
+1 - Enabled
+ Indicates the link will be enabled when the media device is created.
+
+3 - Enabled and Immutable
+ Indicates that the link enabled state can't be modified at runtime.
+
+Example:
+::
+ $ echo 3 > "/configfs/vimc/mdev/links/my-sensor:0->my-capture:0/flags"
+
+4) Activating/Deactivating device
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To activate the device, write one of "plugged", "plug" or "1" to file
+/configfs/vimc/<ndev-name>/hotplug
+
+Example:
+::
+ $ echo 1 > /configfs/vimc/mdev/hotplug
+
+You should see a new node /dev/mediaX in your devfs.
+
+To deactivate the device, write one of "unplugged", "unplug" or "0" to file
+/configfs/vimc/<ndev-name>/hotplug
+
+Example:
+::
+ $ echo 0 > /configfs/vimc/mdev/hotplug
+
+Subdevices
+----------
+
+Subdevices defines the behavior of an entity in the topology. Depending on the
+subdevice, the entity can have multiple pads of type source or sink.
+
+vimc-sensor:
+ Generates images in several formats using video test pattern generator.
+ Exposes:
+
+ * 1 Pad source
+
+vimc-debayer:
+ Transforms images in bayer format into a non bayer format.
+ Exposes:
+
+ * 1 Pad sink
+ * 1 Pad source
+
+vimc-scaler:
+ Mutiplies the size of the image by 3.
+ Exposes:
+
+ * 1 Pad sink
+ * 1 Pad source
+
+vimc-capture:
+ Exposes node /dev/videoX to allow userspace to capture the stream.
+ Exposes:
+
+ * 1 Pad sink
+ * 1 Pad source
diff --git a/drivers/media/platform/vimc/Kconfig b/drivers/media/platform/vimc/Kconfig
index 71c9fe7d3370..7f1fb550d4c3 100644
--- a/drivers/media/platform/vimc/Kconfig
+++ b/drivers/media/platform/vimc/Kconfig
@@ -1,15 +1,14 @@
config VIDEO_VIMC
tristate "Virtual Media Controller Driver (VIMC)"
- depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && CONFIGFS_FS
select VIDEOBUF2_VMALLOC
select VIDEO_V4L2_TPG
default n
---help---
- Skeleton driver for Virtual Media Controller
+ Virtual Media Controller Driver

This driver can be compared to the vivid driver for emulating
a media node that exposes a complex media topology. The topology
- is hard coded for now but is meant to be highly configurable in
- the future.
+ is configurable through configfs API.

When in doubt, say N.
diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile
index 4b2e3de7856e..5d926a5ef15c 100644
--- a/drivers/media/platform/vimc/Makefile
+++ b/drivers/media/platform/vimc/Makefile
@@ -1,10 +1,9 @@
# SPDX-License-Identifier: GPL-2.0
-vimc-objs := vimc-core.o
+vimc-objs := vimc-core.o vimc-common.o vimc-configfs.o
vimc_capture-objs := vimc-capture.o
-vimc_common-objs := vimc-common.o
vimc_debayer-objs := vimc-debayer.o
vimc_scaler-objs := vimc-scaler.o
vimc_sensor-objs := vimc-sensor.o

-obj-$(CONFIG_VIDEO_VIMC) += vimc.o vimc_capture.o vimc_common.o vimc-debayer.o \
- vimc_scaler.o vimc_sensor.o
+obj-$(CONFIG_VIDEO_VIMC) += vimc.o vimc_capture.o vimc-debayer.o vimc_scaler.o \
+ vimc_sensor.o
diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c
index 3f7e9ed56633..55a5f85b4591 100644
--- a/drivers/media/platform/vimc/vimc-capture.c
+++ b/drivers/media/platform/vimc/vimc-capture.c
@@ -23,6 +23,7 @@
#include <media/videobuf2-core.h>
#include <media/videobuf2-vmalloc.h>

+#include "vimc-configfs.h"
#include "vimc-common.h"

#define VIMC_CAP_DRV_NAME "vimc-capture"
@@ -418,7 +419,7 @@ static int vimc_cap_comp_bind(struct device *comp, struct device *master,
}

/* Initialize the media entity */
- vcap->vdev.entity.name = pdata->entity_name;
+ vcap->vdev.entity.name = pdata->name;
vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L;
ret = media_entity_pads_init(&vcap->vdev.entity,
1, vcap->ved.pads);
@@ -443,7 +444,7 @@ static int vimc_cap_comp_bind(struct device *comp, struct device *master,
ret = vb2_queue_init(q);
if (ret) {
dev_err(comp, "%s: vb2 queue init failed (err=%d)\n",
- pdata->entity_name, ret);
+ pdata->name, ret);
goto err_clean_m_ent;
}

@@ -476,7 +477,7 @@ static int vimc_cap_comp_bind(struct device *comp, struct device *master,
vdev->queue = q;
vdev->v4l2_dev = v4l2_dev;
vdev->vfl_dir = VFL_DIR_RX;
- strscpy(vdev->name, pdata->entity_name, sizeof(vdev->name));
+ strscpy(vdev->name, pdata->name, sizeof(vdev->name));
video_set_drvdata(vdev, &vcap->ved);

/* Register the video_device with the v4l2 and the media framework */
@@ -534,7 +535,44 @@ static struct platform_driver vimc_cap_pdrv = {
},
};

-module_platform_driver(vimc_cap_pdrv);
+static struct config_item_type vimc_cap_cfs_pad_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group vimc_cap_cfs_sink_pad_group;
+
+static void vimc_cap_configfs_cb(struct config_group *group)
+{
+ config_group_init_type_name(&vimc_cap_cfs_sink_pad_group,
+ VIMC_CFS_SINK_PAD_NAME(0),
+ &vimc_cap_cfs_pad_type);
+ configfs_add_default_group(&vimc_cap_cfs_sink_pad_group, group);
+}
+
+struct vimc_cfs_drv vimc_cap_cfs_drv = {
+ .name = VIMC_CAP_DRV_NAME,
+ .configfs_cb = vimc_cap_configfs_cb,
+};
+
+static int __init vimc_cap_init(void)
+{
+ int ret = platform_driver_register(&vimc_cap_pdrv);
+
+ if (ret)
+ return ret;
+
+ vimc_cfs_drv_register(&vimc_cap_cfs_drv);
+ return 0;
+}
+
+static void __exit vimc_cap_exit(void)
+{
+ platform_driver_unregister(&vimc_cap_pdrv);
+ vimc_cfs_drv_unregister(&vimc_cap_cfs_drv);
+}
+
+module_init(vimc_cap_init);
+module_exit(vimc_cap_exit);

MODULE_DEVICE_TABLE(platform, vimc_cap_driver_ids);

diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h
index 2e9981b18166..9a33e7901b72 100644
--- a/drivers/media/platform/vimc/vimc-common.h
+++ b/drivers/media/platform/vimc/vimc-common.h
@@ -22,6 +22,8 @@
#include <media/media-device.h>
#include <media/v4l2-device.h>

+#define VIMC_MAX_NAME_LEN 32
+
/* VIMC-specific controls */
#define VIMC_CID_VIMC_BASE (0x00f00000 | 0xf000)
#define VIMC_CID_VIMC_CLASS (0x00f00000 | 1)
@@ -63,16 +65,62 @@ do { \
/**
* struct vimc_platform_data - platform data to components
*
- * @entity_name: The name of the entity to be created
+ * @name: The name of the device
+ * @group: The configfs group the device belongs
*
* Board setup code will often provide additional information using the device's
* platform_data field to hold additional information.
- * When injecting a new platform_device in the component system the core needs
- * to provide to the corresponding submodules the name of the entity that should
- * be used when registering the subdevice in the Media Controller system.
+ * When injecting a new platform_device in the component system, the name of the
+ * device is required to allow the system to register it with a proper name.
+ * Also the configfs group is given to allow the driver to add custom items in
+ * the group.
+ * This struct is used by the entity submodules and the core system to be able
+ * to retrieve the name to register the device in the Media Controller system.
*/
struct vimc_platform_data {
- char entity_name[32];
+ char name[VIMC_MAX_NAME_LEN];
+ struct config_group *group;
+};
+
+/**
+ * struct vimc_platform_data_link - platform data to components of type link
+ *
+ * @source: source component of the link
+ * @source_pad: source pad of the link
+ * @sink: sink component of the link
+ * @sink_pad: sink pad of the link
+ * @flags: flags of the link
+ *
+ * Board setup code will often provide additional information using the device's
+ * platform_data field to hold additional information.
+ * When injecting a new platform_device representing a link in the component
+ * system, source and sink information is required to allow the link module to
+ * create the proper link between entities.
+ */
+struct vimc_platform_data_link {
+ struct platform_device *source;
+ u16 source_pad;
+ struct platform_device *sink;
+ u16 sink_pad;
+ u32 flags;
+ struct list_head list;
+};
+
+/**
+ * struct vimc_platform_data_core - platform data to the core
+ *
+ * @data: see struct vimc_platform_data
+ * @links: list of struct vimc_platform_data_link
+ *
+ * Board setup code will often provide additional information using the device's
+ * platform_data field to hold additional information.
+ * When injecting a new platform_device representing the core component, a list
+ * of struct vimc_platform_data_list is required to allow the core to create
+ * create the proper links between entities.
+ */
+struct vimc_platform_data_core {
+ struct vimc_platform_data data;
+ struct list_head *links;
};

/**
diff --git a/drivers/media/platform/vimc/vimc-configfs.c b/drivers/media/platform/vimc/vimc-configfs.c
new file mode 100644
index 000000000000..68e5f1ea736b
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-configfs.c
@@ -0,0 +1,665 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * vimc-configfs.c Virtual Media Controller Driver
+ *
+ * Copyright (C) 2018 Helen Koike <helen.koike@xxxxxxxxxxxxx>
+ */
+
+#include <linux/component.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+
+#include "vimc-common.h"
+#include "vimc-configfs.h"
+#include "vimc-core.h"
+
+#define CHAR_SEPARATOR ':'
+#define LINK_SEPARATOR "->"
+#define CFS_SUBSYS_NAME "vimc"
+
+#define ci_err(ci, fmt, ...) \
+ pr_err("vimc: %s: " pr_fmt(fmt), (ci)->ci_name, ##__VA_ARGS__)
+#define cg_err(cg, ...) ci_err(&(cg)->cg_item, ##__VA_ARGS__)
+#define ci_warn(ci, fmt, ...) \
+ pr_warn("vimc: %s: " pr_fmt(fmt), (ci)->ci_name, ##__VA_ARGS__)
+#define cg_warn(cg, ...) ci_warn(&(cg)->cg_item, ##__VA_ARGS__)
+#define ci_dbg(ci, fmt, ...) \
+ pr_debug("vimc: %s: " pr_fmt(fmt), (ci)->ci_name, ##__VA_ARGS__)
+#define cg_dbg(cg, ...) ci_dbg(&(cg)->cg_item, ##__VA_ARGS__)
+
+#define is_plugged(cfs) (!!(cfs)->pdev)
+
+enum vimc_cfs_hotplug_state {
+ VIMC_CFS_HOTPLUG_STATE_UNPLUGGED = 0,
+ VIMC_CFS_HOTPLUG_STATE_PLUGGED = 1,
+};
+
+const static char *vimc_cfs_hotplug_values[2][3] = {
+ [VIMC_CFS_HOTPLUG_STATE_UNPLUGGED] = {"unplugged\n", "unplug\n", "0\n"},
+ [VIMC_CFS_HOTPLUG_STATE_PLUGGED] = {"plugged\n", "plug\n", "1\n"},
+};
+
+/* --------------------------------------------------------------------------
+ * Pipeline structures
+ */
+
+static struct vimc_cfs_subsystem {
+ struct configfs_subsystem subsys;
+ struct list_head drvs;
+} vimc_cfs_subsys;
+
+/* Structure which describes the whole topology */
+struct vimc_cfs_device {
+ struct platform_device *pdev;
+ struct vimc_platform_data_core pdata;
+ struct list_head ents;
+ struct list_head links;
+ struct config_group gdev;
+ struct config_group gents;
+ struct config_group glinks;
+};
+
+/* Structure which describes individual configuration for each entity */
+struct vimc_cfs_ent {
+ struct platform_device *pdev;
+ struct vimc_platform_data pdata;
+ char drv[VIMC_MAX_NAME_LEN];
+ struct list_head list;
+ struct config_group cg;
+};
+
+/* Structure which describes links between entities */
+struct vimc_cfs_link {
+ struct vimc_platform_data_link pdata;
+ char source_name[VIMC_MAX_NAME_LEN];
+ char sink_name[VIMC_MAX_NAME_LEN];
+ struct config_item ci;
+};
+
+void vimc_cfs_drv_register(struct vimc_cfs_drv *c_drv)
+{
+ list_add(&c_drv->list, &vimc_cfs_subsys.drvs);
+}
+EXPORT_SYMBOL_GPL(vimc_cfs_drv_register);
+
+void vimc_cfs_drv_unregister(struct vimc_cfs_drv *c_drv)
+{
+ list_del(&c_drv->list);
+}
+EXPORT_SYMBOL_GPL(vimc_cfs_drv_unregister);
+
+/* --------------------------------------------------------------------------
+ * Platform Device builders
+ */
+
+static int vimc_cfs_link_get_entities(const struct vimc_cfs_device *cfs,
+ struct vimc_cfs_link *c_link)
+{
+ struct vimc_cfs_ent *c_ent;
+
+ c_link->pdata.source = NULL;
+ c_link->pdata.sink = NULL;
+ list_for_each_entry(c_ent, &cfs->ents, list) {
+ if (!c_link->pdata.source &&
+ !strcmp(c_ent->pdata.name, c_link->source_name))
+ c_link->pdata.source = c_ent->pdev;
+ if (!c_link->pdata.sink &&
+ !strcmp(c_ent->pdata.name, c_link->sink_name))
+ c_link->pdata.sink = c_ent->pdev;
+ if (c_link->pdata.source && c_link->pdata.sink)
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int vimc_cfs_comp_compare(struct device *comp, void *data)
+{
+ dev_dbg(comp, "comp compare %p %p", comp, data);
+
+ return comp == data;
+}
+
+static const struct component_master_ops vimc_cfs_comp_ops = {
+ .bind = vimc_core_comp_bind,
+ .unbind = vimc_core_comp_unbind,
+};
+
+static void vimc_cfs_device_unplug(struct vimc_cfs_device *cfs)
+{
+ struct vimc_cfs_ent *c_ent;
+
+ dev_dbg(&cfs->pdev->dev, "Unplugging device");
+
+ component_master_del(&cfs->pdev->dev, &vimc_cfs_comp_ops);
+ list_for_each_entry(c_ent, &cfs->ents, list) {
+ platform_device_unregister(c_ent->pdev);
+ c_ent->pdev = NULL;
+ }
+ platform_device_unregister(cfs->pdev);
+ cfs->pdev = NULL;
+}
+
+static int vimc_cfs_device_plug(struct vimc_cfs_device *cfs)
+{
+ struct component_match *match = NULL;
+ struct vimc_cfs_ent *c_ent;
+ struct vimc_cfs_link *c_link;
+ int ret = 0;
+
+ cg_dbg(&cfs->gdev, "Plugging device");
+
+ if (list_empty(&cfs->ents)) {
+ /* TODO: add support for a default topology */
+ cg_err(&cfs->gdev,
+ "At least an entity is required to plug the device");
+ return -EINVAL;
+ }
+
+ cfs->pdev = platform_device_register_data(NULL, VIMC_CORE_PDEV_NAME,
+ PLATFORM_DEVID_AUTO,
+ &cfs->pdata,
+ sizeof(cfs->pdata));
+ if (IS_ERR(cfs->pdev))
+ return PTR_ERR(cfs->pdev);
+
+ /* Add component_match for inner structure of the pipeline */
+ list_for_each_entry(c_ent, &cfs->ents, list) {
+ cg_dbg(&c_ent->cg, "registering entity %s:%s", c_ent->drv,
+ c_ent->pdata.name);
+ if (c_ent->pdev)
+ cg_err(&c_ent->cg, "pdev is not null");
+ c_ent->pdev = platform_device_register_data(&cfs->pdev->dev,
+ c_ent->drv,
+ PLATFORM_DEVID_AUTO,
+ &c_ent->pdata,
+ sizeof(c_ent->pdata));
+ if (IS_ERR(c_ent->pdev)) {
+ ret = PTR_ERR(c_ent->pdev);
+ goto unregister_ents;
+ }
+ component_match_add(&cfs->pdev->dev, &match,
+ vimc_cfs_comp_compare, &c_ent->pdev->dev);
+ }
+ list_for_each_entry(c_link, cfs->pdata.links, pdata.list) {
+ ret = vimc_cfs_link_get_entities(cfs, c_link);
+ if (ret) {
+ ci_err(&c_link->ci, "could not validate link");
+ goto unregister_ents;
+ }
+ }
+
+ dev_dbg(&cfs->pdev->dev, "Adding master device");
+ ret = component_master_add_with_match(&cfs->pdev->dev,
+ &vimc_cfs_comp_ops, match);
+ if (ret)
+ goto unregister_ents;
+
+ return 0;
+
+unregister_ents:
+ list_for_each_entry_continue_reverse(c_ent, &cfs->ents, list) {
+ platform_device_unregister(c_ent->pdev);
+ c_ent->pdev = NULL;
+ }
+
+ platform_device_unregister(cfs->pdev);
+ cfs->pdev = NULL;
+
+ return ret;
+}
+
+/* --------------------------------------------------------------------------
+ * Links
+ */
+
+static ssize_t vimc_cfs_links_attr_flags_show(struct config_item *item,
+ char *buf)
+{
+ struct vimc_cfs_link *c_link = container_of(item, struct vimc_cfs_link,
+ ci);
+
+ sprintf(buf, "%d\n", c_link->pdata.flags);
+ return strlen(buf);
+}
+
+static ssize_t vimc_cfs_links_attr_flags_store(struct config_item *item,
+ const char *buf, size_t size)
+{
+ struct vimc_cfs_link *c_link = container_of(item, struct vimc_cfs_link,
+ ci);
+
+ if (kstrtou32(buf, 0, &c_link->pdata.flags))
+ return -EINVAL;
+
+ return size;
+}
+
+CONFIGFS_ATTR(vimc_cfs_links_attr_, flags);
+
+static struct configfs_attribute *vimc_cfs_link_attrs[] = {
+ &vimc_cfs_links_attr_attr_flags,
+ NULL,
+};
+
+static void vimc_cfs_link_release(struct config_item *item)
+{
+ struct vimc_cfs_link *c_link = container_of(item, struct vimc_cfs_link,
+ ci);
+
+ kfree(c_link);
+}
+
+static struct configfs_item_operations vimc_cfs_link_item_ops = {
+ .release = vimc_cfs_link_release,
+};
+
+static struct config_item_type vimc_cfs_link_type = {
+ .ct_item_ops = &vimc_cfs_link_item_ops,
+ .ct_attrs = vimc_cfs_link_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static void vimc_cfs_link_drop_item(struct config_group *group,
+ struct config_item *item)
+{
+ struct vimc_cfs_link *link = container_of(item,
+ struct vimc_cfs_link, ci);
+ struct vimc_cfs_device *cfs = container_of(group,
+ struct vimc_cfs_device,
+ glinks);
+
+ if (is_plugged(cfs))
+ vimc_cfs_device_unplug(cfs);
+ list_del(&link->pdata.list);
+}
+
+static struct config_item *vimc_cfs_link_make_item(struct config_group *group,
+ const char *name)
+{
+ struct vimc_cfs_device *cfs = container_of(group,
+ struct vimc_cfs_device,
+ glinks);
+ size_t src_pad_strlen, sink_pad_strlen, sink_namelen, source_namelen;
+ const char *sep, *src_pad_str, *sink_pad_str, *sink_name,
+ *source_name = name;
+ struct vimc_cfs_link *c_link;
+ u16 source_pad, sink_pad;
+ char tmp[4];
+
+ cg_dbg(&cfs->gdev, "Creating link %s", name);
+
+ if (is_plugged(cfs))
+ vimc_cfs_device_unplug(cfs);
+
+ /* Parse format "source_name:source_pad->sink_name:sink_pad" */
+ sep = strchr(source_name, CHAR_SEPARATOR);
+ if (!sep)
+ goto syntax_error;
+ source_namelen = (size_t)(sep - source_name);
+
+ src_pad_str = &sep[1];
+ sep = strstr(src_pad_str, LINK_SEPARATOR);
+ if (!sep)
+ goto syntax_error;
+ src_pad_strlen = (size_t)(sep - src_pad_str);
+
+ sink_name = &sep[strlen(LINK_SEPARATOR)];
+ sep = strchr(sink_name, CHAR_SEPARATOR);
+ if (!sep)
+ goto syntax_error;
+ sink_namelen = (size_t)(sep - sink_name);
+
+ sink_pad_str = &sep[1];
+ sink_pad_strlen = strlen(sink_pad_str);
+
+ /* Validate sizes */
+ if (!src_pad_strlen || !sink_pad_strlen ||
+ !sink_namelen || !source_namelen)
+ goto syntax_error;
+
+ /* we limit the size here so we don't need to allocate another buffer */
+ if (src_pad_strlen >= sizeof(tmp) || sink_pad_strlen >= sizeof(tmp)) {
+ cg_err(&cfs->gdev,
+ "Pad with more then %ld digits is not supported",
+ sizeof(tmp) - 1);
+ goto syntax_error;
+ }
+ strscpy(tmp, src_pad_str, src_pad_strlen + 1);
+ if (kstrtou16(tmp, 0, &source_pad)) {
+ cg_err(&cfs->gdev, "Couldn't convert pad %s to number", tmp);
+ goto syntax_error;
+ }
+ strscpy(tmp, sink_pad_str, sink_pad_strlen + 1);
+ if (kstrtou16(tmp, 0, &sink_pad)) {
+ cg_err(&cfs->gdev, "Couldn't convert pad %s to number", tmp);
+ goto syntax_error;
+ }
+
+ c_link = kzalloc(sizeof(*c_link), GFP_KERNEL);
+ if (!c_link)
+ return ERR_PTR(-ENOMEM);
+
+ c_link->pdata.source_pad = source_pad;
+ c_link->pdata.sink_pad = sink_pad;
+ strscpy(c_link->source_name, source_name, source_namelen + 1);
+ strscpy(c_link->sink_name, sink_name, sink_namelen + 1);
+
+ /* Configure group */
+ list_add(&c_link->pdata.list, cfs->pdata.links);
+ config_item_init_type_name(&c_link->ci, name, &vimc_cfs_link_type);
+
+ return &c_link->ci;
+
+syntax_error:
+ cg_err(&cfs->gdev,
+ "Couldn't create link %s, wrong syntax.", name);
+ return ERR_PTR(-EINVAL);
+}
+
+/* --------------------------------------------------------------------------
+ * Entities
+ */
+
+/* *TODO: add support for hotplug in entity level */
+
+static int vimc_cfs_drv_cb(const char *drv_name, struct config_group *group)
+{
+ struct vimc_cfs_drv *c_drv = NULL;
+
+ list_for_each_entry(c_drv, &vimc_cfs_subsys.drvs, list) {
+ if (!strcmp(drv_name, c_drv->name))
+ break;
+ }
+ if (!c_drv)
+ return -EINVAL;
+
+ if (c_drv->configfs_cb)
+ c_drv->configfs_cb(group);
+
+ return 0;
+}
+
+static void vimc_cfs_ent_release(struct config_item *item)
+{
+ struct vimc_cfs_ent *c_ent = container_of(item, struct vimc_cfs_ent,
+ cg.cg_item);
+
+ kfree(c_ent);
+}
+
+static struct configfs_item_operations vimc_cfs_ent_item_ops = {
+ .release = vimc_cfs_ent_release,
+};
+
+static struct config_item_type vimc_cfs_ent_type = {
+ .ct_item_ops = &vimc_cfs_ent_item_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static void vimc_cfs_ent_drop_item(struct config_group *group,
+ struct config_item *item)
+{
+ struct vimc_cfs_ent *c_ent = container_of(item, struct vimc_cfs_ent,
+ cg.cg_item);
+ struct vimc_cfs_device *cfs = container_of(group,
+ struct vimc_cfs_device,
+ gents);
+
+ if (is_plugged(cfs))
+ vimc_cfs_device_unplug(cfs);
+ list_del(&c_ent->list);
+}
+
+static struct config_group *vimc_cfs_ent_make_group(struct config_group *group,
+ const char *name)
+{
+ struct vimc_cfs_device *cfs = container_of(group,
+ struct vimc_cfs_device,
+ gents);
+ const char *drv_name = name;
+ char *ent_name, *sep = strchr(drv_name, CHAR_SEPARATOR);
+ struct vimc_cfs_ent *c_ent;
+ size_t drv_namelen;
+
+ if (is_plugged(cfs))
+ vimc_cfs_device_unplug(cfs);
+
+ /* Parse format "drv_name:ent_name" */
+ if (!sep) {
+ cg_err(&cfs->gdev,
+ "Could not find separator '%c'", CHAR_SEPARATOR);
+ goto syntax_error;
+ }
+ drv_namelen = (size_t)(sep - drv_name);
+ ent_name = &sep[1];
+ if (!*ent_name || !drv_namelen) {
+ cg_err(&cfs->gdev,
+ "%s: Driver name and entity name can't be empty.",
+ name);
+ goto syntax_error;
+ }
+ if (drv_namelen >= sizeof(c_ent->drv)) {
+ cg_err(&cfs->gdev,
+ "%s: Driver name length should be less then %ld.",
+ name, sizeof(c_ent->drv));
+ goto syntax_error;
+ }
+
+ c_ent = kzalloc(sizeof(*c_ent), GFP_KERNEL);
+ if (!c_ent)
+ return ERR_PTR(-ENOMEM);
+
+ /* Configure platform device */
+ strscpy(c_ent->drv, drv_name, drv_namelen + 1);
+ strscpy(c_ent->pdata.name, ent_name, sizeof(c_ent->pdata.name));
+ c_ent->pdata.group = &c_ent->cg;
+
+ cg_dbg(&cfs->gdev, "New entity %s:%s", c_ent->drv, c_ent->pdata.name);
+
+ /* Configure group */
+ config_group_init_type_name(&c_ent->cg, name, &vimc_cfs_ent_type);
+ if (vimc_cfs_drv_cb(c_ent->drv, &c_ent->cg)) {
+ cg_err(&c_ent->cg, "Module %s not found", c_ent->drv);
+ kfree(c_ent);
+ return ERR_PTR(-EINVAL);
+ }
+ list_add(&c_ent->list, &cfs->ents);
+
+ return &c_ent->cg;
+
+syntax_error:
+ cg_err(&cfs->gdev,
+ "Couldn't create entity %s, wrong syntax.", name);
+ return ERR_PTR(-EINVAL);
+}
+
+/* --------------------------------------------------------------------------
+ * Default group: Links
+ */
+
+static struct configfs_group_operations vimc_cfs_dlink_group_ops = {
+ .make_item = vimc_cfs_link_make_item,
+ .drop_item = vimc_cfs_link_drop_item,
+};
+
+static struct config_item_type vimc_cfs_dlink_type = {
+ .ct_group_ops = &vimc_cfs_dlink_group_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+void vimc_cfs_dlink_add_default_group(struct vimc_cfs_device *cfs)
+{
+ config_group_init_type_name(&cfs->glinks, "links",
+ &vimc_cfs_dlink_type);
+ configfs_add_default_group(&cfs->glinks, &cfs->gdev);
+}
+
+/* --------------------------------------------------------------------------
+ * Default group: Entities
+ */
+
+static struct configfs_group_operations vimc_cfs_dent_group_ops = {
+ .make_group = vimc_cfs_ent_make_group,
+ .drop_item = vimc_cfs_ent_drop_item,
+};
+
+static struct config_item_type vimc_cfs_dent_type = {
+ .ct_group_ops = &vimc_cfs_dent_group_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+void vimc_cfs_dent_add_default_group(struct vimc_cfs_device *cfs)
+{
+ config_group_init_type_name(&cfs->gents, "entities",
+ &vimc_cfs_dent_type);
+ configfs_add_default_group(&cfs->gents, &cfs->gdev);
+}
+
+/* --------------------------------------------------------------------------
+ * Device instance
+ */
+
+static int vimc_cfs_decode_state(const char *buf, size_t size)
+{
+ unsigned int i, j;
+
+ for (i = 0; i < ARRAY_SIZE(vimc_cfs_hotplug_values); i++) {
+ for (j = 0; j < ARRAY_SIZE(vimc_cfs_hotplug_values[0]); j++) {
+ if (!strncmp(buf, vimc_cfs_hotplug_values[i][j], size))
+ return i;
+ }
+ }
+ return -EINVAL;
+}
+
+static ssize_t vimc_cfs_dev_attr_hotplug_show(struct config_item *item,
+ char *buf)
+{
+ struct vimc_cfs_device *cfs = container_of(item, struct vimc_cfs_device,
+ gdev.cg_item);
+
+ strcpy(buf, vimc_cfs_hotplug_values[is_plugged(cfs)][0]);
+ return strlen(buf);
+}
+
+static int vimc_cfs_hotplug_set(struct vimc_cfs_device *cfs,
+ enum vimc_cfs_hotplug_state state)
+{
+ if (state == is_plugged(cfs)) {
+ return 0;
+ } else if (state == VIMC_CFS_HOTPLUG_STATE_UNPLUGGED) {
+ vimc_cfs_device_unplug(cfs);
+ return 0;
+ } else if (state == VIMC_CFS_HOTPLUG_STATE_PLUGGED) {
+ return vimc_cfs_device_plug(cfs);
+ }
+ return -EINVAL;
+}
+
+static ssize_t vimc_cfs_dev_attr_hotplug_store(struct config_item *item,
+ const char *buf, size_t size)
+{
+ struct vimc_cfs_device *cfs = container_of(item, struct vimc_cfs_device,
+ gdev.cg_item);
+ int state = vimc_cfs_decode_state(buf, size);
+
+ if (vimc_cfs_hotplug_set(cfs, state))
+ return -EINVAL;
+ return size;
+}
+
+CONFIGFS_ATTR(vimc_cfs_dev_attr_, hotplug);
+
+static struct configfs_attribute *vimc_cfs_dev_attrs[] = {
+ &vimc_cfs_dev_attr_attr_hotplug,
+ NULL,
+};
+
+static void vimc_cfs_dev_release(struct config_item *item)
+{
+ struct vimc_cfs_device *cfs = container_of(item, struct vimc_cfs_device,
+ gdev.cg_item);
+
+ kfree(cfs);
+}
+
+static struct configfs_item_operations vimc_cfs_dev_item_ops = {
+ .release = vimc_cfs_dev_release,
+};
+
+static struct config_item_type vimc_cfs_dev_type = {
+ .ct_item_ops = &vimc_cfs_dev_item_ops,
+ .ct_attrs = vimc_cfs_dev_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static void vimc_cfs_dev_drop_item(struct config_group *group,
+ struct config_item *item)
+{
+ struct vimc_cfs_device *cfs = container_of(group,
+ struct vimc_cfs_device,
+ gdev);
+
+ if (is_plugged(cfs))
+ vimc_cfs_device_unplug(cfs);
+}
+
+static struct config_group *vimc_cfs_dev_make_group(
+ struct config_group *group, const char *name)
+{
+ struct vimc_cfs_device *cfs = kzalloc(sizeof(*cfs), GFP_KERNEL);
+
+ if (!cfs)
+ return ERR_PTR(-ENOMEM);
+
+ /* Configure pipeline */
+ INIT_LIST_HEAD(&cfs->ents);
+ INIT_LIST_HEAD(&cfs->links);
+
+ /* Configure platform data */
+ strscpy(cfs->pdata.data.name, name, sizeof(cfs->pdata.data.name));
+ cfs->pdata.data.group = &cfs->gdev;
+ cfs->pdata.links = &cfs->links;
+
+ /* Configure configfs group */
+ config_group_init_type_name(&cfs->gdev, name, &vimc_cfs_dev_type);
+ vimc_cfs_dent_add_default_group(cfs);
+ vimc_cfs_dlink_add_default_group(cfs);
+
+ return &cfs->gdev;
+}
+
+/* --------------------------------------------------------------------------
+ * Subsystem
+ */
+
+static struct configfs_group_operations vimc_cfs_subsys_group_ops = {
+ /* Create vimc devices */
+ .make_group = vimc_cfs_dev_make_group,
+ .drop_item = vimc_cfs_dev_drop_item,
+};
+
+static struct config_item_type vimc_cfs_subsys_type = {
+ .ct_group_ops = &vimc_cfs_subsys_group_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+int vimc_cfs_subsys_register(void)
+{
+ struct configfs_subsystem *subsys = &vimc_cfs_subsys.subsys;
+ int ret;
+
+ INIT_LIST_HEAD(&vimc_cfs_subsys.drvs);
+ config_group_init_type_name(&subsys->su_group, CFS_SUBSYS_NAME,
+ &vimc_cfs_subsys_type);
+ mutex_init(&subsys->su_mutex);
+ ret = configfs_register_subsystem(subsys);
+
+ return ret;
+}
+
+void vimc_cfs_subsys_unregister(void)
+{
+ configfs_unregister_subsystem(&vimc_cfs_subsys.subsys);
+}
diff --git a/drivers/media/platform/vimc/vimc-configfs.h b/drivers/media/platform/vimc/vimc-configfs.h
new file mode 100644
index 000000000000..6278b53d11ba
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-configfs.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * vimc-configfs.h Virtual Media Controller Driver
+ *
+ * Copyright (C) 2018 Helen Koike <helen.koike@xxxxxxxxxxxxx>
+ */
+
+#ifndef _VIMC_CONFIGFS_H_
+#define _VIMC_CONFIGFS_H_
+
+#include <linux/configfs.h>
+
+#define VIMC_CFS_SRC_PAD_NAME(n) "pad:source:" #n
+#define VIMC_CFS_SINK_PAD_NAME(n) "pad:sink:" #n
+
+struct vimc_cfs_drv {
+ const char *name;
+ void (*const configfs_cb)(struct config_group *group);
+ struct list_head list;
+};
+
+int vimc_cfs_subsys_register(void);
+
+void vimc_cfs_subsys_unregister(void);
+
+void vimc_cfs_drv_register(struct vimc_cfs_drv *c_drv);
+
+void vimc_cfs_drv_unregister(struct vimc_cfs_drv *c_drv);
+
+#endif
diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c
index ce809d2e3d53..816015e7c8f6 100644
--- a/drivers/media/platform/vimc/vimc-core.c
+++ b/drivers/media/platform/vimc/vimc-core.c
@@ -23,150 +23,29 @@
#include <media/v4l2-device.h>

#include "vimc-common.h"
-
-#define VIMC_PDEV_NAME "vimc"
-#define VIMC_MDEV_MODEL_NAME "VIMC MDEV"
-
-#define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) { \
- .src_ent = src, \
- .src_pad = srcpad, \
- .sink_ent = sink, \
- .sink_pad = sinkpad, \
- .flags = link_flags, \
-}
+#include "vimc-configfs.h"

struct vimc_device {
- /* The platform device */
- struct platform_device pdev;
-
- /* The pipeline configuration */
- const struct vimc_pipeline_config *pipe_cfg;
-
/* The Associated media_device parent */
struct media_device mdev;

/* Internal v4l2 parent device*/
struct v4l2_device v4l2_dev;
-
- /* Subdevices */
- struct platform_device **subdevs;
-};
-
-/* Structure which describes individual configuration for each entity */
-struct vimc_ent_config {
- const char *name;
- const char *drv;
-};
-
-/* Structure which describes links between entities */
-struct vimc_ent_link {
- unsigned int src_ent;
- u16 src_pad;
- unsigned int sink_ent;
- u16 sink_pad;
- u32 flags;
-};
-
-/* Structure which describes the whole topology */
-struct vimc_pipeline_config {
- const struct vimc_ent_config *ents;
- size_t num_ents;
- const struct vimc_ent_link *links;
- size_t num_links;
-};
-
-/* --------------------------------------------------------------------------
- * Topology Configuration
- */
-
-static const struct vimc_ent_config ent_config[] = {
- {
- .name = "Sensor A",
- .drv = "vimc-sensor",
- },
- {
- .name = "Sensor B",
- .drv = "vimc-sensor",
- },
- {
- .name = "Debayer A",
- .drv = "vimc-debayer",
- },
- {
- .name = "Debayer B",
- .drv = "vimc-debayer",
- },
- {
- .name = "Raw Capture 0",
- .drv = "vimc-capture",
- },
- {
- .name = "Raw Capture 1",
- .drv = "vimc-capture",
- },
- {
- .name = "RGB/YUV Input",
- /* TODO: change this to vimc-input when it is implemented */
- .drv = "vimc-sensor",
- },
- {
- .name = "Scaler",
- .drv = "vimc-scaler",
- },
- {
- .name = "RGB/YUV Capture",
- .drv = "vimc-capture",
- },
-};
-
-static const struct vimc_ent_link ent_links[] = {
- /* Link: Sensor A (Pad 0)->(Pad 0) Debayer A */
- VIMC_ENT_LINK(0, 0, 2, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
- /* Link: Sensor A (Pad 0)->(Pad 0) Raw Capture 0 */
- VIMC_ENT_LINK(0, 0, 4, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
- /* Link: Sensor B (Pad 0)->(Pad 0) Debayer B */
- VIMC_ENT_LINK(1, 0, 3, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
- /* Link: Sensor B (Pad 0)->(Pad 0) Raw Capture 1 */
- VIMC_ENT_LINK(1, 0, 5, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
- /* Link: Debayer A (Pad 1)->(Pad 0) Scaler */
- VIMC_ENT_LINK(2, 1, 7, 0, MEDIA_LNK_FL_ENABLED),
- /* Link: Debayer B (Pad 1)->(Pad 0) Scaler */
- VIMC_ENT_LINK(3, 1, 7, 0, 0),
- /* Link: RGB/YUV Input (Pad 0)->(Pad 0) Scaler */
- VIMC_ENT_LINK(6, 0, 7, 0, 0),
- /* Link: Scaler (Pad 1)->(Pad 0) RGB/YUV Capture */
- VIMC_ENT_LINK(7, 1, 8, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
-};
-
-static const struct vimc_pipeline_config pipe_cfg = {
- .ents = ent_config,
- .num_ents = ARRAY_SIZE(ent_config),
- .links = ent_links,
- .num_links = ARRAY_SIZE(ent_links)
};

-/* -------------------------------------------------------------------------- */
-
-static int vimc_create_links(struct vimc_device *vimc)
+static int vimc_core_links_create(const struct device *master)
{
- unsigned int i;
+ struct vimc_platform_data_core *pdata = master->platform_data;
+ struct vimc_ent_device *ved_src, *ved_sink;
+ struct vimc_platform_data_link *plink;
int ret;

- /* Initialize the links between entities */
- for (i = 0; i < vimc->pipe_cfg->num_links; i++) {
- const struct vimc_ent_link *link = &vimc->pipe_cfg->links[i];
- /*
- * TODO: Check another way of retrieving ved struct without
- * relying on platform_get_drvdata
- */
- struct vimc_ent_device *ved_src =
- platform_get_drvdata(vimc->subdevs[link->src_ent]);
- struct vimc_ent_device *ved_sink =
- platform_get_drvdata(vimc->subdevs[link->sink_ent]);
-
- ret = media_create_pad_link(ved_src->ent, link->src_pad,
- ved_sink->ent, link->sink_pad,
- link->flags);
+ list_for_each_entry(plink, pdata->links, list) {
+ ved_src = platform_get_drvdata(plink->source);
+ ved_sink = platform_get_drvdata(plink->sink);
+ ret = media_create_pad_link(ved_src->ent, plink->source_pad,
+ ved_sink->ent, plink->sink_pad,
+ plink->flags);
if (ret)
return ret;
}
@@ -174,10 +53,10 @@ static int vimc_create_links(struct vimc_device *vimc)
return 0;
}

-static int vimc_comp_bind(struct device *master)
+int vimc_core_comp_bind(struct device *master)
{
- struct vimc_device *vimc = container_of(to_platform_device(master),
- struct vimc_device, pdev);
+ struct vimc_device *vimc =
+ platform_get_drvdata(to_platform_device(master));
int ret;

dev_dbg(master, "bind");
@@ -194,9 +73,7 @@ static int vimc_comp_bind(struct device *master)
ret = component_bind_all(master, &vimc->v4l2_dev);
if (ret)
goto err_v4l2_unregister;
-
- /* Initialize links */
- ret = vimc_create_links(vimc);
+ ret = vimc_core_links_create(master);
if (ret)
goto err_comp_unbind_all;

@@ -228,11 +105,12 @@ static int vimc_comp_bind(struct device *master)

return ret;
}
+EXPORT_SYMBOL_GPL(vimc_core_comp_bind);

-static void vimc_comp_unbind(struct device *master)
+void vimc_core_comp_unbind(struct device *master)
{
- struct vimc_device *vimc = container_of(to_platform_device(master),
- struct vimc_device, pdev);
+ struct vimc_device *vimc =
+ platform_get_drvdata(to_platform_device(master));

dev_dbg(master, "unbind");

@@ -240,147 +118,56 @@ static void vimc_comp_unbind(struct device *master)
component_unbind_all(master, NULL);
v4l2_device_unregister(&vimc->v4l2_dev);
}
-
-static int vimc_comp_compare(struct device *comp, void *data)
-{
- const struct platform_device *pdev = to_platform_device(comp);
- const char *name = data;
-
- return !strcmp(pdev->dev.platform_data, name);
-}
-
-static struct component_match *vimc_add_subdevs(struct vimc_device *vimc)
-{
- struct component_match *match = NULL;
- struct vimc_platform_data pdata;
- int i;
-
- for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
- dev_dbg(&vimc->pdev.dev, "new pdev for %s\n",
- vimc->pipe_cfg->ents[i].drv);
-
- strscpy(pdata.entity_name, vimc->pipe_cfg->ents[i].name,
- sizeof(pdata.entity_name));
-
- vimc->subdevs[i] = platform_device_register_data(&vimc->pdev.dev,
- vimc->pipe_cfg->ents[i].drv,
- PLATFORM_DEVID_AUTO,
- &pdata,
- sizeof(pdata));
- if (IS_ERR(vimc->subdevs[i])) {
- match = ERR_CAST(vimc->subdevs[i]);
- while (--i >= 0)
- platform_device_unregister(vimc->subdevs[i]);
-
- return match;
- }
-
- component_match_add(&vimc->pdev.dev, &match, vimc_comp_compare,
- (void *)vimc->pipe_cfg->ents[i].name);
- }
-
- return match;
-}
-
-static void vimc_rm_subdevs(struct vimc_device *vimc)
-{
- unsigned int i;
-
- for (i = 0; i < vimc->pipe_cfg->num_ents; i++)
- platform_device_unregister(vimc->subdevs[i]);
-}
-
-static const struct component_master_ops vimc_comp_ops = {
- .bind = vimc_comp_bind,
- .unbind = vimc_comp_unbind,
-};
+EXPORT_SYMBOL_GPL(vimc_core_comp_unbind);

static int vimc_probe(struct platform_device *pdev)
{
- struct vimc_device *vimc = container_of(pdev, struct vimc_device, pdev);
- struct component_match *match = NULL;
- int ret;
+ const struct vimc_platform_data_core *pdata = pdev->dev.platform_data;
+ struct vimc_device *vimc = devm_kzalloc(&pdev->dev, sizeof(*vimc),
+ GFP_KERNEL);

dev_dbg(&pdev->dev, "probe");

- /* Create platform_device for each entity in the topology*/
- vimc->subdevs = devm_kcalloc(&vimc->pdev.dev, vimc->pipe_cfg->num_ents,
- sizeof(*vimc->subdevs), GFP_KERNEL);
- if (!vimc->subdevs)
- return -ENOMEM;
-
- match = vimc_add_subdevs(vimc);
- if (IS_ERR(match))
- return PTR_ERR(match);
-
- /* Link the media device within the v4l2_device */
- vimc->v4l2_dev.mdev = &vimc->mdev;
-
/* Initialize media device */
- strscpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME,
- sizeof(vimc->mdev.model));
+ strscpy(vimc->mdev.model, pdata->data.name, sizeof(vimc->mdev.model));
vimc->mdev.dev = &pdev->dev;
media_device_init(&vimc->mdev);
+ vimc->v4l2_dev.mdev = &vimc->mdev;

- /* Add self to the component system */
- ret = component_master_add_with_match(&pdev->dev, &vimc_comp_ops,
- match);
- if (ret) {
- media_device_cleanup(&vimc->mdev);
- vimc_rm_subdevs(vimc);
- return ret;
- }
+ platform_set_drvdata(pdev, vimc);

return 0;
}

static int vimc_remove(struct platform_device *pdev)
{
- struct vimc_device *vimc = container_of(pdev, struct vimc_device, pdev);
+ struct vimc_device *vimc = platform_get_drvdata(pdev);

dev_dbg(&pdev->dev, "remove");

- component_master_del(&pdev->dev, &vimc_comp_ops);
- vimc_rm_subdevs(vimc);
+ media_device_cleanup(&vimc->mdev);

return 0;
}

-static void vimc_dev_release(struct device *dev)
-{
-}
-
-static struct vimc_device vimc_dev = {
- .pipe_cfg = &pipe_cfg,
- .pdev = {
- .name = VIMC_PDEV_NAME,
- .dev.release = vimc_dev_release,
- }
-};
-
static struct platform_driver vimc_pdrv = {
.probe = vimc_probe,
.remove = vimc_remove,
.driver = {
- .name = VIMC_PDEV_NAME,
- },
+ .name = "vimc-core",
+ }
};

static int __init vimc_init(void)
{
int ret;

- ret = platform_device_register(&vimc_dev.pdev);
- if (ret) {
- dev_err(&vimc_dev.pdev.dev,
- "platform device registration failed (err=%d)\n", ret);
+ ret = platform_driver_register(&vimc_pdrv);
+ if (ret)
return ret;
- }

- ret = platform_driver_register(&vimc_pdrv);
+ ret = vimc_cfs_subsys_register();
if (ret) {
- dev_err(&vimc_dev.pdev.dev,
- "platform driver registration failed (err=%d)\n", ret);
platform_driver_unregister(&vimc_pdrv);
return ret;
}
@@ -390,9 +177,9 @@ static int __init vimc_init(void)

static void __exit vimc_exit(void)
{
- platform_driver_unregister(&vimc_pdrv);
+ vimc_cfs_subsys_unregister();

- platform_device_unregister(&vimc_dev.pdev);
+ platform_driver_unregister(&vimc_pdrv);
}

module_init(vimc_init);
diff --git a/drivers/media/platform/vimc/vimc-core.h b/drivers/media/platform/vimc/vimc-core.h
new file mode 100644
index 000000000000..42c8f92354af
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-core.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * vimc-core.h Virtual Media Controller Driver
+ *
+ * Copyright (C) 2018 Helen Koike <helen.koike@xxxxxxxxxxxxx>
+ */
+
+#ifndef _VIMC_CORE_H_
+#define _VIMC_CORE_H_
+
+#define VIMC_CORE_PDEV_NAME "vimc-core"
+
+int vimc_core_comp_bind(struct device *master);
+
+void vimc_core_comp_unbind(struct device *master);
+
+#endif
diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c
index 77887f66f323..fa326dbb7562 100644
--- a/drivers/media/platform/vimc/vimc-debayer.c
+++ b/drivers/media/platform/vimc/vimc-debayer.c
@@ -23,6 +23,7 @@
#include <linux/v4l2-mediabus.h>
#include <media/v4l2-subdev.h>

+#include "vimc-configfs.h"
#include "vimc-common.h"

#define VIMC_DEB_DRV_NAME "vimc-debayer"
@@ -522,7 +523,7 @@ static int vimc_deb_comp_bind(struct device *comp, struct device *master,
void *master_data)
{
struct v4l2_device *v4l2_dev = master_data;
- struct vimc_platform_data *pdata = comp->platform_data;
+ struct vimc_platform_data *pdata = dev_get_platdata(comp);
struct vimc_deb_device *vdeb;
int ret;

@@ -532,8 +533,7 @@ static int vimc_deb_comp_bind(struct device *comp, struct device *master,
return -ENOMEM;

/* Initialize ved and sd */
- ret = vimc_ent_sd_register(&vdeb->ved, &vdeb->sd, v4l2_dev,
- pdata->entity_name,
+ ret = vimc_ent_sd_register(&vdeb->ved, &vdeb->sd, v4l2_dev, pdata->name,
MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV, 2,
(const unsigned long[2]) {MEDIA_PAD_FL_SINK,
MEDIA_PAD_FL_SOURCE},
@@ -594,7 +594,50 @@ static struct platform_driver vimc_deb_pdrv = {
},
};

-module_platform_driver(vimc_deb_pdrv);
+static struct config_item_type vimc_deb_cfs_pad_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group vimc_deb_cfs_sink_pad_group;
+static struct config_group vimc_deb_cfs_src_pad_group;
+
+static void vimc_deb_configfs_cb(struct config_group *group)
+{
+ config_group_init_type_name(&vimc_deb_cfs_sink_pad_group,
+ VIMC_CFS_SINK_PAD_NAME(0),
+ &vimc_deb_cfs_pad_type);
+ configfs_add_default_group(&vimc_deb_cfs_sink_pad_group, group);
+
+ config_group_init_type_name(&vimc_deb_cfs_src_pad_group,
+ VIMC_CFS_SRC_PAD_NAME(1),
+ &vimc_deb_cfs_pad_type);
+ configfs_add_default_group(&vimc_deb_cfs_src_pad_group, group);
+}
+
+struct vimc_cfs_drv vimc_deb_cfs_drv = {
+ .name = VIMC_DEB_DRV_NAME,
+ .configfs_cb = vimc_deb_configfs_cb,
+};
+
+static int __init vimc_deb_init(void)
+{
+ int ret = platform_driver_register(&vimc_deb_pdrv);
+
+ if (ret)
+ return ret;
+
+ vimc_cfs_drv_register(&vimc_deb_cfs_drv);
+ return 0;
+}
+
+static void __exit vimc_deb_exit(void)
+{
+ platform_driver_unregister(&vimc_deb_pdrv);
+ vimc_cfs_drv_unregister(&vimc_deb_cfs_drv);
+}
+
+module_init(vimc_deb_init);
+module_exit(vimc_deb_exit);

MODULE_DEVICE_TABLE(platform, vimc_deb_driver_ids);

diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c
index b0952ee86296..efb29384197d 100644
--- a/drivers/media/platform/vimc/vimc-scaler.c
+++ b/drivers/media/platform/vimc/vimc-scaler.c
@@ -23,6 +23,7 @@
#include <linux/v4l2-mediabus.h>
#include <media/v4l2-subdev.h>

+#include "vimc-configfs.h"
#include "vimc-common.h"

#define VIMC_SCA_DRV_NAME "vimc-scaler"
@@ -394,8 +395,7 @@ static int vimc_sca_comp_bind(struct device *comp, struct device *master,
return -ENOMEM;

/* Initialize ved and sd */
- ret = vimc_ent_sd_register(&vsca->ved, &vsca->sd, v4l2_dev,
- pdata->entity_name,
+ ret = vimc_ent_sd_register(&vsca->ved, &vsca->sd, v4l2_dev, pdata->name,
MEDIA_ENT_F_PROC_VIDEO_SCALER, 2,
(const unsigned long[2]) {MEDIA_PAD_FL_SINK,
MEDIA_PAD_FL_SOURCE},
@@ -448,7 +448,50 @@ static struct platform_driver vimc_sca_pdrv = {
},
};

-module_platform_driver(vimc_sca_pdrv);
+static struct config_item_type vimc_sca_cfs_pad_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group vimc_sca_cfs_sink_pad_group;
+static struct config_group vimc_sca_cfs_src_pad_group;
+
+static void vimc_sca_configfs_cb(struct config_group *group)
+{
+ config_group_init_type_name(&vimc_sca_cfs_sink_pad_group,
+ VIMC_CFS_SINK_PAD_NAME(0),
+ &vimc_sca_cfs_pad_type);
+ configfs_add_default_group(&vimc_sca_cfs_sink_pad_group, group);
+
+ config_group_init_type_name(&vimc_sca_cfs_src_pad_group,
+ VIMC_CFS_SRC_PAD_NAME(1),
+ &vimc_sca_cfs_pad_type);
+ configfs_add_default_group(&vimc_sca_cfs_src_pad_group, group);
+}
+
+struct vimc_cfs_drv vimc_sca_cfs_drv = {
+ .name = VIMC_SCA_DRV_NAME,
+ .configfs_cb = vimc_sca_configfs_cb,
+};
+
+static int __init vimc_sca_init(void)
+{
+ int ret = platform_driver_register(&vimc_sca_pdrv);
+
+ if (ret)
+ return ret;
+
+ vimc_cfs_drv_register(&vimc_sca_cfs_drv);
+ return 0;
+}
+
+static void __exit vimc_sca_exit(void)
+{
+ platform_driver_unregister(&vimc_sca_pdrv);
+ vimc_cfs_drv_unregister(&vimc_sca_cfs_drv);
+}
+
+module_init(vimc_sca_init);
+module_exit(vimc_sca_exit);

MODULE_DEVICE_TABLE(platform, vimc_sca_driver_ids);

diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c
index 32ca9c6172b1..37540e72c6bc 100644
--- a/drivers/media/platform/vimc/vimc-sensor.c
+++ b/drivers/media/platform/vimc/vimc-sensor.c
@@ -28,6 +28,7 @@
#include <media/v4l2-subdev.h>
#include <media/tpg/v4l2-tpg.h>

+#include "vimc-configfs.h"
#include "vimc-common.h"

#define VIMC_SEN_DRV_NAME "vimc-sensor"
@@ -405,8 +406,7 @@ static int vimc_sen_comp_bind(struct device *comp, struct device *master,
}

/* Initialize ved and sd */
- ret = vimc_ent_sd_register(&vsen->ved, &vsen->sd, v4l2_dev,
- pdata->entity_name,
+ ret = vimc_ent_sd_register(&vsen->ved, &vsen->sd, v4l2_dev, pdata->name,
MEDIA_ENT_F_CAM_SENSOR, 1,
(const unsigned long[1]) {MEDIA_PAD_FL_SOURCE},
&vimc_sen_ops);
@@ -471,7 +471,44 @@ static struct platform_driver vimc_sen_pdrv = {
},
};

-module_platform_driver(vimc_sen_pdrv);
+static struct config_item_type vimc_sen_cfs_pad_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group vimc_sen_cfs_src_pad_group;
+
+static void vimc_sen_configfs_cb(struct config_group *group)
+{
+ config_group_init_type_name(&vimc_sen_cfs_src_pad_group,
+ VIMC_CFS_SRC_PAD_NAME(0),
+ &vimc_sen_cfs_pad_type);
+ configfs_add_default_group(&vimc_sen_cfs_src_pad_group, group);
+}
+
+struct vimc_cfs_drv vimc_sen_cfs_drv = {
+ .name = VIMC_SEN_DRV_NAME,
+ .configfs_cb = vimc_sen_configfs_cb,
+};
+
+static int __init vimc_sen_init(void)
+{
+ int ret = platform_driver_register(&vimc_sen_pdrv);
+
+ if (ret)
+ return ret;
+
+ vimc_cfs_drv_register(&vimc_sen_cfs_drv);
+ return 0;
+}
+
+static void __exit vimc_sen_exit(void)
+{
+ platform_driver_unregister(&vimc_sen_pdrv);
+ vimc_cfs_drv_unregister(&vimc_sen_cfs_drv);
+}
+
+module_init(vimc_sen_init);
+module_exit(vimc_sen_exit);

MODULE_DEVICE_TABLE(platform, vimc_sen_driver_ids);

--
2.19.1