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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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





[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux