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.ko...@collabora.com>

---
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.ko...@collabora.com>
+ */
+
+#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.ko...@collabora.com>
+ */
+
+#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.ko...@collabora.com>
+ */
+
+#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

Reply via email to