linux-media.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/5] media: vimc: use configfs in order to configure devices topologies
@ 2019-09-19 20:32 Dafna Hirschfeld
  2019-09-19 20:32 ` [PATCH 1/5] media: vimc: upon streaming, check that the pipeline starts with a source entity Dafna Hirschfeld
                   ` (5 more replies)
  0 siblings, 6 replies; 20+ messages in thread
From: Dafna Hirschfeld @ 2019-09-19 20:32 UTC (permalink / raw)
  To: linux-media
  Cc: dafna.hirschfeld, laurent.pinchart, helen.koike, ezequiel,
	andre.almeida, skhan, hverkuil, kernel, dafna3

This patchset introduces the usage of configfs in order to create vimc devices
with a configured topology. A patch introducing configfs usage was already sent by Helen Koike:
https://patchwork.linuxtv.org/patch/53397/ . The current patch is based on her patch but
suggests a new API for using configfs.
It uses symlinks to represent a link between two entities, an approach already used in the kernel
by usb gadgets composed with configfs to associate usb gadget's functions to its configurations.
For example, a topology of sensor->capture will be created with the following commands:

CONFIGFS_ROOT=/sys/kernel/config

mkdir ${CONFIGFS_ROOT}/vimc/mdev/
mkdir ${CONFIGFS_ROOT}/vimc/mdev/vimc-sensor:sen
mkdir ${CONFIGFS_ROOT}/vimc/mdev/vimc-capture:cap
tree ${CONFIGFS_ROOT}
/configfs/
`-- vimc
    `-- mdev
        |-- hotplug
        |-- vimc-capture:cap
        |   `-- pad:sink:0
        `-- vimc-sensor:sen
            `-- pad:source:0

mkdir ${CONFIGFS_ROOT}/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap
ln -s ${CONFIGFS_ROOT}/vimc/mdev/vimc-capture:cap/pad:sink:0 ${CONFIGFS_ROOT}/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap
tree ${CONFIGFS_ROOT}
/configfs/
`-- vimc
    `-- mdev
        |-- hotplug
        |-- vimc-capture:cap
        |   `-- pad:sink:0
        `-- vimc-sensor:sen
            `-- pad:source:0
                `-- to-cap
                    |-- enabled
                    |-- immutable
                    `-- pad:sink:0 -> ../../../../../vimc/mdev/vimc-capture:cap/pad:sink:0

There are several reasons to prefer the symlink approach in order to represent links between entities.
The previous approach in which links are represented with directories of the form 'entity1:pad>-><entity2:pad'
requires userspace to parse the dirctories names in order to understand the topology, while in the symlink
approach userspace needs only to traverse the configfs tree.
Also, the usage of symlinks prevents userspace from creating links between entities that don't exist and also
an entity can't be removed if there is a symlink pointing to it or from it, while in the previous approach the
links were created by creating unrelated directories and care had to be taken to ensure consistency. This way
the topology configured from userspace is restricted to always be valid and represent the current topology of
the device. This results in less validation needed in kernel code when plugging the device and less possibility
for mistakes in the userspace side. Last, but not least, using symlinks is the natural way of associating things
in configfs.

This patch is meant to demonstrate the suggested configfs api and get comments and acceptance/disagreement from
the community. It passes few tests that configure basic topology and streams the capture entities.
Here is the tests script: https://gitlab.collabora.com/dafna/scripts/blob/master/configfs/sym-unit-tests-simple-topo.sh
Further versions will go through more extensive debugging.

The patchset is rebased on top of v5 of the patchset 'Collapse vimc into single monolithic driver' sent by Shuah Khan
https://lkml.org/lkml/2019/9/17/656

Patch 1, was sent by me before as a single patch and is needed for the configfs implementation.

Patch 2, documents how to use the new configfs api in order to create and set devices topologies.

Patch 3, only adds the new configfs api code but does not use it yet, so it still creates only the hardcoded device.

Patch 4, removes the hardcoded device topology and creates devices with topologies configured with the configfs.

Patch 5, implements indexing for the bus_info field since now there can be more than one vimc device.

Dafna Hirschfeld (5):
  media: vimc: upon streaming, check that the pipeline starts with a
    source entity
  docs: media: vimc: Documenting vimc topology configuration using
    configfs
  media: vimc: Add the implementation for the configfs api
  media: vimc: use configfs instead of having hardcoded configuration
  media: vimc: Add device index to the bus_info

 Documentation/media/v4l-drivers/vimc.dot    |  28 +-
 Documentation/media/v4l-drivers/vimc.rst    | 240 ++++++-
 drivers/media/platform/vimc/Kconfig         |   9 +-
 drivers/media/platform/vimc/Makefile        |   2 +-
 drivers/media/platform/vimc/vimc-capture.c  |  50 +-
 drivers/media/platform/vimc/vimc-common.h   |  86 ++-
 drivers/media/platform/vimc/vimc-configfs.c | 656 ++++++++++++++++++++
 drivers/media/platform/vimc/vimc-configfs.h |  41 ++
 drivers/media/platform/vimc/vimc-core.c     | 350 +++++------
 drivers/media/platform/vimc/vimc-debayer.c  |  35 +-
 drivers/media/platform/vimc/vimc-scaler.c   |  35 +-
 drivers/media/platform/vimc/vimc-sensor.c   |  33 +-
 drivers/media/platform/vimc/vimc-streamer.c |  39 +-
 13 files changed, 1289 insertions(+), 315 deletions(-)
 create mode 100644 drivers/media/platform/vimc/vimc-configfs.c
 create mode 100644 drivers/media/platform/vimc/vimc-configfs.h

-- 
2.20.1


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 1/5] media: vimc: upon streaming, check that the pipeline starts with a source entity
  2019-09-19 20:32 [PATCH 0/5] media: vimc: use configfs in order to configure devices topologies Dafna Hirschfeld
@ 2019-09-19 20:32 ` Dafna Hirschfeld
  2019-09-26 14:32   ` Shuah Khan
  2019-09-19 20:32 ` [PATCH 2/5] docs: media: vimc: Documenting vimc topology configuration using configfs Dafna Hirschfeld
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 20+ messages in thread
From: Dafna Hirschfeld @ 2019-09-19 20:32 UTC (permalink / raw)
  To: linux-media
  Cc: dafna.hirschfeld, laurent.pinchart, helen.koike, ezequiel,
	andre.almeida, skhan, hverkuil, kernel, dafna3

Userspace can disable links and create pipelines that
do not start with a source entity. Trying to stream
from such a pipeline should fail with -EPIPE
currently this is not handled and cause kernel crash.

Reproducing the crash:
media-ctl -d0 -l "5:1->21:0[0]" -v
v4l2-ctl -z platform:vimc -d "RGB/YUV Capture" -v width=1920,height=1440
v4l2-ctl --stream-mmap --stream-count=100 -d /dev/video2

Signed-off-by: Dafna Hirschfeld <dafna.hirschfeld@collabora.com>
---
 drivers/media/platform/vimc/vimc-streamer.c | 39 +++++++++++++++------
 1 file changed, 28 insertions(+), 11 deletions(-)

diff --git a/drivers/media/platform/vimc/vimc-streamer.c b/drivers/media/platform/vimc/vimc-streamer.c
index faa2879c25df..d0a9f8a0f26a 100644
--- a/drivers/media/platform/vimc/vimc-streamer.c
+++ b/drivers/media/platform/vimc/vimc-streamer.c
@@ -12,6 +12,19 @@
 
 #include "vimc-streamer.h"
 
+/**
+ * Check if the entity has only source pads
+ */
+static bool vimc_is_source(struct media_entity *ent)
+{
+	int i;
+
+	for (i = 0; i < ent->num_pads; i++)
+		if (ent->pads[i].flags & MEDIA_PAD_FL_SINK)
+			return false;
+	return true;
+}
+
 /**
  * vimc_get_source_entity - get the entity connected with the first sink pad
  *
@@ -82,14 +95,12 @@ static int vimc_streamer_pipeline_init(struct vimc_stream *stream,
 	struct media_entity *entity;
 	struct video_device *vdev;
 	struct v4l2_subdev *sd;
-	int ret = 0;
+	int ret = -EINVAL;
 
 	stream->pipe_size = 0;
 	while (stream->pipe_size < VIMC_STREAMER_PIPELINE_MAX_SIZE) {
-		if (!ved) {
-			vimc_streamer_pipeline_terminate(stream);
-			return -EINVAL;
-		}
+		if (!ved)
+			break;
 		stream->ved_pipeline[stream->pipe_size++] = ved;
 
 		if (is_media_entity_v4l2_subdev(ved->ent)) {
@@ -98,15 +109,22 @@ static int vimc_streamer_pipeline_init(struct vimc_stream *stream,
 			if (ret && ret != -ENOIOCTLCMD) {
 				pr_err("subdev_call error %s\n",
 				       ved->ent->name);
-				vimc_streamer_pipeline_terminate(stream);
-				return ret;
+				break;
 			}
 		}
 
 		entity = vimc_get_source_entity(ved->ent);
-		/* Check if the end of the pipeline was reached*/
-		if (!entity)
+		/* Check if the end of the pipeline was reached */
+		if (!entity) {
+			/* the first entity of the pipe should be source only */
+			if (!vimc_is_source(ved->ent)) {
+				pr_err("first entity in the pipe '%s' is not a source\n",
+				       ved->ent->name);
+				ret = -EPIPE;
+				break;
+			}
 			return 0;
+		}
 
 		/* Get the next device in the pipeline */
 		if (is_media_entity_v4l2_subdev(entity)) {
@@ -119,9 +137,8 @@ static int vimc_streamer_pipeline_init(struct vimc_stream *stream,
 			ved = video_get_drvdata(vdev);
 		}
 	}
-
 	vimc_streamer_pipeline_terminate(stream);
-	return -EINVAL;
+	return ret;
 }
 
 /**
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 2/5] docs: media: vimc: Documenting vimc topology configuration using configfs
  2019-09-19 20:32 [PATCH 0/5] media: vimc: use configfs in order to configure devices topologies Dafna Hirschfeld
  2019-09-19 20:32 ` [PATCH 1/5] media: vimc: upon streaming, check that the pipeline starts with a source entity Dafna Hirschfeld
@ 2019-09-19 20:32 ` Dafna Hirschfeld
  2019-09-20 13:39   ` Hans Verkuil
  2019-09-26 18:59   ` Shuah Khan
  2019-09-19 20:32 ` [PATCH 3/5] media: vimc: Add the implementation for the configfs api Dafna Hirschfeld
                   ` (3 subsequent siblings)
  5 siblings, 2 replies; 20+ messages in thread
From: Dafna Hirschfeld @ 2019-09-19 20:32 UTC (permalink / raw)
  To: linux-media
  Cc: dafna.hirschfeld, laurent.pinchart, helen.koike, ezequiel,
	andre.almeida, skhan, hverkuil, kernel, dafna3

Add explanation of how to use configfs in order to create a
vimc device with a given topology.

Signed-off-by: Dafna Hirschfeld <dafna.hirschfeld@collabora.com>
---
 Documentation/media/v4l-drivers/vimc.dot |  28 ++-
 Documentation/media/v4l-drivers/vimc.rst | 240 ++++++++++++++++++++---
 2 files changed, 220 insertions(+), 48 deletions(-)

diff --git a/Documentation/media/v4l-drivers/vimc.dot b/Documentation/media/v4l-drivers/vimc.dot
index 57863a13fa39..e3b41ac2bc46 100644
--- a/Documentation/media/v4l-drivers/vimc.dot
+++ b/Documentation/media/v4l-drivers/vimc.dot
@@ -2,21 +2,15 @@
 
 digraph board {
 	rankdir=TB
-	n00000001 [label="{{} | Sensor A\n/dev/v4l-subdev0 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green]
-	n00000001:port0 -> n00000005:port0 [style=bold]
-	n00000001:port0 -> n0000000b [style=bold]
-	n00000003 [label="{{} | Sensor B\n/dev/v4l-subdev1 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green]
-	n00000003:port0 -> n00000008:port0 [style=bold]
-	n00000003:port0 -> n0000000f [style=bold]
-	n00000005 [label="{{<port0> 0} | Debayer A\n/dev/v4l-subdev2 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
-	n00000005:port1 -> n00000017:port0
-	n00000008 [label="{{<port0> 0} | Debayer B\n/dev/v4l-subdev3 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
-	n00000008:port1 -> n00000017:port0 [style=dashed]
-	n0000000b [label="Raw Capture 0\n/dev/video0", shape=box, style=filled, fillcolor=yellow]
-	n0000000f [label="Raw Capture 1\n/dev/video1", shape=box, style=filled, fillcolor=yellow]
-	n00000013 [label="RGB/YUV Input\n/dev/video2", shape=box, style=filled, fillcolor=yellow]
-	n00000013 -> n00000017:port0 [style=dashed]
-	n00000017 [label="{{<port0> 0} | Scaler\n/dev/v4l-subdev4 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
-	n00000017:port1 -> n0000001a [style=bold]
-	n0000001a [label="RGB/YUV Capture\n/dev/video3", shape=box, style=filled, fillcolor=yellow]
+	n00000001 [label="cap-deb\n/dev/video0", shape=box, style=filled, fillcolor=yellow]
+	n00000005 [label="cap-sen\n/dev/video1", shape=box, style=filled, fillcolor=yellow]
+	n00000009 [label="cap-sca\n/dev/video2", shape=box, style=filled, fillcolor=yellow]
+	n0000000d [label="{{<port0> 0} | sca\n/dev/v4l-subdev0 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
+	n0000000d:port1 -> n00000009 [style=bold]
+	n00000010 [label="{{<port0> 0} | deb\n/dev/v4l-subdev1 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
+	n00000010:port1 -> n00000001 [style=bold]
+	n00000010:port1 -> n0000000d:port0 [style=bold]
+	n00000013 [label="{{} | sen\n/dev/v4l-subdev2 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green]
+	n00000013:port0 -> n00000005 [style=bold]
+	n00000013:port0 -> n00000010:port0 [style=bold]
 }
diff --git a/Documentation/media/v4l-drivers/vimc.rst b/Documentation/media/v4l-drivers/vimc.rst
index a582af0509ee..e5636883545f 100644
--- a/Documentation/media/v4l-drivers/vimc.rst
+++ b/Documentation/media/v4l-drivers/vimc.rst
@@ -1,45 +1,225 @@
 .. SPDX-License-Identifier: GPL-2.0
 
+==========================================
 The Virtual Media Controller Driver (vimc)
 ==========================================
 
-The vimc driver emulates complex video hardware using the V4L2 API and the Media
-API. It has a capture device and three subdevices: sensor, debayer and scaler.
+The vimc driver emulates complex video hardware topologies using the V4L2 API
+and the Media API. It has a capture device and three subdevices:
+sensor, debayer and scaler. It exposes media devices through /dev/mediaX nodes,
+video capture devices through /dev/videoX and sub-devices through /dev/v4l-subdevX.
+
+
+To configure a media device of a given topology, a ConfigFS API is provided.
+
+Configuring a topology through ConfigFS (Experimental)
+======================================================
+
+.. note:: This API is under development and might change in the future.
+
+Mount configfs:
+::
+
+	$ mkdir /configfs
+	$ mount -t configfs none /configfs
+
+When loading the module, you will see a folder named vimc
+::
+
+	$ tree /configfs/
+	/configfs/
+	`-- vimc
+
+Creating a media device
+-----------------------
+
+To create a media device create a new folder under /configfs/vimc/
+
+Example:
+::
+
+	$ mkdir /configfs/vimc/mdev
+	$ tree /configfs/vimc/mdev
+	/configfs/
+	`-- vimc
+	    `-- mdev
+	        `-- hotplug
+
+Creating entities
+-----------------
+
+To create an entity in the media device's topology, create a folder under
+``/configfs/vimc/<mdev-name>/`` with the following format:
+
+	<entity-type>:<entity-name>
+
+Where <entity-type> is one of the following:
+
+- vimc-sensor
+- vimc-scaler
+- vimc-debayer
+- vimc-capture
+
+Example:
+::
+
+	$ mkdir /configfs/vimc/mdev/vimc-sensor:sen
+	$ mkdir /configfs/vimc/mdev/vimc-capture:cap-sen
+	$ tree /configfs/
+	/configfs/
+	`-- vimc
+	    `-- mdev
+		|-- hotplug
+		|-- vimc-capture:cap-sen
+		|   `-- pad:sink:0
+		`-- vimc-sensor:sen
+                    `-- pad:source:0
+
+Default folders are created under the entity directory for each pad of the entity.
+
+Creating links
+--------------
+
+To create a link between two entities, you should create a directory for the link
+under the source pad of the link and then set it to be a symbolic link to the sink pad:
 
-Topology
---------
+Example:
+::
+
+	$ mkdir "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap"
+	$ ln -s "/configfs/vimc/mdev/vimc-capture:cap-sen/pad:sink:0" "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap"
+	$ tree /configfs
+	/configfs
+	`-- vimc
+	    `-- mdev
+	        |-- hotplug
+	        |-- vimc-capture:cap-sen
+	        |   `-- pad:sink:0
+	        `-- vimc-sensor:sen
+	            `-- pad:source:0
+	                `-- to-cap
+	                    |-- enabled
+	                    |-- immutable
+	                    `-- pad:sink:0 -> ../../../../../vimc/mdev/vimc-capture:cap-sen/pad:sink:0
+
+The `enabled` and `immutable` are two boolean attributes of the link corresponding to the link flags
+
+
+Flag values are described in :ref:`Documentation/media/uapi/mediactl/media-types.rst <media-link-flag>`
+( seek for ``MEDIA_LNK_FL_*``)
+
+1 - Enabled
+	Indicates that 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.
+
+Change an attribute of the link by writing "on" or "1" to set it on , and "off" or "0" to set it off
+
+Example:
+::
+
+	$ echo "on" > /configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap/immutable
+
+Activating/Deactivating device
+------------------------------
+
+To activate the device, write one of "plugged", "plug" or "1" to the file
+``/configfs/vimc/<mdev-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 the file
+``/configfs/vimc/<ndev-name>/hotplug``
+
+Example:
+::
+
+	$ echo unplugged > /configfs/vimc/mdev/hotplug
+
+Topology Configuration - Full Example
+-------------------------------------
+
+Here is a full example of a simple topology configuration:
+
+.. code-block:: bash
+
+    # Creating the entities
+    mkdir "/configfs/vimc/mdev"
+    mkdir "/configfs/vimc/mdev/vimc-sensor:sen"
+    mkdir "/configfs/vimc/mdev/vimc-debayer:deb"
+    mkdir "/configfs/vimc/mdev/vimc-scaler:sca"
+    mkdir "/configfs/vimc/mdev/vimc-capture:cap-sca" #/dev/video2
+    mkdir "/configfs/vimc/mdev/vimc-capture:cap-sen" #/dev/video1
+    mkdir "/configfs/vimc/mdev/vimc-capture:cap-deb" #/dev/video0
+
+    # Creating the links
+    #sen -> deb
+    mkdir "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-deb"
+    ln -s "/configfs/vimc/mdev/vimc-debayer:deb/pad:sink:0" "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-deb"
+    echo on > "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-deb/immutable"
+    echo on > "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-deb/enabled"
+
+    #deb -> sca
+    mkdir "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-sca"
+    ln -s "/configfs/vimc/mdev/vimc-scaler:sca/pad:sink:0" "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-sca"
+    echo on > "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-sca/immutable"
+    echo on > "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-sca/enabled"
+
+    #sca -> cap-sca
+    mkdir "/configfs/vimc/mdev/vimc-scaler:sca/pad:source:1/to-cap"
+    ln -s "/configfs/vimc/mdev/vimc-capture:cap-sca/pad:sink:0" "/configfs/vimc/mdev/vimc-scaler:sca/pad:source:1/to-cap"
+    echo on > "/configfs/vimc/mdev/vimc-scaler:sca/pad:source:1/to-cap/immutable"
+    echo on > "/configfs/vimc/mdev/vimc-scaler:sca/pad:source:1/to-cap/enabled"
+
+    #sen -> cap-sen
+    mkdir "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap"
+    ln -s "/configfs/vimc/mdev/vimc-capture:cap-sen/pad:sink:0" "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap"
+    echo on > "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap/immutable"
+    echo on > "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap/enabled"
+
+    #deb -> cap-deb
+    mkdir "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-cap"
+    ln -s "/configfs/vimc/mdev/vimc-capture:cap-deb/pad:sink:0" "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-cap"
+    echo on > "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-cap/immutable"
+    echo on > "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-cap/enabled"
 
-The topology is hardcoded, although you could modify it in vimc-core and
-recompile the driver to achieve your own topology. This is the default topology:
 
 .. _vimc_topology_graph:
 
 .. kernel-figure:: vimc.dot
-    :alt:   Diagram of the default media pipeline topology
+    :alt:   Diagram of the configured simple topology in the example
     :align: center
 
-    Media pipeline graph on vimc
+    Simple Media pipeline graph on vimc configured through configfs
 
-Configuring the topology
-~~~~~~~~~~~~~~~~~~~~~~~~
+Configuring the pipeline formats
+================================
 
-Each subdevice will come with its default configuration (pixelformat, height,
-width, ...). One needs to configure the topology in order to match the
+Each subdevice has a default format configuration (pixelformat, height,
+width, ...). You should configure the formats in order to match the
 configuration on each linked subdevice to stream frames through the pipeline.
-If the configuration doesn't match, the stream will fail. The ``v4l-utils``
+If the configuration doesn't match, streaming will fail. The ``v4l-utils``
 package is a bundle of user-space applications, that comes with ``media-ctl`` and
-``v4l2-ctl`` that can be used to configure the vimc configuration. This sequence
-of commands fits for the default topology:
+``v4l2-ctl`` that can be used to configure the formats of the entities. This sequence
+of commands fits the simple topology created in the full example of topology configuration:
 
 .. code-block:: bash
 
-        media-ctl -d platform:vimc -V '"Sensor A":0[fmt:SBGGR8_1X8/640x480]'
-        media-ctl -d platform:vimc -V '"Debayer A":0[fmt:SBGGR8_1X8/640x480]'
-        media-ctl -d platform:vimc -V '"Sensor B":0[fmt:SBGGR8_1X8/640x480]'
-        media-ctl -d platform:vimc -V '"Debayer B":0[fmt:SBGGR8_1X8/640x480]'
-        v4l2-ctl -z platform:vimc -d "RGB/YUV Capture" -v width=1920,height=1440
-        v4l2-ctl -z platform:vimc -d "Raw Capture 0" -v pixelformat=BA81
-        v4l2-ctl -z platform:vimc -d "Raw Capture 1" -v pixelformat=BA81
+	media-ctl -d platform:vimc-000 -V '"sen":0[fmt:SBGGR8_1X8/640x480]'
+	media-ctl -d platform:vimc-000 -V '"deb":0[fmt:SBGGR8_1X8/640x480]'
+	media-ctl -d platform:vimc-000 -V '"deb":1[fmt:RGB888_1X24/640x480]'
+	media-ctl -d platform:vimc-000 -V '"sca":0[fmt:RGB888_1X24/640x480]'
+	media-ctl -d platform:vimc-000 -V '"sca":1[fmt:RGB888_1X24/640x480]'
+	v4l2-ctl -z platform:vimc-000 -d "cap-sen" -v pixelformat=BA81
+	v4l2-ctl -z platform:vimc-000 -d "cap-deb" -v pixelformat=RGB3
+	# The default scaling value of the scaler is 3, so need to set its capture accordingly
+	v4l2-ctl -z platform:vimc -d "cap-sca" -v pixelformat=RGB3,width=1920,height=1440
 
 Subdevices
 ----------
@@ -61,8 +241,8 @@ vimc-debayer:
 	* 1 Pad source
 
 vimc-scaler:
-	Scale up the image by a factor of 3. E.g.: a 640x480 image becomes a
-        1920x1440 image. (this value can be configured, see at
+	Scales up the image by a factor of 3. E.g.: a 640x480 image becomes a
+        1920x1440 image. (this value can be configured, see
         `Module options`_).
 	Exposes:
 
@@ -77,12 +257,10 @@ vimc-capture:
 	* 1 Pad source
 
 
-        Module options
----------------
-
-Vimc has a few module parameters to configure the driver.
+Module options
+==============
 
-        param=value
+Vimc has 2 module parameters to configure the driver.
 
 * ``sca_mult=<unsigned int>``
 
@@ -98,10 +276,10 @@ Vimc has a few module parameters to configure the driver.
         otherwise the next odd number is considered (the default value is 3).
 
 Source code documentation
--------------------------
+=========================
 
 vimc-streamer
-~~~~~~~~~~~~~
+-------------
 
 .. kernel-doc:: drivers/media/platform/vimc/vimc-streamer.h
    :internal:
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 3/5] media: vimc: Add the implementation for the configfs api
  2019-09-19 20:32 [PATCH 0/5] media: vimc: use configfs in order to configure devices topologies Dafna Hirschfeld
  2019-09-19 20:32 ` [PATCH 1/5] media: vimc: upon streaming, check that the pipeline starts with a source entity Dafna Hirschfeld
  2019-09-19 20:32 ` [PATCH 2/5] docs: media: vimc: Documenting vimc topology configuration using configfs Dafna Hirschfeld
@ 2019-09-19 20:32 ` Dafna Hirschfeld
  2019-09-26 19:25   ` Shuah Khan
  2019-09-27  7:55   ` Andrzej Pietrasiewicz
  2019-09-19 20:32 ` [PATCH 4/5] media: vimc: use configfs instead of having hardcoded configuration Dafna Hirschfeld
                   ` (2 subsequent siblings)
  5 siblings, 2 replies; 20+ messages in thread
From: Dafna Hirschfeld @ 2019-09-19 20:32 UTC (permalink / raw)
  To: linux-media
  Cc: dafna.hirschfeld, laurent.pinchart, helen.koike, ezequiel,
	andre.almeida, skhan, hverkuil, kernel, dafna3

Add the code that implements the usage of configfs in order
to create and configure a device topology from userspace.
The code is only added in this patch but is not used.
It will be used in next patch in the series.

Signed-off-by: Helen Koike <helen.koike@collabora.com>
[refactored for upstream]
Signed-off-by: Dafna Hirschfeld <dafna.hirschfeld@collabora.com>
---
 drivers/media/platform/vimc/Kconfig         |   9 +-
 drivers/media/platform/vimc/Makefile        |   2 +-
 drivers/media/platform/vimc/vimc-capture.c  |  21 +
 drivers/media/platform/vimc/vimc-common.h   |  46 ++
 drivers/media/platform/vimc/vimc-configfs.c | 656 ++++++++++++++++++++
 drivers/media/platform/vimc/vimc-configfs.h |  41 ++
 drivers/media/platform/vimc/vimc-core.c     |  21 +-
 drivers/media/platform/vimc/vimc-debayer.c  |  22 +
 drivers/media/platform/vimc/vimc-scaler.c   |  26 +-
 drivers/media/platform/vimc/vimc-sensor.c   |  21 +
 10 files changed, 856 insertions(+), 9 deletions(-)
 create mode 100644 drivers/media/platform/vimc/vimc-configfs.c
 create mode 100644 drivers/media/platform/vimc/vimc-configfs.h

diff --git a/drivers/media/platform/vimc/Kconfig b/drivers/media/platform/vimc/Kconfig
index bd221d3e1a4a..6d7836d58ef4 100644
--- a/drivers/media/platform/vimc/Kconfig
+++ b/drivers/media/platform/vimc/Kconfig
@@ -1,15 +1,14 @@
 # SPDX-License-Identifier: GPL-2.0-only
 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
 	help
-	  Skeleton driver for Virtual Media Controller
+	  Virtual Media Controller Driver
 
-	  This driver can be compared to the vivid driver for emulating
+	  This driver emulates
 	  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 the configfs API.
 
 	  When in doubt, say N.
diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile
index a53b2b532e9f..eb03d487f308 100644
--- a/drivers/media/platform/vimc/Makefile
+++ b/drivers/media/platform/vimc/Makefile
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0
 vimc-y := vimc-core.o vimc-common.o vimc-streamer.o vimc-capture.o \
-		vimc-debayer.o vimc-scaler.o vimc-sensor.o
+		vimc-debayer.o vimc-scaler.o vimc-sensor.o  vimc-configfs.o
 
 obj-$(CONFIG_VIDEO_VIMC) += vimc.o
 
diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c
index 602f80323031..5cc419e76ce7 100644
--- a/drivers/media/platform/vimc/vimc-capture.c
+++ b/drivers/media/platform/vimc/vimc-capture.c
@@ -9,6 +9,7 @@
 #include <media/videobuf2-core.h>
 #include <media/videobuf2-vmalloc.h>
 
+#include "vimc-configfs.h"
 #include "vimc-common.h"
 #include "vimc-streamer.h"
 
@@ -488,3 +489,23 @@ struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc,
 
 	return NULL;
 }
+
+static void vimc_cap_create_cfs_pads(struct config_group *ent_group)
+{
+	vimc_cfs_add_sink_pad_api(ent_group, 0, VIMC_CFS_SINK_PAD_NUM(0));
+}
+
+struct vimc_cfs_drv vimc_cap_cfs_drv = {
+	.name = VIMC_CAP_NAME,
+	.create_pads = vimc_cap_create_cfs_pads,
+};
+
+__exit void vimc_cap_exit(void)
+{
+	vimc_cfs_drv_unregister(&vimc_cap_cfs_drv);
+}
+
+__init void vimc_cap_init(void)
+{
+	vimc_cfs_drv_register(&vimc_cap_cfs_drv);
+}
diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h
index 698db7c07645..e0e3b3ab7b19 100644
--- a/drivers/media/platform/vimc/vimc-common.h
+++ b/drivers/media/platform/vimc/vimc-common.h
@@ -14,6 +14,7 @@
 #include <media/v4l2-device.h>
 
 #define VIMC_PDEV_NAME "vimc"
+#define VIMC_MAX_NAME_LEN V4L2_SUBDEV_NAME_SIZE
 
 /* VIMC-specific controls */
 #define VIMC_CID_VIMC_BASE		(0x00f00000 | 0xf000)
@@ -31,6 +32,11 @@
 #define VIMC_IS_SRC(pad)	(pad)
 #define VIMC_IS_SINK(pad)	(!(pad))
 
+#define VIMC_DEB_NAME "vimc-debayer"
+#define VIMC_SEN_NAME "vimc-sensor"
+#define VIMC_SCA_NAME "vimc-scaler"
+#define VIMC_CAP_NAME "vimc-capture"
+
 /**
  * struct vimc_colorimetry_clamp - Adjust colorimetry parameters
  *
@@ -72,6 +78,18 @@ struct vimc_platform_data {
 	char entity_name[32];
 };
 
+/**
+ * struct vimc_platform_data_core - platform data to the core
+ *
+ * @ents: list of vimc_entity_data objects allocated by the configfs
+ * @links: list of vimc_links objects allocated by the configfs
+ *
+ */
+struct vimc_platform_data_core {
+	struct list_head *ents;
+	struct list_head *links;
+};
+
 /**
  * struct vimc_pix_map - maps media bus code with v4l2 pixel format
  *
@@ -92,6 +110,8 @@ struct vimc_pix_map {
 /**
  * struct vimc_ent_device - core struct that represents a node in the topology
  *
+ * @name:		the name of the entity
+ * @drv_name:		the name of the driver of the entity
  * @ent:		the pointer to struct media_entity for the node
  * @pads:		the list of pads of the node
  * @process_frame:	callback send a frame to that node
@@ -108,12 +128,30 @@ struct vimc_pix_map {
  * media_entity
  */
 struct vimc_ent_device {
+	char name[VIMC_MAX_NAME_LEN];
+	char drv_name[VIMC_MAX_NAME_LEN];
 	struct media_entity *ent;
 	struct media_pad *pads;
 	void * (*process_frame)(struct vimc_ent_device *ved,
 				const void *frame);
 	void (*vdev_get_format)(struct vimc_ent_device *ved,
 			      struct v4l2_pix_format *fmt);
+	struct list_head list;
+};
+
+struct vimc_entity_data {
+	char name[VIMC_MAX_NAME_LEN];
+	char drv_name[VIMC_MAX_NAME_LEN];
+	struct list_head list;
+};
+
+struct vimc_link {
+	char source_name[VIMC_MAX_NAME_LEN];
+	char sink_name[VIMC_MAX_NAME_LEN];
+	u16 source_pad;
+	u16 sink_pad;
+	u32 flags;
+	struct list_head list;
 };
 
 /**
@@ -156,18 +194,26 @@ struct vimc_ent_config {
 struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc,
 				     const char *vcfg_name);
 void vimc_cap_rm(struct vimc_device *vimc, struct vimc_ent_device *ved);
+void vimc_cap_init(void);
+void vimc_cap_exit(void);
 
 struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc,
 				     const char *vcfg_name);
 void vimc_deb_rm(struct vimc_device *vimc, struct vimc_ent_device *ved);
+void vimc_deb_init(void);
+void vimc_deb_exit(void);
 
 struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc,
 				     const char *vcfg_name);
 void vimc_sca_rm(struct vimc_device *vimc, struct vimc_ent_device *ved);
+void vimc_sca_init(void);
+void vimc_sca_exit(void);
 
 struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc,
 				     const char *vcfg_name);
 void vimc_sen_rm(struct vimc_device *vimc, struct vimc_ent_device *ved);
+void vimc_sen_init(void);
+void vimc_sen_exit(void);
 
 /**
  * vimc_pads_init - initialize pads
diff --git a/drivers/media/platform/vimc/vimc-configfs.c b/drivers/media/platform/vimc/vimc-configfs.c
new file mode 100644
index 000000000000..933aece0bb5f
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-configfs.c
@@ -0,0 +1,656 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * vimc-configfs.c Virtual Media Controller Driver
+ *
+ * Copyright (C) 2018 Helen Koike <helen.koike@collabora.com>
+ */
+
+#include <linux/platform_device.h>
+
+#include "vimc-common.h"
+#include "vimc-configfs.h"
+
+#define CHAR_SEPARATOR ':'
+#define CFS_SUBSYS_NAME "vimc"
+#define MAX_PAD_DIGI_NUM 4
+
+#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: %s:" pr_fmt(fmt), (ci)->ci_name, __func__, ##__VA_ARGS__)
+#define cg_dbg(cg, ...) ci_dbg(&(cg)->cg_item, ##__VA_ARGS__)
+
+#define IS_PLUGGED(cfs) (!!(cfs)->pdev)
+
+// currently there is no entity with more than two pads, this will
+// change when adding the splitter entity
+#define VIMC_ENT_MAX_PADS 2
+
+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 list_head ents;
+	struct list_head links;
+	struct platform_device *pdev;
+	struct vimc_platform_data_core pdata;
+	struct config_group gdev;
+};
+
+/* Structure which describes individual configuration for each entity */
+struct vimc_cfs_ent {
+	struct vimc_entity_data ent;
+	struct config_group cg;
+	struct config_group pad_groups[VIMC_ENT_MAX_PADS];
+};
+
+struct vimc_cfs_link {
+	struct vimc_link link;
+	struct config_item ci;
+};
+
+void vimc_cfs_drv_register(struct vimc_cfs_drv *c_drv)
+{
+	pr_debug("%s: adding driver %s\n", __func__, c_drv->name);
+	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)
+{
+	pr_debug("%s: removing driver %s\n", __func__, c_drv->name);
+	list_del(&c_drv->list);
+}
+EXPORT_SYMBOL_GPL(vimc_cfs_drv_unregister);
+
+/* --------------------------------------------------------------------------
+ * Platform Device builders
+ */
+
+static void vimc_cfs_device_unplug(struct vimc_cfs_device *cfs)
+{
+	dev_dbg(&cfs->pdev->dev, "Unplugging device\n");
+	platform_device_unregister(cfs->pdev);
+
+	cfs->pdev = NULL;
+}
+
+static int vimc_cfs_device_plug(struct vimc_cfs_device *cfs)
+{
+	cg_dbg(&cfs->gdev, "Plugging device\n");
+
+	if (list_empty(&cfs->ents)) {
+		cg_warn(&cfs->gdev,
+			"At least one entity is required to plug the device\n");
+		return -EINVAL;
+	}
+
+	cfs->pdev = platform_device_register_data(NULL, "vimc-core",
+						  PLATFORM_DEVID_AUTO,
+						  &cfs->pdata,
+						  sizeof(cfs->pdata));
+	if (IS_ERR(cfs->pdev)) {
+		int ret = PTR_ERR(cfs->pdev);
+
+		cfs->pdev = NULL;
+		return ret;
+	}
+
+	return 0;
+}
+
+/* --------------------------------------------------------------------------
+ * Links
+ */
+
+static ssize_t vimc_cfs_link_store_flag(struct config_item *item,
+					const char *buf,
+					size_t size, u32 flag)
+{
+	struct vimc_cfs_link *c_link =
+		container_of(item, struct vimc_cfs_link, ci);
+
+	if (!strncmp(buf, "on\n", 4) || !strncmp(buf, "1\n", 3)) {
+		c_link->link.flags |= MEDIA_LNK_FL_ENABLED;
+		return strlen(buf);
+	} else if (!strncmp(buf, "off\n", 5) || !strncmp(buf, "0\n", 3)) {
+		c_link->link.flags &= ~MEDIA_LNK_FL_ENABLED;
+		return strlen(buf);
+	}
+	return -EINVAL;
+}
+
+static ssize_t vimc_cfs_link_show_flag(struct config_item *item,
+				       char *buf, u32 flag)
+{
+	struct vimc_cfs_link *c_link = container_of(item,
+						    struct vimc_cfs_link, ci);
+
+	if (c_link->link.flags & flag)
+		strscpy(buf, "on\n", 4);
+	else
+		strscpy(buf, "off\n", 5);
+	return strlen(buf);
+}
+
+static ssize_t vimc_cfs_link_enabled_store(struct config_item *item,
+					   const char *buf,
+					   size_t size)
+{
+	return vimc_cfs_link_store_flag(item, buf, size, MEDIA_LNK_FL_ENABLED);
+}
+
+
+static ssize_t vimc_cfs_link_enabled_show(struct config_item *item,
+					  char *buf)
+{
+	return vimc_cfs_link_show_flag(item, buf, MEDIA_LNK_FL_ENABLED);
+}
+
+CONFIGFS_ATTR(vimc_cfs_link_, enabled);
+
+static ssize_t vimc_cfs_link_immutable_show(struct config_item *item,
+						 char *buf)
+{
+	return vimc_cfs_link_show_flag(item, buf, MEDIA_LNK_FL_IMMUTABLE);
+}
+
+static ssize_t vimc_cfs_link_immutable_store(struct config_item *item,
+						   const char *buf, size_t size)
+{
+	return vimc_cfs_link_store_flag(item, buf, size,
+					MEDIA_LNK_FL_IMMUTABLE);
+}
+
+CONFIGFS_ATTR(vimc_cfs_link_, immutable);
+
+static void vimc_cfs_fill_link_data(struct config_item *src,
+				struct config_item *target)
+{
+	struct config_item *src_ent_ci = src->ci_parent->ci_parent;
+	struct config_item *trgt_ent_ci = target->ci_parent;
+	struct vimc_cfs_link *c_link =
+			container_of(src, struct vimc_cfs_link, ci);
+	struct vimc_cfs_ent *vimc_src_ent = container_of(src_ent_ci,
+							 struct vimc_cfs_ent,
+							 cg.cg_item);
+	struct vimc_cfs_ent *vimc_trgt_ent = container_of(trgt_ent_ci,
+							 struct vimc_cfs_ent,
+							 cg.cg_item);
+	struct vimc_cfs_device *cfs = container_of(src_ent_ci->ci_parent,
+							 struct vimc_cfs_device,
+							 gdev.cg_item);
+
+	if (IS_PLUGGED(cfs))
+		vimc_cfs_device_unplug(cfs);
+
+	/* src and target validation already done in the allow_link callback,
+	 * so there is no need to check sscanf result
+	 */
+	sscanf(src->ci_parent->ci_name, VIMC_CFS_SRC_PAD "%hu",
+	       &c_link->link.source_pad);
+	sscanf(target->ci_parent->ci_name, VIMC_CFS_SINK_PAD "%hu",
+	       &c_link->link.sink_pad);
+
+	strscpy(c_link->link.source_name, vimc_src_ent->ent.name,
+		sizeof(c_link->link.source_name));
+	strscpy(c_link->link.sink_name, vimc_trgt_ent->ent.name,
+		sizeof(c_link->link.sink_name));
+
+	cg_dbg(&cfs->gdev, "creating link %s:%u->%s:%u\n",
+			c_link->link.source_name, c_link->link.source_pad,
+			c_link->link.sink_name, c_link->link.sink_pad);
+
+	list_add(&c_link->link.list, &cfs->links);
+}
+
+static void vimc_cfs_drop_link(struct config_item *src,
+			       struct config_item *target)
+{
+	struct vimc_cfs_link *c_link = container_of(src,
+						    struct vimc_cfs_link, ci);
+
+	ci_dbg(&c_link->ci, "dropping link %s:%u->%s:%u\n",
+	       c_link->link.source_name, c_link->link.source_pad,
+	       c_link->link.sink_name, c_link->link.sink_pad);
+
+	c_link->link.source_pad = 0;
+	c_link->link.sink_pad = 0;
+	memset(c_link->link.source_name, 0, sizeof(c_link->link.source_name));
+	memset(c_link->link.sink_name, 0, sizeof(c_link->link.sink_name));
+	list_del(&c_link->link.list);
+}
+
+int vimc_cfs_allow_link(struct config_item *src, struct config_item *target)
+{
+	struct config_item *s = src;
+	struct config_item *t = target;
+	struct config_item *src_ent_ci, *trgt_ent_ci;
+	int i;
+	int ret = 0;
+	struct configfs_subsystem *subsys = &vimc_cfs_subsys.subsys;
+
+	if (strncmp(target->ci_name, VIMC_CFS_SINK_PAD,
+		    sizeof(VIMC_CFS_SINK_PAD) - 1)) {
+		ci_warn(src, "link target (%s) does not start with '%s'\n",
+			target->ci_name, VIMC_CFS_SINK_PAD);
+		return -EINVAL;
+	}
+
+	mutex_lock(&subsys->su_mutex);
+	for (i = 0; i < 3; i++)
+		s = s->ci_parent;
+
+	for (i = 0; i < 2; i++) {
+		t = t->ci_parent;
+		if (!t) {
+			ci_warn(src, "link target (%s) is not a sink pad\n",
+				target->ci_name);
+			ret = -EINVAL;
+			goto end;
+		}
+	}
+
+	if (s != t) {
+		ci_warn(src, "not allow linking between different vimcs devices: (%s) and (%s)\n",
+			s->ci_name, t->ci_name);
+		ret = -EINVAL;
+		goto end;
+	}
+	src_ent_ci = src->ci_parent->ci_parent;
+	trgt_ent_ci = target->ci_parent;
+	if (src_ent_ci == trgt_ent_ci) {
+		ci_warn(src, "both pads belong to the same entity (%s) - this is not allowed\n",
+				src_ent_ci->ci_name);
+		ret = -EINVAL;
+		goto end;
+	}
+
+	vimc_cfs_fill_link_data(src, target);
+end:
+	mutex_unlock(&subsys->su_mutex);
+	return ret;
+}
+
+static void vimc_cfs_prepare_link_release(struct config_item *item)
+{
+	struct vimc_cfs_link *c_link = container_of(item,
+						    struct vimc_cfs_link, ci);
+
+	ci_dbg(item, "releasing link '%s'", item->ci_name);
+	kfree(c_link);
+}
+
+static struct configfs_attribute *vimc_cfs_link_attrs[] = {
+	&vimc_cfs_link_attr_enabled,
+	&vimc_cfs_link_attr_immutable,
+	NULL,
+};
+
+static struct configfs_item_operations vimc_cfs_allow_link_item_ops = {
+	.allow_link = vimc_cfs_allow_link,
+	.drop_link = vimc_cfs_drop_link,
+	.release = vimc_cfs_prepare_link_release,
+};
+
+struct config_item_type vimc_cfs_allow_link_type = {
+	.ct_owner = THIS_MODULE,
+	.ct_item_ops = &vimc_cfs_allow_link_item_ops,
+	.ct_attrs = vimc_cfs_link_attrs,
+};
+
+/* --------------------------------------------------------------------------
+ * Source pad instance
+ */
+
+static void vimc_cfs_src_pad_prepare_link_drop_item(
+		struct config_group *src_pad_group,
+		struct config_item *link_item)
+{
+
+	struct config_item *cfs_item;
+	struct vimc_cfs_device *cfs;
+
+	cfs_item = src_pad_group->cg_item.ci_parent->ci_parent;
+	cfs = container_of(cfs_item->ci_parent, struct vimc_cfs_device,
+			   gdev.cg_item);
+	cg_dbg(&cfs->gdev, "dropping prepared link '%s'\n",
+	       link_item->ci_name);
+	if (IS_PLUGGED(cfs))
+		vimc_cfs_device_unplug(cfs);
+	config_item_put(link_item);
+}
+
+static struct config_item *vimc_cfs_src_pad_prepare_link_make_item(
+			   struct config_group *group,
+			   const char *name)
+{
+	struct vimc_cfs_link *c_link = kzalloc(sizeof(*c_link), GFP_KERNEL);
+
+	cg_dbg(group, "link name is '%s'\n", name);
+	config_item_init_type_name(&c_link->ci, name,
+				   &vimc_cfs_allow_link_type);
+	return &c_link->ci;
+}
+struct config_item_type vimc_cfs_empty_type = {
+	.ct_owner = THIS_MODULE,
+};
+
+static struct configfs_group_operations vimc_cfs_src_pad_ops = {
+	.make_item = vimc_cfs_src_pad_prepare_link_make_item,
+	.drop_item = vimc_cfs_src_pad_prepare_link_drop_item
+};
+
+struct config_item_type vimc_cfs_src_pad_type = {
+	.ct_owner = THIS_MODULE,
+	.ct_group_ops = &vimc_cfs_src_pad_ops,
+};
+
+/* --------------------------------------------------------------------------
+ * Device instance
+ */
+
+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);
+
+	ci_dbg(item, "releasing entity '%s' of driver '%s'",
+	       c_ent->ent.name,
+	       c_ent->ent.drv_name);
+	kfree(c_ent);
+}
+
+static struct configfs_item_operations vimc_cfs_ent_item_ops = {
+	.release	= vimc_cfs_ent_release,
+};
+
+struct config_item_type vimc_cfs_ent_type = {
+	.ct_owner = THIS_MODULE,
+	.ct_item_ops = &vimc_cfs_ent_item_ops,
+};
+
+void vimc_cfs_add_sink_pad_api(struct config_group *ent_group,
+					int pad_idx, const char *name)
+{
+	struct vimc_cfs_ent *c_ent = container_of(ent_group,
+						  struct vimc_cfs_ent, cg);
+
+	config_group_init_type_name(&c_ent->pad_groups[pad_idx], name,
+				    &vimc_cfs_empty_type);
+	configfs_add_default_group(&c_ent->pad_groups[pad_idx], ent_group);
+}
+EXPORT_SYMBOL_GPL(vimc_cfs_add_sink_pad_api);
+
+void vimc_cfs_add_source_pad_api(struct config_group *ent_group,
+					  int pad_idx, const char *name)
+{
+	struct vimc_cfs_ent *c_ent = container_of(ent_group,
+						  struct vimc_cfs_ent, cg);
+
+	config_group_init_type_name(&c_ent->pad_groups[pad_idx], name,
+				    &vimc_cfs_src_pad_type);
+	configfs_add_default_group(&c_ent->pad_groups[pad_idx], ent_group);
+}
+EXPORT_SYMBOL_GPL(vimc_cfs_add_source_pad_api);
+
+static void vimc_cfs_dev_drop_ent_item(struct config_group *dev_group,
+					   struct config_item *ent_item)
+{
+	struct vimc_cfs_ent *c_ent = container_of(ent_item, struct vimc_cfs_ent,
+						  cg.cg_item);
+	struct vimc_cfs_device *cfs = container_of(dev_group,
+						   struct vimc_cfs_device,
+						   gdev);
+
+	if (IS_PLUGGED(cfs))
+		vimc_cfs_device_unplug(cfs);
+
+	cg_dbg(&cfs->gdev, "dropping entity '%s' of driver '%s'",
+	       c_ent->ent.name, c_ent->ent.drv_name);
+	list_del(&c_ent->ent.list);
+	config_item_put(ent_item);
+}
+
+static struct config_group *vimc_cfs_dev_make_ent_group(
+			struct config_group *group, const char *name)
+{
+	struct vimc_cfs_device *cfs = container_of(group,
+						   struct vimc_cfs_device,
+						   gdev);
+	char *ent_name, *sep = strchr(name, CHAR_SEPARATOR);
+	struct vimc_cfs_ent *c_ent;
+	struct vimc_entity_data *ent;
+	size_t drv_namelen;
+	struct vimc_cfs_drv *c_drv = NULL;
+
+	cg_dbg(group, "trying to make entity '%s'\n", name);
+	if (IS_PLUGGED(cfs))
+		vimc_cfs_device_unplug(cfs);
+
+	/* Parse format "drv_name:ent_name" */
+	if (!sep) {
+		cg_warn(&cfs->gdev,
+			"Could not find separator '%c'\n", CHAR_SEPARATOR);
+		goto syntax_error;
+	}
+	drv_namelen = (size_t)(sep - name);
+	ent_name = &sep[1];
+	if (!*ent_name || !drv_namelen) {
+		cg_warn(&cfs->gdev,
+			"%s: Driver name and entity name can't be empty.\n",
+		       name);
+		goto syntax_error;
+	}
+	if (drv_namelen >= VIMC_MAX_NAME_LEN) {
+		cg_err(&cfs->gdev,
+		       "%s: Driver name length should be less than %u.\n",
+		       name, VIMC_MAX_NAME_LEN);
+		goto syntax_error;
+	}
+	list_for_each_entry(ent, &cfs->ents, list) {
+		if (!strncmp(ent->name, ent_name, sizeof(ent->name))) {
+			cg_err(&cfs->gdev, "entity `%s` already exist\n",
+			       ent->name);
+			goto syntax_error;
+		}
+	}
+
+	c_ent = kzalloc(sizeof(*c_ent), GFP_KERNEL);
+	if (!c_ent)
+		return ERR_PTR(-ENOMEM);
+
+	strscpy(c_ent->ent.drv_name, name, drv_namelen + 1);
+	strscpy(c_ent->ent.name, ent_name, sizeof(c_ent->ent.name));
+
+	/* Configure group */
+
+	/* TODO: add support for hotplug at entity level */
+	list_for_each_entry(c_drv, &vimc_cfs_subsys.drvs, list) {
+		if (!strcmp(c_ent->ent.drv_name, c_drv->name)) {
+			config_group_init_type_name(&c_ent->cg, name,
+						    &vimc_cfs_ent_type);
+			if (c_drv->create_pads)
+				c_drv->create_pads(&c_ent->cg);
+			list_add(&c_ent->ent.list, &cfs->ents);
+			return &c_ent->cg;
+		}
+	}
+	cg_warn(&cfs->gdev, "entity type %s not found\n", c_ent->ent.drv_name);
+	kfree(c_ent);
+	return ERR_PTR(-EINVAL);
+
+syntax_error:
+	cg_err(&cfs->gdev,
+		"couldn't create entity %s, wrong syntax.", name);
+	return ERR_PTR(-EINVAL);
+}
+
+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_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_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_, hotplug);
+
+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);
+
+	ci_dbg(item, "releasing dev %s\n", item->ci_name);
+	kfree(cfs);
+}
+
+static struct configfs_group_operations vimc_cfs_dev_group_ops = {
+	.make_group = vimc_cfs_dev_make_ent_group,
+	.drop_item = vimc_cfs_dev_drop_ent_item,
+};
+
+static struct configfs_item_operations vimc_cfs_dev_item_ops = {
+	.release = vimc_cfs_dev_release,
+};
+
+static struct configfs_attribute *vimc_cfs_dev_attrs[] = {
+	&vimc_cfs_dev_attr_hotplug,
+	NULL,
+};
+
+static struct config_item_type vimc_cfs_dev_type = {
+	.ct_group_ops = &vimc_cfs_dev_group_ops,
+	.ct_item_ops = &vimc_cfs_dev_item_ops,
+	.ct_attrs = vimc_cfs_dev_attrs,
+	.ct_owner = THIS_MODULE,
+};
+
+/* --------------------------------------------------------------------------
+ * Subsystem
+ * --------------------------------------------------------------------------
+ */
+
+static void vimc_cfs_subsys_drop_dev_item(struct config_group *group,
+				   struct config_item *item)
+{
+	struct vimc_cfs_device *cfs = container_of(to_config_group(item),
+						   struct vimc_cfs_device,
+						   gdev);
+
+	if (IS_PLUGGED(cfs))
+		vimc_cfs_device_unplug(cfs);
+	cg_dbg(&cfs->gdev, "dropping subsys '%s'", item->ci_name);
+	config_item_put(item);
+}
+
+static struct config_group *vimc_cfs_subsys_make_dev_group(
+				struct config_group *group, const char *name)
+{
+	struct vimc_cfs_device *cfs = kzalloc(sizeof(*cfs), GFP_KERNEL);
+
+	if (!cfs)
+		return ERR_PTR(-ENOMEM);
+
+	cg_dbg(&cfs->gdev, "making dev group '%s'", name);
+	/* Configure platform data */
+	INIT_LIST_HEAD(&cfs->ents);
+	INIT_LIST_HEAD(&cfs->links);
+	cfs->pdata.links = &cfs->links;
+	cfs->pdata.ents = &cfs->ents;
+
+	config_group_init_type_name(&cfs->gdev, name, &vimc_cfs_dev_type);
+
+	return &cfs->gdev;
+}
+
+static struct configfs_group_operations vimc_cfs_subsys_group_ops = {
+	.make_group	= vimc_cfs_subsys_make_dev_group,
+	.drop_item	= vimc_cfs_subsys_drop_dev_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..0d3afdb31fdd
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-configfs.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * vimc-configfs.h Virtual Media Controller Driver
+ *
+ * Copyright (C) 2018 Helen Koike <helen.koike@collabora.com>
+ */
+
+#ifndef _VIMC_CONFIGFS_H_
+#define _VIMC_CONFIGFS_H_
+
+#include <linux/configfs.h>
+
+#define VIMC_CFS_SRC_PAD "pad:source:"
+#define VIMC_CFS_SINK_PAD "pad:sink:"
+
+#define VIMC_CFS_SRC_PAD_NUM(n) "pad:source:" #n
+#define VIMC_CFS_SINK_PAD_NUM(n) "pad:sink:" #n
+
+extern struct config_item_type vimc_default_cfs_pad_type;
+
+void vimc_cfs_add_source_pad_api(struct config_group *ent_group,
+					int pad_idx, const char *name);
+
+void vimc_cfs_add_sink_pad_api(struct config_group *ent_group,
+				      int pad_idx, const char *name);
+struct vimc_cfs_drv {
+	const char *name;
+	struct list_head list;
+
+	void (*const create_pads)(struct config_group *parent);
+};
+
+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 6e3e5c91ae39..476bad2cb2de 100644
--- a/drivers/media/platform/vimc/vimc-core.c
+++ b/drivers/media/platform/vimc/vimc-core.c
@@ -15,6 +15,8 @@
 
 #define VIMC_MDEV_MODEL_NAME "VIMC MDEV"
 
+#include "vimc-configfs.h"
+
 #define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) {	\
 	.src_ent = src,						\
 	.src_pad = srcpad,					\
@@ -335,13 +337,30 @@ static int __init vimc_init(void)
 		return ret;
 	}
 
+	ret = vimc_cfs_subsys_register();
+	if (ret) {
+		pr_err("%s: configfs subsys registration failed (%d)\n",
+		       __func__, ret);
+		platform_driver_unregister(&vimc_pdrv);
+		return ret;
+	}
+
+	vimc_sen_init();
+	vimc_deb_init();
+	vimc_sca_init();
+	vimc_cap_init();
 	return 0;
 }
 
 static void __exit vimc_exit(void)
 {
-	platform_driver_unregister(&vimc_pdrv);
+	vimc_sen_exit();
+	vimc_deb_exit();
+	vimc_sca_exit();
+	vimc_cap_exit();
 
+	vimc_cfs_subsys_unregister();
+	platform_driver_unregister(&vimc_pdrv);
 	platform_device_unregister(&vimc_dev.pdev);
 }
 
diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c
index feac47d79449..e461b155e514 100644
--- a/drivers/media/platform/vimc/vimc-debayer.c
+++ b/drivers/media/platform/vimc/vimc-debayer.c
@@ -12,6 +12,7 @@
 #include <media/v4l2-subdev.h>
 
 #include "vimc-common.h"
+#include "vimc-configfs.h"
 
 static unsigned int deb_mean_win_size = 3;
 module_param(deb_mean_win_size, uint, 0000);
@@ -533,3 +534,24 @@ struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc,
 
 	return &vdeb->ved;
 }
+
+static void vimc_deb_create_cfs_pads(struct config_group *ent_group)
+{
+	vimc_cfs_add_source_pad_api(ent_group, 0, VIMC_CFS_SRC_PAD_NUM(1));
+	vimc_cfs_add_sink_pad_api(ent_group, 1, VIMC_CFS_SINK_PAD_NUM(0));
+}
+
+struct vimc_cfs_drv vimc_deb_cfs_drv = {
+	.name = VIMC_DEB_NAME,
+	.create_pads = vimc_deb_create_cfs_pads,
+};
+
+__exit void vimc_deb_exit(void)
+{
+	vimc_cfs_drv_unregister(&vimc_deb_cfs_drv);
+}
+
+__init void vimc_deb_init(void)
+{
+	vimc_cfs_drv_register(&vimc_deb_cfs_drv);
+}
diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c
index a6a3cc5be872..e5cf0073d68a 100644
--- a/drivers/media/platform/vimc/vimc-scaler.c
+++ b/drivers/media/platform/vimc/vimc-scaler.c
@@ -10,6 +10,7 @@
 #include <linux/v4l2-mediabus.h>
 #include <media/v4l2-subdev.h>
 
+#include "vimc-configfs.h"
 #include "vimc-common.h"
 
 static unsigned int sca_mult = 3;
@@ -373,14 +374,35 @@ struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc,
 				   &vimc_sca_int_ops, &vimc_sca_ops);
 	if (ret) {
 		kfree(vsca);
-		return NULL;
+		return ERR_PTR(ret);
 	}
 
 	vsca->ved.process_frame = vimc_sca_process_frame;
-	vsca->dev = &vimc->pdev.dev;
+	vsca->dev = vimc->mdev.dev;
 
 	/* Initialize the frame format */
 	vsca->sink_fmt = sink_fmt_default;
 
 	return &vsca->ved;
 }
+
+static void vimc_sca_create_cfs_pads(struct config_group *ent_group)
+{
+	vimc_cfs_add_source_pad_api(ent_group, 0, VIMC_CFS_SRC_PAD_NUM(1));
+	vimc_cfs_add_sink_pad_api(ent_group, 1, VIMC_CFS_SINK_PAD_NUM(0));
+}
+
+struct vimc_cfs_drv vimc_sca_cfs_drv = {
+	.name = VIMC_SCA_NAME,
+	.create_pads = vimc_sca_create_cfs_pads,
+};
+
+__exit void vimc_sca_exit(void)
+{
+	vimc_cfs_drv_unregister(&vimc_sca_cfs_drv);
+}
+
+__init void vimc_sca_init(void)
+{
+	vimc_cfs_drv_register(&vimc_sca_cfs_drv);
+}
diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c
index 46dc6a535abe..22e3ad98c818 100644
--- a/drivers/media/platform/vimc/vimc-sensor.c
+++ b/drivers/media/platform/vimc/vimc-sensor.c
@@ -12,6 +12,7 @@
 #include <media/v4l2-subdev.h>
 #include <media/tpg/v4l2-tpg.h>
 
+#include "vimc-configfs.h"
 #include "vimc-common.h"
 
 struct vimc_sen_device {
@@ -391,3 +392,23 @@ struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc,
 
 	return NULL;
 }
+
+static void vimc_sen_create_cfs_pads(struct config_group *ent_group)
+{
+	vimc_cfs_add_source_pad_api(ent_group, 0, VIMC_CFS_SRC_PAD_NUM(0));
+}
+
+struct vimc_cfs_drv vimc_sen_cfs_drv = {
+	.name = VIMC_SEN_NAME,
+	.create_pads = vimc_sen_create_cfs_pads,
+};
+
+__exit void vimc_sen_exit(void)
+{
+	vimc_cfs_drv_unregister(&vimc_sen_cfs_drv);
+}
+
+__init void vimc_sen_init(void)
+{
+	vimc_cfs_drv_register(&vimc_sen_cfs_drv);
+}
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 4/5] media: vimc: use configfs instead of having hardcoded configuration
  2019-09-19 20:32 [PATCH 0/5] media: vimc: use configfs in order to configure devices topologies Dafna Hirschfeld
                   ` (2 preceding siblings ...)
  2019-09-19 20:32 ` [PATCH 3/5] media: vimc: Add the implementation for the configfs api Dafna Hirschfeld
@ 2019-09-19 20:32 ` Dafna Hirschfeld
  2019-09-26 19:41   ` Shuah Khan
  2019-09-19 20:32 ` [PATCH 5/5] media: vimc: Add device index to the bus_info Dafna Hirschfeld
  2019-09-20 13:17 ` [PATCH 0/5] media: vimc: use configfs in order to configure devices topologies Hans Verkuil
  5 siblings, 1 reply; 20+ messages in thread
From: Dafna Hirschfeld @ 2019-09-19 20:32 UTC (permalink / raw)
  To: linux-media
  Cc: dafna.hirschfeld, laurent.pinchart, helen.koike, ezequiel,
	andre.almeida, skhan, hverkuil, kernel, dafna3

Use configfs in order to create a device and set its topology
and remove the hardcoded device.

Signed-off-by: Helen Koike <helen.koike@collabora.com>
[refactored for upstream]
Signed-off-by: Dafna Hirschfeld <dafna.hirschfeld@collabora.com>
---
 drivers/media/platform/vimc/vimc-capture.c  |  21 +-
 drivers/media/platform/vimc/vimc-common.h   |  50 +--
 drivers/media/platform/vimc/vimc-configfs.c |   6 +-
 drivers/media/platform/vimc/vimc-core.c     | 319 ++++++++------------
 drivers/media/platform/vimc/vimc-debayer.c  |  13 +-
 drivers/media/platform/vimc/vimc-scaler.c   |   9 +-
 drivers/media/platform/vimc/vimc-sensor.c   |  12 +-
 7 files changed, 170 insertions(+), 260 deletions(-)

diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c
index 5cc419e76ce7..6ccf879c0b75 100644
--- a/drivers/media/platform/vimc/vimc-capture.c
+++ b/drivers/media/platform/vimc/vimc-capture.c
@@ -335,11 +335,12 @@ static void vimc_cap_release(struct video_device *vdev)
 	kfree(vcap);
 }
 
-void vimc_cap_rm(struct vimc_device *vimc, struct vimc_ent_device *ved)
+void vimc_cap_rm(struct vimc_ent_device *ved)
 {
-	struct vimc_cap_device *vcap;
+	struct vimc_cap_device *vcap = container_of(ved,
+						    struct vimc_cap_device,
+						    ved);
 
-	vcap = container_of(ved, struct vimc_cap_device, ved);
 	vb2_queue_release(&vcap->queue);
 	media_entity_cleanup(ved->ent);
 	video_unregister_device(&vcap->vdev);
@@ -397,8 +398,9 @@ struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc,
 	/* Allocate the vimc_cap_device struct */
 	vcap = kzalloc(sizeof(*vcap), GFP_KERNEL);
 	if (!vcap)
-		return NULL;
+		return ERR_PTR(-ENOMEM);
 
+	strscpy(vcap->ved.name, vcfg_name, sizeof(vcap->ved.name));
 	/* Allocate the pads */
 	vcap->ved.pads =
 		vimc_pads_init(1, (const unsigned long[1]) {MEDIA_PAD_FL_SINK});
@@ -408,7 +410,7 @@ struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc,
 	}
 
 	/* Initialize the media entity */
-	vcap->vdev.entity.name = vcfg_name;
+	vcap->vdev.entity.name = vcap->ved.name;
 	vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L;
 	ret = media_entity_pads_init(&vcap->vdev.entity,
 				     1, vcap->ved.pads);
@@ -432,7 +434,7 @@ struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc,
 
 	ret = vb2_queue_init(q);
 	if (ret) {
-		dev_err(&vimc->pdev.dev, "%s: vb2 queue init failed (err=%d)\n",
+		dev_err(vimc->mdev.dev, "%s: vb2 queue init failed (err=%d)\n",
 			vcfg_name, ret);
 		goto err_clean_m_ent;
 	}
@@ -452,7 +454,8 @@ struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc,
 	vcap->ved.ent = &vcap->vdev.entity;
 	vcap->ved.process_frame = vimc_cap_process_frame;
 	vcap->ved.vdev_get_format = vimc_cap_get_format;
-	vcap->dev = &vimc->pdev.dev;
+
+	vcap->dev = vimc->mdev.dev;
 
 	/* Initialize the video_device struct */
 	vdev = &vcap->vdev;
@@ -471,7 +474,7 @@ struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc,
 	/* Register the video_device with the v4l2 and the media framework */
 	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
 	if (ret) {
-		dev_err(&vimc->pdev.dev, "%s: video register failed (err=%d)\n",
+		dev_err(vimc->mdev.dev, "%s: video register failed (err=%d)\n",
 			vcap->vdev.name, ret);
 		goto err_release_queue;
 	}
@@ -487,7 +490,7 @@ struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc,
 err_free_vcap:
 	kfree(vcap);
 
-	return NULL;
+	return ERR_PTR(ret);
 }
 
 static void vimc_cap_create_cfs_pads(struct config_group *ent_group)
diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h
index e0e3b3ab7b19..5c7f451a7bf0 100644
--- a/drivers/media/platform/vimc/vimc-common.h
+++ b/drivers/media/platform/vimc/vimc-common.h
@@ -63,21 +63,6 @@ do {									\
 		(fmt)->xfer_func = V4L2_XFER_FUNC_DEFAULT;		\
 } while (0)
 
-/**
- * struct vimc_platform_data - platform data to components
- *
- * @entity_name:	The name of the entity to be created
- *
- * 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.
- */
-struct vimc_platform_data {
-	char entity_name[32];
-};
-
 /**
  * struct vimc_platform_data_core - platform data to the core
  *
@@ -157,61 +142,56 @@ struct vimc_link {
 /**
  * struct vimc_device - main device for vimc driver
  *
- * @pdev	pointer to the platform device
- * @pipe_cfg	pointer to the vimc pipeline configuration structure
- * @ent_devs	array of vimc_ent_device pointers
  * @mdev	the associated media_device parent
  * @v4l2_dev	Internal v4l2 parent device
+ * @ents	list of vimc_ent_device objects
  */
 struct vimc_device {
-	struct platform_device pdev;
-	const struct vimc_pipeline_config *pipe_cfg;
-	struct vimc_ent_device **ent_devs;
 	struct media_device mdev;
 	struct v4l2_device v4l2_dev;
+
+	struct list_head ents;
 };
 
 /**
- * struct vimc_ent_config	Structure which describes individual
- *				configuration for each entity
- *
- * @name			entity name
- * @ved				pointer to vimc_ent_device (a node in the
- *					topology)
+ * struct ent_type		Structure for the add and remove callbacks
+ *				of entity type
+ * @name			entity type name
  * @add				subdev add hook - initializes and registers
  *					subdev called from vimc-core
  * @rm				subdev rm hook - unregisters and frees
  *					subdev called from vimc-core
  */
-struct vimc_ent_config {
-	const char *name;
-	struct vimc_ent_device *(*add)(struct vimc_device *vimc,
+
+struct vimc_ent_type {
+	const char name[VIMC_MAX_NAME_LEN];
+	struct vimc_ent_device*	(*add)(struct vimc_device *vimc,
 				       const char *vcfg_name);
-	void (*rm)(struct vimc_device *vimc, struct vimc_ent_device *ved);
+	void (*rm)(struct vimc_ent_device *ent);
 };
 
 /* prototypes for vimc_ent_config add and rm hooks */
 struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc,
 				     const char *vcfg_name);
-void vimc_cap_rm(struct vimc_device *vimc, struct vimc_ent_device *ved);
+void vimc_cap_rm(struct vimc_ent_device *ent);
 void vimc_cap_init(void);
 void vimc_cap_exit(void);
 
 struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc,
 				     const char *vcfg_name);
-void vimc_deb_rm(struct vimc_device *vimc, struct vimc_ent_device *ved);
+void vimc_deb_rm(struct vimc_ent_device *ent);
 void vimc_deb_init(void);
 void vimc_deb_exit(void);
 
 struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc,
 				     const char *vcfg_name);
-void vimc_sca_rm(struct vimc_device *vimc, struct vimc_ent_device *ved);
+void vimc_sca_rm(struct vimc_ent_device *ent);
 void vimc_sca_init(void);
 void vimc_sca_exit(void);
 
 struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc,
 				     const char *vcfg_name);
-void vimc_sen_rm(struct vimc_device *vimc, struct vimc_ent_device *ved);
+void vimc_sen_rm(struct vimc_ent_device *ent);
 void vimc_sen_init(void);
 void vimc_sen_exit(void);
 
diff --git a/drivers/media/platform/vimc/vimc-configfs.c b/drivers/media/platform/vimc/vimc-configfs.c
index 933aece0bb5f..e99f926d2f93 100644
--- a/drivers/media/platform/vimc/vimc-configfs.c
+++ b/drivers/media/platform/vimc/vimc-configfs.c
@@ -335,8 +335,8 @@ static void vimc_cfs_src_pad_prepare_link_drop_item(
 	cfs_item = src_pad_group->cg_item.ci_parent->ci_parent;
 	cfs = container_of(cfs_item->ci_parent, struct vimc_cfs_device,
 			   gdev.cg_item);
-	cg_dbg(&cfs->gdev, "dropping prepared link '%s'\n",
-	       link_item->ci_name);
+	cg_dbg(&cfs->gdev, "dropping prepared link '%s' cfs=%p\n",
+	       link_item->ci_name, cfs);
 	if (IS_PLUGGED(cfs))
 		vimc_cfs_device_unplug(cfs);
 	config_item_put(link_item);
@@ -614,7 +614,7 @@ static struct config_group *vimc_cfs_subsys_make_dev_group(
 	if (!cfs)
 		return ERR_PTR(-ENOMEM);
 
-	cg_dbg(&cfs->gdev, "making dev group '%s'", name);
+	cg_dbg(&cfs->gdev, "making dev group '%s', cfs=%p", name, cfs);
 	/* Configure platform data */
 	INIT_LIST_HEAD(&cfs->ents);
 	INIT_LIST_HEAD(&cfs->links);
diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c
index 476bad2cb2de..06a36cc1f00c 100644
--- a/drivers/media/platform/vimc/vimc-core.c
+++ b/drivers/media/platform/vimc/vimc-core.c
@@ -17,178 +17,137 @@
 
 #include "vimc-configfs.h"
 
-#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,					\
-}
-
-/* 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 struct vimc_ent_config ent_config[] = {
-	{
-		.name = "Sensor A",
-		.add = vimc_sen_add,
-		.rm = vimc_sen_rm,
-	},
+static struct vimc_ent_type ent_types[] = {
 	{
-		.name = "Sensor B",
+		.name = VIMC_SEN_NAME,
 		.add = vimc_sen_add,
 		.rm = vimc_sen_rm,
 	},
 	{
-		.name = "Debayer A",
+		.name = VIMC_DEB_NAME,
 		.add = vimc_deb_add,
 		.rm = vimc_deb_rm,
 	},
 	{
-		.name = "Debayer B",
-		.add = vimc_deb_add,
-		.rm = vimc_deb_rm,
-	},
-	{
-		.name = "Raw Capture 0",
-		.add = vimc_cap_add,
-		.rm = vimc_cap_rm,
-	},
-	{
-		.name = "Raw Capture 1",
+		.name = VIMC_CAP_NAME,
 		.add = vimc_cap_add,
 		.rm = vimc_cap_rm,
 	},
 	{
-		/* TODO: change this to vimc-input when it is implemented */
-		.name = "RGB/YUV Input",
-		.add = vimc_sen_add,
-		.rm = vimc_sen_rm,
-	},
-	{
-		.name = "Scaler",
+		.name = VIMC_SCA_NAME,
 		.add = vimc_sca_add,
 		.rm = vimc_sca_rm,
 	},
-	{
-		.name = "RGB/YUV Capture",
-		.add = vimc_cap_add,
-		.rm = vimc_cap_rm,
-	},
 };
 
-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 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 struct vimc_ent_type *vimc_get_ent_type(const char *drv_name)
+{
+	int i;
 
-/* -------------------------------------------------------------------------- */
+	for (i = 0; i < ARRAY_SIZE(ent_types); i++)
+		if (!strcmp(drv_name, ent_types[i].name))
+			return &ent_types[i];
+	return NULL;
+}
 
-static void vimc_rm_links(struct vimc_device *vimc)
+struct vimc_ent_device *vimc_get_ent_dev(const struct vimc_device *vimc,
+					 const char *const name)
 {
-	unsigned int i;
+	struct vimc_ent_device *ent;
 
-	for (i = 0; i < vimc->pipe_cfg->num_ents; i++)
-		media_entity_remove_links(vimc->ent_devs[i]->ent);
+	list_for_each_entry(ent, &vimc->ents, list) {
+		if (!strcmp(ent->name, name))
+			return ent;
+	}
+	return NULL;
 }
+//TODO - remove subdevs upon failure
+static int vimc_core_links_create(const struct vimc_device *vimc,
+				  const struct vimc_platform_data_core *pdata)
+{
+	struct vimc_link *link;
+
+	list_for_each_entry(link, pdata->links, list) {
+		struct vimc_ent_device *source = vimc_get_ent_dev(vimc,
+						 link->source_name);
+		struct vimc_ent_device *sink = vimc_get_ent_dev(vimc,
+					       link->sink_name);
+		int ret;
+
+		ret = media_create_pad_link(source->ent,
+					    link->source_pad,
+					    sink->ent,
+					    link->sink_pad,
+					    link->flags);
 
-static int vimc_create_links(struct vimc_device *vimc)
+		if (ret) {
+			pr_err("%s: failed to create link %s:%u->%s:%u\n",
+					__func__,
+					source->name, link->source_pad,
+					sink->name, link->sink_pad);
+			return ret;
+		}
+		pr_debug("%s: created link %s:%u->%s:%u\n", __func__,
+				source->name, link->source_pad,
+				sink->name, link->sink_pad);
+	}
+	return 0;
+}
+static void vimc_rm_subdevs(struct vimc_device *vimc,
+			    const struct vimc_platform_data_core *pdata)
 {
-	unsigned int i;
-	int ret;
+	struct vimc_ent_device *ent;
+	struct vimc_ent_device *ent_tmp;
 
-	/* 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];
+	list_for_each_entry_safe(ent, ent_tmp, &vimc->ents, list) {
 
-		struct vimc_ent_device *ved_src =
-			vimc->ent_devs[link->src_ent];
-		struct vimc_ent_device *ved_sink =
-			vimc->ent_devs[link->sink_ent];
+		struct vimc_ent_type *ent_type = vimc_get_ent_type(ent->drv_name);
 
-		ret = media_create_pad_link(ved_src->ent, link->src_pad,
-					    ved_sink->ent, link->sink_pad,
-					    link->flags);
-		if (ret)
-			goto err_rm_links;
+		BUG_ON(!ent_type);
+		dev_dbg(vimc->mdev.dev, "removing entity %s:%s\n",
+			ent->drv_name, ent->name);
+		list_del(&ent->list);
+		ent_type->rm(ent);
 	}
-
-	return 0;
-
-err_rm_links:
-	vimc_rm_links(vimc);
-	return ret;
 }
 
-static int vimc_add_subdevs(struct vimc_device *vimc)
+static int vimc_add_subdevs(struct vimc_device *vimc,
+		const struct vimc_platform_data_core *pdata)
 {
-	unsigned int i;
-	struct vimc_ent_device *ved;
-
-	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
-		dev_dbg(&vimc->pdev.dev, "new entity for %s\n",
-			vimc->pipe_cfg->ents[i].name);
-		ved = vimc->pipe_cfg->ents[i].add(vimc,
-					vimc->pipe_cfg->ents[i].name);
-		if (!ved) {
-			dev_err(&vimc->pdev.dev, "add new entity for %s\n",
-				vimc->pipe_cfg->ents[i].name);
-			return -EINVAL;
+	struct vimc_entity_data *ent;
+	int ret;
+
+	list_for_each_entry(ent, pdata->ents, list) {
+
+		struct vimc_ent_type *ent_type =
+			vimc_get_ent_type(ent->drv_name);
+		struct vimc_ent_device *ent_dev;
+
+		BUG_ON(!ent_type);
+
+		ent_dev = ent_type->add(vimc, ent->name);
+		if (IS_ERR(ent_dev)) {
+			ret = PTR_ERR(ent_dev);
+			dev_err(vimc->mdev.dev, "failed to add entity %s:%s\n",
+				ent->drv_name, ent->name);
+			goto err;
 		}
-		vimc->ent_devs[i] = ved;
+		strscpy(ent_dev->name, ent->name, sizeof(ent_dev->name));
+		strscpy(ent_dev->drv_name, ent->drv_name,
+			sizeof(ent_dev->drv_name));
+		list_add(&ent_dev->list, &vimc->ents);
+		dev_dbg(vimc->mdev.dev, "%s: entity %s:%s added\n", __func__,
+				ent->drv_name, ent->name);
 	}
 	return 0;
+err:
+	vimc_rm_subdevs(vimc, pdata);
+	return ret;
 }
 
-static void vimc_rm_subdevs(struct vimc_device *vimc)
-{
-	unsigned int i;
-
-	for (i = 0; i < vimc->pipe_cfg->num_ents; i++)
-		if (vimc->ent_devs[i])
-			vimc->pipe_cfg->ents[i].rm(vimc, vimc->ent_devs[i]);
-}
-
-static int vimc_register_devices(struct vimc_device *vimc)
+static int vimc_register_devices(struct vimc_device *vimc,
+				 const struct vimc_platform_data_core *pdata)
 {
 	int ret;
 
@@ -196,25 +155,15 @@ static int vimc_register_devices(struct vimc_device *vimc)
 	ret = v4l2_device_register(vimc->mdev.dev, &vimc->v4l2_dev);
 	if (ret) {
 		dev_err(vimc->mdev.dev,
-			"v4l2 device register failed (err=%d)\n", ret);
+			"v4l2 device register failed (%d)\n", ret);
 		return ret;
 	}
 
-	/* allocate ent_devs */
-	vimc->ent_devs = kmalloc_array(vimc->pipe_cfg->num_ents,
-				       sizeof(*vimc->ent_devs),
-				       GFP_KERNEL);
-	if (!vimc->ent_devs)
-		goto err_v4l2_unregister;
-
-	/* Invoke entity config hooks to initialize and register subdevs */
-	ret = vimc_add_subdevs(vimc);
+	ret = vimc_add_subdevs(vimc, pdata);
 	if (ret)
-		/* remove sundevs that got added */
-		goto err_rm_subdevs;
+		goto err_v4l2_unregister;
 
-	/* Initialize links */
-	ret = vimc_create_links(vimc);
+	ret = vimc_core_links_create(vimc, pdata);
 	if (ret)
 		goto err_rm_subdevs;
 
@@ -222,16 +171,15 @@ static int vimc_register_devices(struct vimc_device *vimc)
 	ret = media_device_register(&vimc->mdev);
 	if (ret) {
 		dev_err(vimc->mdev.dev,
-			"media device register failed (err=%d)\n", ret);
+			"media device register failed (%d)\n", ret);
 		goto err_rm_subdevs;
 	}
 
-	/* Expose all subdev's nodes*/
+	/* Expose all subdev's nodes */
 	ret = v4l2_device_register_subdev_nodes(&vimc->v4l2_dev);
 	if (ret) {
 		dev_err(vimc->mdev.dev,
-			"vimc subdev nodes registration failed (err=%d)\n",
-			ret);
+			"vimc subdev nodes registration failed (%d)\n", ret);
 		goto err_mdev_unregister;
 	}
 
@@ -241,30 +189,28 @@ static int vimc_register_devices(struct vimc_device *vimc)
 	media_device_unregister(&vimc->mdev);
 	media_device_cleanup(&vimc->mdev);
 err_rm_subdevs:
-	vimc_rm_subdevs(vimc);
-	kfree(vimc->ent_devs);
+	vimc_rm_subdevs(vimc, pdata);
 err_v4l2_unregister:
 	v4l2_device_unregister(&vimc->v4l2_dev);
 
 	return ret;
 }
 
-static void vimc_unregister(struct vimc_device *vimc)
-{
-	media_device_unregister(&vimc->mdev);
-	media_device_cleanup(&vimc->mdev);
-	v4l2_device_unregister(&vimc->v4l2_dev);
-	kfree(vimc->ent_devs);
-}
-
 static int vimc_probe(struct platform_device *pdev)
 {
-	struct vimc_device *vimc = container_of(pdev, struct vimc_device, pdev);
+	const struct vimc_platform_data_core *pdata = pdev->dev.platform_data;
+	struct vimc_device *vimc;
 	int ret;
 
-	dev_dbg(&pdev->dev, "probe");
+	dev_dbg(&pdev->dev, "probe\n");
+
+	vimc = devm_kzalloc(&pdev->dev, sizeof(*vimc),
+			GFP_KERNEL);
+	if (!vimc)
+		return -ENOMEM;
 
 	memset(&vimc->mdev, 0, sizeof(vimc->mdev));
+	INIT_LIST_HEAD(&vimc->ents);
 
 	/* Link the media device within the v4l2_device */
 	vimc->v4l2_dev.mdev = &vimc->mdev;
@@ -277,44 +223,33 @@ static int vimc_probe(struct platform_device *pdev)
 	vimc->mdev.dev = &pdev->dev;
 	media_device_init(&vimc->mdev);
 
-	ret = vimc_register_devices(vimc);
-	if (ret) {
-		media_device_cleanup(&vimc->mdev);
+	ret = vimc_register_devices(vimc, pdata);
+	if (ret)
 		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);
+	struct vimc_platform_data_core *pdata = pdev->dev.platform_data;
 
-	dev_dbg(&pdev->dev, "remove");
+	dev_dbg(&pdev->dev, "remove\n");
 
-	vimc_rm_subdevs(vimc);
-	vimc_unregister(vimc);
+	media_device_unregister(&vimc->mdev);
+	media_device_cleanup(&vimc->mdev);
+	vimc_rm_subdevs(vimc, pdata);
+	v4l2_device_unregister(&vimc->v4l2_dev);
 
 	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",
 	},
 };
 
@@ -322,18 +257,9 @@ 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);
-		return ret;
-	}
-
 	ret = platform_driver_register(&vimc_pdrv);
 	if (ret) {
-		dev_err(&vimc_dev.pdev.dev,
-			"platform driver registration failed (err=%d)\n", ret);
-		platform_driver_unregister(&vimc_pdrv);
+		pr_err("vimc init: platform driver register failed (%d)\n", ret);
 		return ret;
 	}
 
@@ -361,7 +287,6 @@ static void __exit vimc_exit(void)
 
 	vimc_cfs_subsys_unregister();
 	platform_driver_unregister(&vimc_pdrv);
-	platform_device_unregister(&vimc_dev.pdev);
 }
 
 module_init(vimc_init);
diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c
index e461b155e514..2391ac3b5014 100644
--- a/drivers/media/platform/vimc/vimc-debayer.c
+++ b/drivers/media/platform/vimc/vimc-debayer.c
@@ -486,11 +486,12 @@ static const struct v4l2_subdev_internal_ops vimc_deb_int_ops = {
 	.release = vimc_deb_release,
 };
 
-void vimc_deb_rm(struct vimc_device *vimc, struct vimc_ent_device *ved)
+void vimc_deb_rm(struct vimc_ent_device *ved)
 {
-	struct vimc_deb_device *vdeb;
+	struct vimc_deb_device *vdeb = container_of(ved,
+						    struct vimc_deb_device,
+						    ved);
 
-	vdeb = container_of(ved, struct vimc_deb_device, ved);
 	vimc_ent_sd_unregister(ved, &vdeb->sd);
 }
 
@@ -504,7 +505,7 @@ struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc,
 	/* Allocate the vdeb struct */
 	vdeb = kzalloc(sizeof(*vdeb), GFP_KERNEL);
 	if (!vdeb)
-		return NULL;
+		return ERR_PTR(-ENOMEM);
 
 	/* Initialize ved and sd */
 	ret = vimc_ent_sd_register(&vdeb->ved, &vdeb->sd, v4l2_dev,
@@ -515,11 +516,11 @@ struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc,
 				   &vimc_deb_int_ops, &vimc_deb_ops);
 	if (ret) {
 		kfree(vdeb);
-		return NULL;
+		return ERR_PTR(ret);
 	}
 
 	vdeb->ved.process_frame = vimc_deb_process_frame;
-	vdeb->dev = &vimc->pdev.dev;
+	vdeb->dev = vimc->mdev.dev;
 
 	/* Initialize the frame format */
 	vdeb->sink_fmt = sink_fmt_default;
diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c
index e5cf0073d68a..7d9726d0b39c 100644
--- a/drivers/media/platform/vimc/vimc-scaler.c
+++ b/drivers/media/platform/vimc/vimc-scaler.c
@@ -345,11 +345,12 @@ static const struct v4l2_subdev_internal_ops vimc_sca_int_ops = {
 	.release = vimc_sca_release,
 };
 
-void vimc_sca_rm(struct vimc_device *vimc, struct vimc_ent_device *ved)
+void vimc_sca_rm(struct vimc_ent_device *ved)
 {
-	struct vimc_sca_device *vsca;
+	struct vimc_sca_device *vsca = container_of(ved,
+						    struct vimc_sca_device,
+						    ved);
 
-	vsca = container_of(ved, struct vimc_sca_device, ved);
 	vimc_ent_sd_unregister(ved, &vsca->sd);
 }
 
@@ -363,7 +364,7 @@ struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc,
 	/* Allocate the vsca struct */
 	vsca = kzalloc(sizeof(*vsca), GFP_KERNEL);
 	if (!vsca)
-		return NULL;
+		return ERR_PTR(-ENOMEM);
 
 	/* Initialize ved and sd */
 	ret = vimc_ent_sd_register(&vsca->ved, &vsca->sd, v4l2_dev,
diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c
index 22e3ad98c818..556d49f338ab 100644
--- a/drivers/media/platform/vimc/vimc-sensor.c
+++ b/drivers/media/platform/vimc/vimc-sensor.c
@@ -300,12 +300,12 @@ static const struct v4l2_subdev_internal_ops vimc_sen_int_ops = {
 	.release = vimc_sen_release,
 };
 
-void vimc_sen_rm(struct vimc_device *vimc, struct vimc_ent_device *ved)
+void vimc_sen_rm(struct vimc_ent_device *ent)
 {
 	struct vimc_sen_device *vsen;
 
-	vsen = container_of(ved, struct vimc_sen_device, ved);
-	vimc_ent_sd_unregister(ved, &vsen->sd);
+	vsen = container_of(ent, struct vimc_sen_device, ved);
+	vimc_ent_sd_unregister(ent, &vsen->sd);
 }
 
 /* Image Processing Controls */
@@ -335,7 +335,7 @@ struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc,
 	/* Allocate the vsen struct */
 	vsen = kzalloc(sizeof(*vsen), GFP_KERNEL);
 	if (!vsen)
-		return NULL;
+		return ERR_PTR(-ENOMEM);
 
 	v4l2_ctrl_handler_init(&vsen->hdl, 4);
 
@@ -369,7 +369,7 @@ struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc,
 		goto err_free_hdl;
 
 	vsen->ved.process_frame = vimc_sen_process_frame;
-	vsen->dev = &vimc->pdev.dev;
+	vsen->dev = vimc->mdev.dev;
 
 	/* Initialize the frame format */
 	vsen->mbus_format = fmt_default;
@@ -390,7 +390,7 @@ struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc,
 err_free_vsen:
 	kfree(vsen);
 
-	return NULL;
+	return ERR_PTR(ret);
 }
 
 static void vimc_sen_create_cfs_pads(struct config_group *ent_group)
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 5/5] media: vimc: Add device index to the bus_info
  2019-09-19 20:32 [PATCH 0/5] media: vimc: use configfs in order to configure devices topologies Dafna Hirschfeld
                   ` (3 preceding siblings ...)
  2019-09-19 20:32 ` [PATCH 4/5] media: vimc: use configfs instead of having hardcoded configuration Dafna Hirschfeld
@ 2019-09-19 20:32 ` Dafna Hirschfeld
  2019-09-20 13:17 ` [PATCH 0/5] media: vimc: use configfs in order to configure devices topologies Hans Verkuil
  5 siblings, 0 replies; 20+ messages in thread
From: Dafna Hirschfeld @ 2019-09-19 20:32 UTC (permalink / raw)
  To: linux-media
  Cc: dafna.hirschfeld, laurent.pinchart, helen.koike, ezequiel,
	andre.almeida, skhan, hverkuil, kernel, dafna3

Use a bitmask to get the next available device index
to be used for the bus_info field in the v4l2_capability
struct in the VIDIOC_QUERYCAP ioctl

Signed-off-by: Dafna Hirschfeld <dafna.hirschfeld@collabora.com>
---
 drivers/media/platform/vimc/vimc-capture.c |  8 ++++++--
 drivers/media/platform/vimc/vimc-common.h  |  2 ++
 drivers/media/platform/vimc/vimc-core.c    | 24 +++++++++++++++++++++-
 3 files changed, 31 insertions(+), 3 deletions(-)

diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c
index 6ccf879c0b75..408bacb7c77e 100644
--- a/drivers/media/platform/vimc/vimc-capture.c
+++ b/drivers/media/platform/vimc/vimc-capture.c
@@ -31,6 +31,7 @@ struct vimc_cap_device {
 	struct mutex lock;
 	u32 sequence;
 	struct vimc_stream stream;
+	char bus_info[32];
 };
 
 static const struct v4l2_pix_format fmt_default = {
@@ -55,10 +56,12 @@ struct vimc_cap_buffer {
 static int vimc_cap_querycap(struct file *file, void *priv,
 			     struct v4l2_capability *cap)
 {
+	struct vimc_cap_device *vcap = video_drvdata(file);
+
 	strscpy(cap->driver, VIMC_PDEV_NAME, sizeof(cap->driver));
 	strscpy(cap->card, KBUILD_MODNAME, sizeof(cap->card));
-	snprintf(cap->bus_info, sizeof(cap->bus_info),
-		 "platform:%s", VIMC_PDEV_NAME);
+	strscpy(cap->bus_info, vcap->bus_info, sizeof(cap->bus_info));
+
 
 	return 0;
 }
@@ -456,6 +459,7 @@ struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc,
 	vcap->ved.vdev_get_format = vimc_cap_get_format;
 
 	vcap->dev = vimc->mdev.dev;
+	strscpy(vcap->bus_info, vimc->mdev.bus_info, sizeof(vcap->bus_info));
 
 	/* Initialize the video_device struct */
 	vdev = &vcap->vdev;
diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h
index 5c7f451a7bf0..489b32f79b23 100644
--- a/drivers/media/platform/vimc/vimc-common.h
+++ b/drivers/media/platform/vimc/vimc-common.h
@@ -143,12 +143,14 @@ struct vimc_link {
  * struct vimc_device - main device for vimc driver
  *
  * @mdev	the associated media_device parent
+ * @idx		the index of the device, used for info_bus
  * @v4l2_dev	Internal v4l2 parent device
  * @ents	list of vimc_ent_device objects
  */
 struct vimc_device {
 	struct media_device mdev;
 	struct v4l2_device v4l2_dev;
+	unsigned int idx;
 
 	struct list_head ents;
 };
diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c
index 06a36cc1f00c..6065dd51438a 100644
--- a/drivers/media/platform/vimc/vimc-core.c
+++ b/drivers/media/platform/vimc/vimc-core.c
@@ -17,6 +17,11 @@
 
 #include "vimc-configfs.h"
 
+#define VIMC_MAX_DEVS 64
+
+
+static DECLARE_BITMAP(used, VIMC_MAX_DEVS);
+static DEFINE_MUTEX(vimc_dev_idx_lock);
 
 static struct vimc_ent_type ent_types[] = {
 	{
@@ -201,9 +206,20 @@ static int vimc_probe(struct platform_device *pdev)
 	const struct vimc_platform_data_core *pdata = pdev->dev.platform_data;
 	struct vimc_device *vimc;
 	int ret;
+	unsigned int idx;
 
 	dev_dbg(&pdev->dev, "probe\n");
 
+	mutex_lock(&vimc_dev_idx_lock);
+	idx = find_first_zero_bit(used, VIMC_MAX_DEVS);
+	if (idx == VIMC_MAX_DEVS) {
+		mutex_unlock(&vimc_dev_idx_lock);
+		dev_err(&pdev->dev, "there are already %u devs which is the max allowed\n",
+			VIMC_MAX_DEVS);
+		return -EBUSY;
+	}
+	set_bit(idx, used);
+	mutex_unlock(&vimc_dev_idx_lock);
 	vimc = devm_kzalloc(&pdev->dev, sizeof(*vimc),
 			GFP_KERNEL);
 	if (!vimc)
@@ -211,6 +227,7 @@ static int vimc_probe(struct platform_device *pdev)
 
 	memset(&vimc->mdev, 0, sizeof(vimc->mdev));
 	INIT_LIST_HEAD(&vimc->ents);
+	vimc->idx = idx;
 
 	/* Link the media device within the v4l2_device */
 	vimc->v4l2_dev.mdev = &vimc->mdev;
@@ -219,7 +236,8 @@ static int vimc_probe(struct platform_device *pdev)
 	strscpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME,
 		sizeof(vimc->mdev.model));
 	snprintf(vimc->mdev.bus_info, sizeof(vimc->mdev.bus_info),
-		 "platform:%s", VIMC_PDEV_NAME);
+		 "platform:%s-%03u", VIMC_PDEV_NAME, idx);
+
 	vimc->mdev.dev = &pdev->dev;
 	media_device_init(&vimc->mdev);
 
@@ -234,6 +252,7 @@ static int vimc_remove(struct platform_device *pdev)
 {
 	struct vimc_device *vimc = platform_get_drvdata(pdev);
 	struct vimc_platform_data_core *pdata = pdev->dev.platform_data;
+	unsigned long idx = vimc->idx;
 
 	dev_dbg(&pdev->dev, "remove\n");
 
@@ -242,6 +261,9 @@ static int vimc_remove(struct platform_device *pdev)
 	vimc_rm_subdevs(vimc, pdata);
 	v4l2_device_unregister(&vimc->v4l2_dev);
 
+	mutex_lock(&vimc_dev_idx_lock);
+	clear_bit(idx, used);
+	mutex_unlock(&vimc_dev_idx_lock);
 	return 0;
 }
 
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH 0/5] media: vimc: use configfs in order to configure devices topologies
  2019-09-19 20:32 [PATCH 0/5] media: vimc: use configfs in order to configure devices topologies Dafna Hirschfeld
                   ` (4 preceding siblings ...)
  2019-09-19 20:32 ` [PATCH 5/5] media: vimc: Add device index to the bus_info Dafna Hirschfeld
@ 2019-09-20 13:17 ` Hans Verkuil
  2019-09-20 15:07   ` Dafna Hirschfeld
  5 siblings, 1 reply; 20+ messages in thread
From: Hans Verkuil @ 2019-09-20 13:17 UTC (permalink / raw)
  To: Dafna Hirschfeld, linux-media
  Cc: laurent.pinchart, helen.koike, ezequiel, andre.almeida, skhan,
	kernel, dafna3

Hi Dafna,

On 9/19/19 10:32 PM, Dafna Hirschfeld wrote:
> This patchset introduces the usage of configfs in order to create vimc devices
> with a configured topology. A patch introducing configfs usage was already sent by Helen Koike:
> https://patchwork.linuxtv.org/patch/53397/ . The current patch is based on her patch but
> suggests a new API for using configfs.
> It uses symlinks to represent a link between two entities, an approach already used in the kernel
> by usb gadgets composed with configfs to associate usb gadget's functions to its configurations.
> For example, a topology of sensor->capture will be created with the following commands:
> 
> CONFIGFS_ROOT=/sys/kernel/config
> 
> mkdir ${CONFIGFS_ROOT}/vimc/mdev/
> mkdir ${CONFIGFS_ROOT}/vimc/mdev/vimc-sensor:sen
> mkdir ${CONFIGFS_ROOT}/vimc/mdev/vimc-capture:cap
> tree ${CONFIGFS_ROOT}
> /configfs/
> `-- vimc
>     `-- mdev
>         |-- hotplug
>         |-- vimc-capture:cap
>         |   `-- pad:sink:0
>         `-- vimc-sensor:sen
>             `-- pad:source:0
> 
> mkdir ${CONFIGFS_ROOT}/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap
> ln -s ${CONFIGFS_ROOT}/vimc/mdev/vimc-capture:cap/pad:sink:0 ${CONFIGFS_ROOT}/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap
> tree ${CONFIGFS_ROOT}
> /configfs/
> `-- vimc
>     `-- mdev
>         |-- hotplug
>         |-- vimc-capture:cap
>         |   `-- pad:sink:0
>         `-- vimc-sensor:sen
>             `-- pad:source:0
>                 `-- to-cap
>                     |-- enabled
>                     |-- immutable
>                     `-- pad:sink:0 -> ../../../../../vimc/mdev/vimc-capture:cap/pad:sink:0
> 
> There are several reasons to prefer the symlink approach in order to represent links between entities.
> The previous approach in which links are represented with directories of the form 'entity1:pad>-><entity2:pad'
> requires userspace to parse the dirctories names in order to understand the topology, while in the symlink
> approach userspace needs only to traverse the configfs tree.
> Also, the usage of symlinks prevents userspace from creating links between entities that don't exist and also
> an entity can't be removed if there is a symlink pointing to it or from it, while in the previous approach the

Why can't you remove an entity if there is a symlink pointing to it?

In the example above I can remove mdev/vimc-capture:cap just fine. Afterwards
the pad:sink:0 symlink will point to a non-existing file, but that's valid
symlink behavior.

Or is vimc checking internally and prohibits the user from making invalid changes?

In any case, this cover letter is a bit confusing and needs to address this in more
detail.

Regards,

	Hans

> links were created by creating unrelated directories and care had to be taken to ensure consistency. This way
> the topology configured from userspace is restricted to always be valid and represent the current topology of
> the device. This results in less validation needed in kernel code when plugging the device and less possibility
> for mistakes in the userspace side. Last, but not least, using symlinks is the natural way of associating things
> in configfs.
> 
> This patch is meant to demonstrate the suggested configfs api and get comments and acceptance/disagreement from
> the community. It passes few tests that configure basic topology and streams the capture entities.
> Here is the tests script: https://gitlab.collabora.com/dafna/scripts/blob/master/configfs/sym-unit-tests-simple-topo.sh
> Further versions will go through more extensive debugging.
> 
> The patchset is rebased on top of v5 of the patchset 'Collapse vimc into single monolithic driver' sent by Shuah Khan
> https://lkml.org/lkml/2019/9/17/656
> 
> Patch 1, was sent by me before as a single patch and is needed for the configfs implementation.
> 
> Patch 2, documents how to use the new configfs api in order to create and set devices topologies.
> 
> Patch 3, only adds the new configfs api code but does not use it yet, so it still creates only the hardcoded device.
> 
> Patch 4, removes the hardcoded device topology and creates devices with topologies configured with the configfs.
> 
> Patch 5, implements indexing for the bus_info field since now there can be more than one vimc device.
> 
> Dafna Hirschfeld (5):
>   media: vimc: upon streaming, check that the pipeline starts with a
>     source entity
>   docs: media: vimc: Documenting vimc topology configuration using
>     configfs
>   media: vimc: Add the implementation for the configfs api
>   media: vimc: use configfs instead of having hardcoded configuration
>   media: vimc: Add device index to the bus_info
> 
>  Documentation/media/v4l-drivers/vimc.dot    |  28 +-
>  Documentation/media/v4l-drivers/vimc.rst    | 240 ++++++-
>  drivers/media/platform/vimc/Kconfig         |   9 +-
>  drivers/media/platform/vimc/Makefile        |   2 +-
>  drivers/media/platform/vimc/vimc-capture.c  |  50 +-
>  drivers/media/platform/vimc/vimc-common.h   |  86 ++-
>  drivers/media/platform/vimc/vimc-configfs.c | 656 ++++++++++++++++++++
>  drivers/media/platform/vimc/vimc-configfs.h |  41 ++
>  drivers/media/platform/vimc/vimc-core.c     | 350 +++++------
>  drivers/media/platform/vimc/vimc-debayer.c  |  35 +-
>  drivers/media/platform/vimc/vimc-scaler.c   |  35 +-
>  drivers/media/platform/vimc/vimc-sensor.c   |  33 +-
>  drivers/media/platform/vimc/vimc-streamer.c |  39 +-
>  13 files changed, 1289 insertions(+), 315 deletions(-)
>  create mode 100644 drivers/media/platform/vimc/vimc-configfs.c
>  create mode 100644 drivers/media/platform/vimc/vimc-configfs.h
> 


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 2/5] docs: media: vimc: Documenting vimc topology configuration using configfs
  2019-09-19 20:32 ` [PATCH 2/5] docs: media: vimc: Documenting vimc topology configuration using configfs Dafna Hirschfeld
@ 2019-09-20 13:39   ` Hans Verkuil
  2019-09-23  9:29     ` Dafna Hirschfeld
  2019-09-26 18:59   ` Shuah Khan
  1 sibling, 1 reply; 20+ messages in thread
From: Hans Verkuil @ 2019-09-20 13:39 UTC (permalink / raw)
  To: Dafna Hirschfeld, linux-media
  Cc: laurent.pinchart, helen.koike, ezequiel, andre.almeida, skhan,
	kernel, dafna3

On 9/19/19 10:32 PM, Dafna Hirschfeld wrote:
> Add explanation of how to use configfs in order to create a
> vimc device with a given topology.
> 
> Signed-off-by: Dafna Hirschfeld <dafna.hirschfeld@collabora.com>
> ---
>  Documentation/media/v4l-drivers/vimc.dot |  28 ++-
>  Documentation/media/v4l-drivers/vimc.rst | 240 ++++++++++++++++++++---
>  2 files changed, 220 insertions(+), 48 deletions(-)
> 
> diff --git a/Documentation/media/v4l-drivers/vimc.dot b/Documentation/media/v4l-drivers/vimc.dot
> index 57863a13fa39..e3b41ac2bc46 100644
> --- a/Documentation/media/v4l-drivers/vimc.dot
> +++ b/Documentation/media/v4l-drivers/vimc.dot
> @@ -2,21 +2,15 @@
>  
>  digraph board {
>  	rankdir=TB
> -	n00000001 [label="{{} | Sensor A\n/dev/v4l-subdev0 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green]
> -	n00000001:port0 -> n00000005:port0 [style=bold]
> -	n00000001:port0 -> n0000000b [style=bold]
> -	n00000003 [label="{{} | Sensor B\n/dev/v4l-subdev1 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green]
> -	n00000003:port0 -> n00000008:port0 [style=bold]
> -	n00000003:port0 -> n0000000f [style=bold]
> -	n00000005 [label="{{<port0> 0} | Debayer A\n/dev/v4l-subdev2 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
> -	n00000005:port1 -> n00000017:port0
> -	n00000008 [label="{{<port0> 0} | Debayer B\n/dev/v4l-subdev3 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
> -	n00000008:port1 -> n00000017:port0 [style=dashed]
> -	n0000000b [label="Raw Capture 0\n/dev/video0", shape=box, style=filled, fillcolor=yellow]
> -	n0000000f [label="Raw Capture 1\n/dev/video1", shape=box, style=filled, fillcolor=yellow]
> -	n00000013 [label="RGB/YUV Input\n/dev/video2", shape=box, style=filled, fillcolor=yellow]
> -	n00000013 -> n00000017:port0 [style=dashed]
> -	n00000017 [label="{{<port0> 0} | Scaler\n/dev/v4l-subdev4 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
> -	n00000017:port1 -> n0000001a [style=bold]
> -	n0000001a [label="RGB/YUV Capture\n/dev/video3", shape=box, style=filled, fillcolor=yellow]
> +	n00000001 [label="cap-deb\n/dev/video0", shape=box, style=filled, fillcolor=yellow]
> +	n00000005 [label="cap-sen\n/dev/video1", shape=box, style=filled, fillcolor=yellow]
> +	n00000009 [label="cap-sca\n/dev/video2", shape=box, style=filled, fillcolor=yellow]
> +	n0000000d [label="{{<port0> 0} | sca\n/dev/v4l-subdev0 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
> +	n0000000d:port1 -> n00000009 [style=bold]
> +	n00000010 [label="{{<port0> 0} | deb\n/dev/v4l-subdev1 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
> +	n00000010:port1 -> n00000001 [style=bold]
> +	n00000010:port1 -> n0000000d:port0 [style=bold]
> +	n00000013 [label="{{} | sen\n/dev/v4l-subdev2 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green]
> +	n00000013:port0 -> n00000005 [style=bold]
> +	n00000013:port0 -> n00000010:port0 [style=bold]
>  }
> diff --git a/Documentation/media/v4l-drivers/vimc.rst b/Documentation/media/v4l-drivers/vimc.rst
> index a582af0509ee..e5636883545f 100644
> --- a/Documentation/media/v4l-drivers/vimc.rst
> +++ b/Documentation/media/v4l-drivers/vimc.rst
> @@ -1,45 +1,225 @@
>  .. SPDX-License-Identifier: GPL-2.0
>  
> +==========================================
>  The Virtual Media Controller Driver (vimc)
>  ==========================================
>  
> -The vimc driver emulates complex video hardware using the V4L2 API and the Media
> -API. It has a capture device and three subdevices: sensor, debayer and scaler.
> +The vimc driver emulates complex video hardware topologies using the V4L2 API
> +and the Media API. It has a capture device and three subdevices:
> +sensor, debayer and scaler. It exposes media devices through /dev/mediaX nodes,
> +video capture devices through /dev/videoX and sub-devices through /dev/v4l-subdevX.
> +
> +
> +To configure a media device of a given topology, a ConfigFS API is provided.
> +
> +Configuring a topology through ConfigFS (Experimental)
> +======================================================
> +
> +.. note:: This API is under development and might change in the future.
> +
> +Mount configfs:
> +::
> +
> +	$ mkdir /configfs
> +	$ mount -t configfs none /configfs
> +
> +When loading the module, you will see a folder named vimc
> +::
> +
> +	$ tree /configfs/
> +	/configfs/
> +	`-- vimc
> +
> +Creating a media device
> +-----------------------
> +
> +To create a media device create a new folder under /configfs/vimc/
> +
> +Example:
> +::
> +
> +	$ mkdir /configfs/vimc/mdev
> +	$ tree /configfs/vimc/mdev
> +	/configfs/
> +	`-- vimc
> +	    `-- mdev
> +	        `-- hotplug
> +
> +Creating entities
> +-----------------
> +
> +To create an entity in the media device's topology, create a folder under
> +``/configfs/vimc/<mdev-name>/`` with the following format:
> +
> +	<entity-type>:<entity-name>

I suspect that there are restrictions to the characters that can be used in
these names. For one the use of ':' in the entity-name is probably a bad idea.
Also the entity name should be unique, i.e. you can't have a sensor and a scaler
with the same entity name.

Such restrictions should be documented here and also tested in the driver!

> +
> +Where <entity-type> is one of the following:
> +
> +- vimc-sensor
> +- vimc-scaler
> +- vimc-debayer
> +- vimc-capture
> +
> +Example:
> +::
> +
> +	$ mkdir /configfs/vimc/mdev/vimc-sensor:sen
> +	$ mkdir /configfs/vimc/mdev/vimc-capture:cap-sen
> +	$ tree /configfs/
> +	/configfs/
> +	`-- vimc
> +	    `-- mdev
> +		|-- hotplug
> +		|-- vimc-capture:cap-sen
> +		|   `-- pad:sink:0
> +		`-- vimc-sensor:sen
> +                    `-- pad:source:0

Do we need the 'pad:' prefix here? Isn't 'sink:' or 'source:' sufficient?
I think pad:sink:0 is rather cumbersome.

> +
> +Default folders are created under the entity directory for each pad of the entity.
> +
> +Creating links
> +--------------
> +
> +To create a link between two entities, you should create a directory for the link
> +under the source pad of the link and then set it to be a symbolic link to the sink pad:
>  
> -Topology
> ---------
> +Example:
> +::
> +
> +	$ mkdir "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap"

I'd call the directory 'to-cap-sen': it's clearer that this points to the
cap-sen entity. It makes the example easier to understand.

> +	$ ln -s "/configfs/vimc/mdev/vimc-capture:cap-sen/pad:sink:0" "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap"
> +	$ tree /configfs
> +	/configfs
> +	`-- vimc
> +	    `-- mdev
> +	        |-- hotplug
> +	        |-- vimc-capture:cap-sen
> +	        |   `-- pad:sink:0
> +	        `-- vimc-sensor:sen
> +	            `-- pad:source:0
> +	                `-- to-cap
> +	                    |-- enabled
> +	                    |-- immutable
> +	                    `-- pad:sink:0 -> ../../../../../vimc/mdev/vimc-capture:cap-sen/pad:sink:0
> +
> +The `enabled` and `immutable` are two boolean attributes of the link corresponding to the link flags
> +
> +
> +Flag values are described in :ref:`Documentation/media/uapi/mediactl/media-types.rst <media-link-flag>`
> +( seek for ``MEDIA_LNK_FL_*``)
> +
> +1 - Enabled
> +	Indicates that 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.
> +
> +Change an attribute of the link by writing "on" or "1" to set it on , and "off" or "0" to set it off
> +
> +Example:
> +::
> +
> +	$ echo "on" > /configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap/immutable
> +
> +Activating/Deactivating device
> +------------------------------
> +
> +To activate the device, write one of "plugged", "plug" or "1" to the file
> +``/configfs/vimc/<mdev-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 the file
> +``/configfs/vimc/<ndev-name>/hotplug``
> +
> +Example:
> +::
> +
> +	$ echo unplugged > /configfs/vimc/mdev/hotplug
> +
> +Topology Configuration - Full Example
> +-------------------------------------
> +
> +Here is a full example of a simple topology configuration:
> +
> +.. code-block:: bash
> +
> +    # Creating the entities
> +    mkdir "/configfs/vimc/mdev"
> +    mkdir "/configfs/vimc/mdev/vimc-sensor:sen"
> +    mkdir "/configfs/vimc/mdev/vimc-debayer:deb"
> +    mkdir "/configfs/vimc/mdev/vimc-scaler:sca"
> +    mkdir "/configfs/vimc/mdev/vimc-capture:cap-sca" #/dev/video2
> +    mkdir "/configfs/vimc/mdev/vimc-capture:cap-sen" #/dev/video1
> +    mkdir "/configfs/vimc/mdev/vimc-capture:cap-deb" #/dev/video0
> +
> +    # Creating the links
> +    #sen -> deb
> +    mkdir "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-deb"
> +    ln -s "/configfs/vimc/mdev/vimc-debayer:deb/pad:sink:0" "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-deb"
> +    echo on > "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-deb/immutable"

If you set immutable to on, then it should automatically set enabled to on as well,
so no need for the next line.

> +    echo on > "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-deb/enabled"
> +
> +    #deb -> sca
> +    mkdir "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-sca"
> +    ln -s "/configfs/vimc/mdev/vimc-scaler:sca/pad:sink:0" "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-sca"
> +    echo on > "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-sca/immutable"
> +    echo on > "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-sca/enabled"
> +
> +    #sca -> cap-sca
> +    mkdir "/configfs/vimc/mdev/vimc-scaler:sca/pad:source:1/to-cap"

Same in this example: use to-cap-sca

> +    ln -s "/configfs/vimc/mdev/vimc-capture:cap-sca/pad:sink:0" "/configfs/vimc/mdev/vimc-scaler:sca/pad:source:1/to-cap"
> +    echo on > "/configfs/vimc/mdev/vimc-scaler:sca/pad:source:1/to-cap/immutable"
> +    echo on > "/configfs/vimc/mdev/vimc-scaler:sca/pad:source:1/to-cap/enabled"
> +
> +    #sen -> cap-sen
> +    mkdir "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap"
> +    ln -s "/configfs/vimc/mdev/vimc-capture:cap-sen/pad:sink:0" "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap"
> +    echo on > "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap/immutable"
> +    echo on > "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap/enabled"
> +
> +    #deb -> cap-deb
> +    mkdir "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-cap"
> +    ln -s "/configfs/vimc/mdev/vimc-capture:cap-deb/pad:sink:0" "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-cap"
> +    echo on > "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-cap/immutable"
> +    echo on > "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-cap/enabled"
>  
> -The topology is hardcoded, although you could modify it in vimc-core and
> -recompile the driver to achieve your own topology. This is the default topology:
>  
>  .. _vimc_topology_graph:
>  
>  .. kernel-figure:: vimc.dot
> -    :alt:   Diagram of the default media pipeline topology
> +    :alt:   Diagram of the configured simple topology in the example
>      :align: center
>  
> -    Media pipeline graph on vimc
> +    Simple Media pipeline graph on vimc configured through configfs
>  
> -Configuring the topology
> -~~~~~~~~~~~~~~~~~~~~~~~~
> +Configuring the pipeline formats
> +================================
>  
> -Each subdevice will come with its default configuration (pixelformat, height,
> -width, ...). One needs to configure the topology in order to match the
> +Each subdevice has a default format configuration (pixelformat, height,
> +width, ...). You should configure the formats in order to match the
>  configuration on each linked subdevice to stream frames through the pipeline.
> -If the configuration doesn't match, the stream will fail. The ``v4l-utils``
> +If the configuration doesn't match, streaming will fail. The ``v4l-utils``
>  package is a bundle of user-space applications, that comes with ``media-ctl`` and
> -``v4l2-ctl`` that can be used to configure the vimc configuration. This sequence
> -of commands fits for the default topology:
> +``v4l2-ctl`` that can be used to configure the formats of the entities. This sequence
> +of commands fits the simple topology created in the full example of topology configuration:
>  
>  .. code-block:: bash
>  
> -        media-ctl -d platform:vimc -V '"Sensor A":0[fmt:SBGGR8_1X8/640x480]'
> -        media-ctl -d platform:vimc -V '"Debayer A":0[fmt:SBGGR8_1X8/640x480]'
> -        media-ctl -d platform:vimc -V '"Sensor B":0[fmt:SBGGR8_1X8/640x480]'
> -        media-ctl -d platform:vimc -V '"Debayer B":0[fmt:SBGGR8_1X8/640x480]'
> -        v4l2-ctl -z platform:vimc -d "RGB/YUV Capture" -v width=1920,height=1440
> -        v4l2-ctl -z platform:vimc -d "Raw Capture 0" -v pixelformat=BA81
> -        v4l2-ctl -z platform:vimc -d "Raw Capture 1" -v pixelformat=BA81
> +	media-ctl -d platform:vimc-000 -V '"sen":0[fmt:SBGGR8_1X8/640x480]'
> +	media-ctl -d platform:vimc-000 -V '"deb":0[fmt:SBGGR8_1X8/640x480]'
> +	media-ctl -d platform:vimc-000 -V '"deb":1[fmt:RGB888_1X24/640x480]'
> +	media-ctl -d platform:vimc-000 -V '"sca":0[fmt:RGB888_1X24/640x480]'
> +	media-ctl -d platform:vimc-000 -V '"sca":1[fmt:RGB888_1X24/640x480]'
> +	v4l2-ctl -z platform:vimc-000 -d "cap-sen" -v pixelformat=BA81
> +	v4l2-ctl -z platform:vimc-000 -d "cap-deb" -v pixelformat=RGB3
> +	# The default scaling value of the scaler is 3, so need to set its capture accordingly
> +	v4l2-ctl -z platform:vimc -d "cap-sca" -v pixelformat=RGB3,width=1920,height=1440
>  
>  Subdevices
>  ----------
> @@ -61,8 +241,8 @@ vimc-debayer:
>  	* 1 Pad source
>  
>  vimc-scaler:
> -	Scale up the image by a factor of 3. E.g.: a 640x480 image becomes a
> -        1920x1440 image. (this value can be configured, see at
> +	Scales up the image by a factor of 3. E.g.: a 640x480 image becomes a
> +        1920x1440 image. (this value can be configured, see

Note that this might change with this patch:

https://patchwork.kernel.org/patch/11146175/

Just so you are aware of this.

>          `Module options`_).
>  	Exposes:
>  
> @@ -77,12 +257,10 @@ vimc-capture:
>  	* 1 Pad source
>  
>  
> -        Module options
> ----------------
> -
> -Vimc has a few module parameters to configure the driver.
> +Module options
> +==============
>  
> -        param=value
> +Vimc has 2 module parameters to configure the driver.
>  
>  * ``sca_mult=<unsigned int>``
>  
> @@ -98,10 +276,10 @@ Vimc has a few module parameters to configure the driver.
>          otherwise the next odd number is considered (the default value is 3).
>  
>  Source code documentation
> --------------------------
> +=========================
>  
>  vimc-streamer
> -~~~~~~~~~~~~~
> +-------------
>  
>  .. kernel-doc:: drivers/media/platform/vimc/vimc-streamer.h
>     :internal:
> 

Regards,

	Hans

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 0/5] media: vimc: use configfs in order to configure devices topologies
  2019-09-20 13:17 ` [PATCH 0/5] media: vimc: use configfs in order to configure devices topologies Hans Verkuil
@ 2019-09-20 15:07   ` Dafna Hirschfeld
  2019-09-20 15:10     ` Hans Verkuil
  0 siblings, 1 reply; 20+ messages in thread
From: Dafna Hirschfeld @ 2019-09-20 15:07 UTC (permalink / raw)
  To: Hans Verkuil, linux-media
  Cc: laurent.pinchart, helen.koike, ezequiel, andre.almeida, skhan,
	kernel, dafna3

Hi,

On Fri, 2019-09-20 at 15:17 +0200, Hans Verkuil wrote:
> Hi Dafna,
> 
> On 9/19/19 10:32 PM, Dafna Hirschfeld wrote:
> > This patchset introduces the usage of configfs in order to create vimc devices
> > with a configured topology. A patch introducing configfs usage was already sent by Helen Koike:
> > https://patchwork.linuxtv.org/patch/53397/ . The current patch is based on her patch but
> > suggests a new API for using configfs.
> > It uses symlinks to represent a link between two entities, an approach already used in the kernel
> > by usb gadgets composed with configfs to associate usb gadget's functions to its configurations.
> > For example, a topology of sensor->capture will be created with the following commands:
> > 
> > CONFIGFS_ROOT=/sys/kernel/config
> > 
> > mkdir ${CONFIGFS_ROOT}/vimc/mdev/
> > mkdir ${CONFIGFS_ROOT}/vimc/mdev/vimc-sensor:sen
> > mkdir ${CONFIGFS_ROOT}/vimc/mdev/vimc-capture:cap
> > tree ${CONFIGFS_ROOT}
> > /configfs/
> > `-- vimc
> >     `-- mdev
> >         |-- hotplug
> >         |-- vimc-capture:cap
> >         |   `-- pad:sink:0
> >         `-- vimc-sensor:sen
> >             `-- pad:source:0
> > 
> > mkdir ${CONFIGFS_ROOT}/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap
> > ln -s ${CONFIGFS_ROOT}/vimc/mdev/vimc-capture:cap/pad:sink:0 ${CONFIGFS_ROOT}/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap
> > tree ${CONFIGFS_ROOT}
> > /configfs/
> > `-- vimc
> >     `-- mdev
> >         |-- hotplug
> >         |-- vimc-capture:cap
> >         |   `-- pad:sink:0
> >         `-- vimc-sensor:sen
> >             `-- pad:source:0
> >                 `-- to-cap
> >                     |-- enabled
> >                     |-- immutable
> >                     `-- pad:sink:0 -> ../../../../../vimc/mdev/vimc-capture:cap/pad:sink:0
> > 
> > There are several reasons to prefer the symlink approach in order to represent links between entities.
> > The previous approach in which links are represented with directories of the form 'entity1:pad>-><entity2:pad'
> > requires userspace to parse the dirctories names in order to understand the topology, while in the symlink
> > approach userspace needs only to traverse the configfs tree.
> > Also, the usage of symlinks prevents userspace from creating links between entities that don't exist and also
> > an entity can't be removed if there is a symlink pointing to it or from it, while in the previous approach the
> 
> Why can't you remove an entity if there is a symlink pointing to it?
> 
> In the example above I can remove mdev/vimc-capture:cap just fine. Afterwards
> the pad:sink:0 symlink will point to a non-existing file, but that's valid
> symlink behavior.

Hmm, this should not be allowed, maybe you did something wrong?
I get:
# rmdir /sys/kernel/config/vimc/mdev/vimc-capture:cap
rmdir: failed to remove '/sys/kernel/config/vimc/mdev/vimc-capture:cap': Device or resource busy


> Or is vimc checking internally and prohibits the user from making invalid changes?

No, this is not internal to vimc, it is part of configfs, it is also written in the docs:

"A config_item cannot be removed while it links to any other item, nor
can it be removed while an item links to it.  Dangling symlinks are not
allowed in configfs."

Regards,
Dafna

> In any case, this cover letter is a bit confusing and needs to address this in more
> detail.
> 
> Regards,
> 
> 	Hans
> 
> > links were created by creating unrelated directories and care had to be taken to ensure consistency. This way
> > the topology configured from userspace is restricted to always be valid and represent the current topology of
> > the device. This results in less validation needed in kernel code when plugging the device and less possibility
> > for mistakes in the userspace side. Last, but not least, using symlinks is the natural way of associating things
> > in configfs.
> > 
> > This patch is meant to demonstrate the suggested configfs api and get comments and acceptance/disagreement from
> > the community. It passes few tests that configure basic topology and streams the capture entities.
> > Here is the tests script: https://gitlab.collabora.com/dafna/scripts/blob/master/configfs/sym-unit-tests-simple-topo.sh
> > Further versions will go through more extensive debugging.
> > 
> > The patchset is rebased on top of v5 of the patchset 'Collapse vimc into single monolithic driver' sent by Shuah Khan
> > https://lkml.org/lkml/2019/9/17/656
> > 
> > Patch 1, was sent by me before as a single patch and is needed for the configfs implementation.
> > 
> > Patch 2, documents how to use the new configfs api in order to create and set devices topologies.
> > 
> > Patch 3, only adds the new configfs api code but does not use it yet, so it still creates only the hardcoded device.
> > 
> > Patch 4, removes the hardcoded device topology and creates devices with topologies configured with the configfs.
> > 
> > Patch 5, implements indexing for the bus_info field since now there can be more than one vimc device.
> > 
> > Dafna Hirschfeld (5):
> >   media: vimc: upon streaming, check that the pipeline starts with a
> >     source entity
> >   docs: media: vimc: Documenting vimc topology configuration using
> >     configfs
> >   media: vimc: Add the implementation for the configfs api
> >   media: vimc: use configfs instead of having hardcoded configuration
> >   media: vimc: Add device index to the bus_info
> > 
> >  Documentation/media/v4l-drivers/vimc.dot    |  28 +-
> >  Documentation/media/v4l-drivers/vimc.rst    | 240 ++++++-
> >  drivers/media/platform/vimc/Kconfig         |   9 +-
> >  drivers/media/platform/vimc/Makefile        |   2 +-
> >  drivers/media/platform/vimc/vimc-capture.c  |  50 +-
> >  drivers/media/platform/vimc/vimc-common.h   |  86 ++-
> >  drivers/media/platform/vimc/vimc-configfs.c | 656 ++++++++++++++++++++
> >  drivers/media/platform/vimc/vimc-configfs.h |  41 ++
> >  drivers/media/platform/vimc/vimc-core.c     | 350 +++++------
> >  drivers/media/platform/vimc/vimc-debayer.c  |  35 +-
> >  drivers/media/platform/vimc/vimc-scaler.c   |  35 +-
> >  drivers/media/platform/vimc/vimc-sensor.c   |  33 +-
> >  drivers/media/platform/vimc/vimc-streamer.c |  39 +-
> >  13 files changed, 1289 insertions(+), 315 deletions(-)
> >  create mode 100644 drivers/media/platform/vimc/vimc-configfs.c
> >  create mode 100644 drivers/media/platform/vimc/vimc-configfs.h
> > 


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 0/5] media: vimc: use configfs in order to configure devices topologies
  2019-09-20 15:07   ` Dafna Hirschfeld
@ 2019-09-20 15:10     ` Hans Verkuil
  0 siblings, 0 replies; 20+ messages in thread
From: Hans Verkuil @ 2019-09-20 15:10 UTC (permalink / raw)
  To: Dafna Hirschfeld, linux-media
  Cc: laurent.pinchart, helen.koike, ezequiel, andre.almeida, skhan,
	kernel, dafna3

On 9/20/19 5:07 PM, Dafna Hirschfeld wrote:
> Hi,
> 
> On Fri, 2019-09-20 at 15:17 +0200, Hans Verkuil wrote:
>> Hi Dafna,
>>
>> On 9/19/19 10:32 PM, Dafna Hirschfeld wrote:
>>> This patchset introduces the usage of configfs in order to create vimc devices
>>> with a configured topology. A patch introducing configfs usage was already sent by Helen Koike:
>>> https://patchwork.linuxtv.org/patch/53397/ . The current patch is based on her patch but
>>> suggests a new API for using configfs.
>>> It uses symlinks to represent a link between two entities, an approach already used in the kernel
>>> by usb gadgets composed with configfs to associate usb gadget's functions to its configurations.
>>> For example, a topology of sensor->capture will be created with the following commands:
>>>
>>> CONFIGFS_ROOT=/sys/kernel/config
>>>
>>> mkdir ${CONFIGFS_ROOT}/vimc/mdev/
>>> mkdir ${CONFIGFS_ROOT}/vimc/mdev/vimc-sensor:sen
>>> mkdir ${CONFIGFS_ROOT}/vimc/mdev/vimc-capture:cap
>>> tree ${CONFIGFS_ROOT}
>>> /configfs/
>>> `-- vimc
>>>     `-- mdev
>>>         |-- hotplug
>>>         |-- vimc-capture:cap
>>>         |   `-- pad:sink:0
>>>         `-- vimc-sensor:sen
>>>             `-- pad:source:0
>>>
>>> mkdir ${CONFIGFS_ROOT}/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap
>>> ln -s ${CONFIGFS_ROOT}/vimc/mdev/vimc-capture:cap/pad:sink:0 ${CONFIGFS_ROOT}/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap
>>> tree ${CONFIGFS_ROOT}
>>> /configfs/
>>> `-- vimc
>>>     `-- mdev
>>>         |-- hotplug
>>>         |-- vimc-capture:cap
>>>         |   `-- pad:sink:0
>>>         `-- vimc-sensor:sen
>>>             `-- pad:source:0
>>>                 `-- to-cap
>>>                     |-- enabled
>>>                     |-- immutable
>>>                     `-- pad:sink:0 -> ../../../../../vimc/mdev/vimc-capture:cap/pad:sink:0
>>>
>>> There are several reasons to prefer the symlink approach in order to represent links between entities.
>>> The previous approach in which links are represented with directories of the form 'entity1:pad>-><entity2:pad'
>>> requires userspace to parse the dirctories names in order to understand the topology, while in the symlink
>>> approach userspace needs only to traverse the configfs tree.
>>> Also, the usage of symlinks prevents userspace from creating links between entities that don't exist and also
>>> an entity can't be removed if there is a symlink pointing to it or from it, while in the previous approach the
>>
>> Why can't you remove an entity if there is a symlink pointing to it?
>>
>> In the example above I can remove mdev/vimc-capture:cap just fine. Afterwards
>> the pad:sink:0 symlink will point to a non-existing file, but that's valid
>> symlink behavior.
> 
> Hmm, this should not be allowed, maybe you did something wrong?
> I get:
> # rmdir /sys/kernel/config/vimc/mdev/vimc-capture:cap
> rmdir: failed to remove '/sys/kernel/config/vimc/mdev/vimc-capture:cap': Device or resource busy
> 
> 
>> Or is vimc checking internally and prohibits the user from making invalid changes?
> 
> No, this is not internal to vimc, it is part of configfs, it is also written in the docs:
> 
> "A config_item cannot be removed while it links to any other item, nor
> can it be removed while an item links to it.  Dangling symlinks are not
> allowed in configfs."

Ah, I was not aware of that. Just ignore my comments about this, then :-)

Nice, I learned something today.

Regards,

	Hans

> 
> Regards,
> Dafna
> 
>> In any case, this cover letter is a bit confusing and needs to address this in more
>> detail.
>>
>> Regards,
>>
>> 	Hans
>>
>>> links were created by creating unrelated directories and care had to be taken to ensure consistency. This way
>>> the topology configured from userspace is restricted to always be valid and represent the current topology of
>>> the device. This results in less validation needed in kernel code when plugging the device and less possibility
>>> for mistakes in the userspace side. Last, but not least, using symlinks is the natural way of associating things
>>> in configfs.
>>>
>>> This patch is meant to demonstrate the suggested configfs api and get comments and acceptance/disagreement from
>>> the community. It passes few tests that configure basic topology and streams the capture entities.
>>> Here is the tests script: https://gitlab.collabora.com/dafna/scripts/blob/master/configfs/sym-unit-tests-simple-topo.sh
>>> Further versions will go through more extensive debugging.
>>>
>>> The patchset is rebased on top of v5 of the patchset 'Collapse vimc into single monolithic driver' sent by Shuah Khan
>>> https://lkml.org/lkml/2019/9/17/656
>>>
>>> Patch 1, was sent by me before as a single patch and is needed for the configfs implementation.
>>>
>>> Patch 2, documents how to use the new configfs api in order to create and set devices topologies.
>>>
>>> Patch 3, only adds the new configfs api code but does not use it yet, so it still creates only the hardcoded device.
>>>
>>> Patch 4, removes the hardcoded device topology and creates devices with topologies configured with the configfs.
>>>
>>> Patch 5, implements indexing for the bus_info field since now there can be more than one vimc device.
>>>
>>> Dafna Hirschfeld (5):
>>>   media: vimc: upon streaming, check that the pipeline starts with a
>>>     source entity
>>>   docs: media: vimc: Documenting vimc topology configuration using
>>>     configfs
>>>   media: vimc: Add the implementation for the configfs api
>>>   media: vimc: use configfs instead of having hardcoded configuration
>>>   media: vimc: Add device index to the bus_info
>>>
>>>  Documentation/media/v4l-drivers/vimc.dot    |  28 +-
>>>  Documentation/media/v4l-drivers/vimc.rst    | 240 ++++++-
>>>  drivers/media/platform/vimc/Kconfig         |   9 +-
>>>  drivers/media/platform/vimc/Makefile        |   2 +-
>>>  drivers/media/platform/vimc/vimc-capture.c  |  50 +-
>>>  drivers/media/platform/vimc/vimc-common.h   |  86 ++-
>>>  drivers/media/platform/vimc/vimc-configfs.c | 656 ++++++++++++++++++++
>>>  drivers/media/platform/vimc/vimc-configfs.h |  41 ++
>>>  drivers/media/platform/vimc/vimc-core.c     | 350 +++++------
>>>  drivers/media/platform/vimc/vimc-debayer.c  |  35 +-
>>>  drivers/media/platform/vimc/vimc-scaler.c   |  35 +-
>>>  drivers/media/platform/vimc/vimc-sensor.c   |  33 +-
>>>  drivers/media/platform/vimc/vimc-streamer.c |  39 +-
>>>  13 files changed, 1289 insertions(+), 315 deletions(-)
>>>  create mode 100644 drivers/media/platform/vimc/vimc-configfs.c
>>>  create mode 100644 drivers/media/platform/vimc/vimc-configfs.h
>>>
> 


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 2/5] docs: media: vimc: Documenting vimc topology configuration using configfs
  2019-09-20 13:39   ` Hans Verkuil
@ 2019-09-23  9:29     ` Dafna Hirschfeld
  2019-09-23  9:50       ` Hans Verkuil
  0 siblings, 1 reply; 20+ messages in thread
From: Dafna Hirschfeld @ 2019-09-23  9:29 UTC (permalink / raw)
  To: Hans Verkuil, linux-media
  Cc: laurent.pinchart, helen.koike, ezequiel, andre.almeida, skhan,
	kernel, dafna3

On Fri, 2019-09-20 at 15:39 +0200, Hans Verkuil wrote:
> On 9/19/19 10:32 PM, Dafna Hirschfeld wrote:
> > Add explanation of how to use configfs in order to create a
> > vimc device with a given topology.
> > 
> > Signed-off-by: Dafna Hirschfeld <dafna.hirschfeld@collabora.com>
> > ---
> >  Documentation/media/v4l-drivers/vimc.dot |  28 ++-
> >  Documentation/media/v4l-drivers/vimc.rst | 240 ++++++++++++++++++++---
> >  2 files changed, 220 insertions(+), 48 deletions(-)
> > 
> > diff --git a/Documentation/media/v4l-drivers/vimc.dot b/Documentation/media/v4l-drivers/vimc.dot
> > index 57863a13fa39..e3b41ac2bc46 100644
> > --- a/Documentation/media/v4l-drivers/vimc.dot
> > +++ b/Documentation/media/v4l-drivers/vimc.dot
> > @@ -2,21 +2,15 @@
> >  
> >  digraph board {
> >  	rankdir=TB
> > -	n00000001 [label="{{} | Sensor A\n/dev/v4l-subdev0 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green]
> > -	n00000001:port0 -> n00000005:port0 [style=bold]
> > -	n00000001:port0 -> n0000000b [style=bold]
> > -	n00000003 [label="{{} | Sensor B\n/dev/v4l-subdev1 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green]
> > -	n00000003:port0 -> n00000008:port0 [style=bold]
> > -	n00000003:port0 -> n0000000f [style=bold]
> > -	n00000005 [label="{{<port0> 0} | Debayer A\n/dev/v4l-subdev2 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
> > -	n00000005:port1 -> n00000017:port0
> > -	n00000008 [label="{{<port0> 0} | Debayer B\n/dev/v4l-subdev3 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
> > -	n00000008:port1 -> n00000017:port0 [style=dashed]
> > -	n0000000b [label="Raw Capture 0\n/dev/video0", shape=box, style=filled, fillcolor=yellow]
> > -	n0000000f [label="Raw Capture 1\n/dev/video1", shape=box, style=filled, fillcolor=yellow]
> > -	n00000013 [label="RGB/YUV Input\n/dev/video2", shape=box, style=filled, fillcolor=yellow]
> > -	n00000013 -> n00000017:port0 [style=dashed]
> > -	n00000017 [label="{{<port0> 0} | Scaler\n/dev/v4l-subdev4 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
> > -	n00000017:port1 -> n0000001a [style=bold]
> > -	n0000001a [label="RGB/YUV Capture\n/dev/video3", shape=box, style=filled, fillcolor=yellow]
> > +	n00000001 [label="cap-deb\n/dev/video0", shape=box, style=filled, fillcolor=yellow]
> > +	n00000005 [label="cap-sen\n/dev/video1", shape=box, style=filled, fillcolor=yellow]
> > +	n00000009 [label="cap-sca\n/dev/video2", shape=box, style=filled, fillcolor=yellow]
> > +	n0000000d [label="{{<port0> 0} | sca\n/dev/v4l-subdev0 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
> > +	n0000000d:port1 -> n00000009 [style=bold]
> > +	n00000010 [label="{{<port0> 0} | deb\n/dev/v4l-subdev1 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
> > +	n00000010:port1 -> n00000001 [style=bold]
> > +	n00000010:port1 -> n0000000d:port0 [style=bold]
> > +	n00000013 [label="{{} | sen\n/dev/v4l-subdev2 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green]
> > +	n00000013:port0 -> n00000005 [style=bold]
> > +	n00000013:port0 -> n00000010:port0 [style=bold]
> >  }
> > diff --git a/Documentation/media/v4l-drivers/vimc.rst b/Documentation/media/v4l-drivers/vimc.rst
> > index a582af0509ee..e5636883545f 100644
> > --- a/Documentation/media/v4l-drivers/vimc.rst
> > +++ b/Documentation/media/v4l-drivers/vimc.rst
> > @@ -1,45 +1,225 @@
> >  .. SPDX-License-Identifier: GPL-2.0
> >  
> > +==========================================
> >  The Virtual Media Controller Driver (vimc)
> >  ==========================================
> >  
> > -The vimc driver emulates complex video hardware using the V4L2 API and the Media
> > -API. It has a capture device and three subdevices: sensor, debayer and scaler.
> > +The vimc driver emulates complex video hardware topologies using the V4L2 API
> > +and the Media API. It has a capture device and three subdevices:
> > +sensor, debayer and scaler. It exposes media devices through /dev/mediaX nodes,
> > +video capture devices through /dev/videoX and sub-devices through /dev/v4l-subdevX.
> > +
> > +
> > +To configure a media device of a given topology, a ConfigFS API is provided.
> > +
> > +Configuring a topology through ConfigFS (Experimental)
> > +======================================================
> > +
> > +.. note:: This API is under development and might change in the future.
> > +
> > +Mount configfs:
> > +::
> > +
> > +	$ mkdir /configfs
> > +	$ mount -t configfs none /configfs
> > +
> > +When loading the module, you will see a folder named vimc
> > +::
> > +
> > +	$ tree /configfs/
> > +	/configfs/
> > +	`-- vimc
> > +
> > +Creating a media device
> > +-----------------------
> > +
> > +To create a media device create a new folder under /configfs/vimc/
> > +
> > +Example:
> > +::
> > +
> > +	$ mkdir /configfs/vimc/mdev
> > +	$ tree /configfs/vimc/mdev
> > +	/configfs/
> > +	`-- vimc
> > +	    `-- mdev
> > +	        `-- hotplug
> > +
> > +Creating entities
> > +-----------------
> > +
> > +To create an entity in the media device's topology, create a folder under
> > +``/configfs/vimc/<mdev-name>/`` with the following format:
> > +
> > +	<entity-type>:<entity-name>
> 
> I suspect that there are restrictions to the characters that can be used in
> these names. For one the use of ':' in the entity-name is probably a bad idea.
> Also the entity name should be unique, i.e. you can't have a sensor and a scaler
> with the same entity name.
> 
Why is the use of ':' bad idea?
To which char you suggest to replace it?

> Such restrictions should be documented here and also tested in the driver!
> 
> > +
> > +Where <entity-type> is one of the following:
> > +
> > +- vimc-sensor
> > +- vimc-scaler
> > +- vimc-debayer
> > +- vimc-capture
> > +
> > +Example:
> > +::
> > +
> > +	$ mkdir /configfs/vimc/mdev/vimc-sensor:sen
> > +	$ mkdir /configfs/vimc/mdev/vimc-capture:cap-sen
> > +	$ tree /configfs/
> > +	/configfs/
> > +	`-- vimc
> > +	    `-- mdev
> > +		|-- hotplug
> > +		|-- vimc-capture:cap-sen
> > +		|   `-- pad:sink:0
> > +		`-- vimc-sensor:sen
> > +                    `-- pad:source:0
> 
> Do we need the 'pad:' prefix here? Isn't 'sink:' or 'source:' sufficient?
> I think pad:sink:0 is rather cumbersome.
> 
> > +
> > +Default folders are created under the entity directory for each pad of the entity.
> > +
> > +Creating links
> > +--------------
> > +
> > +To create a link between two entities, you should create a directory for the link
> > +under the source pad of the link and then set it to be a symbolic link to the sink pad:
> >  
> > -Topology
> > ---------
> > +Example:
> > +::
> > +
> > +	$ mkdir "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap"
> 
> I'd call the directory 'to-cap-sen': it's clearer that this points to the
> cap-sen entity. It makes the example easier to understand.
> 
> > +	$ ln -s "/configfs/vimc/mdev/vimc-capture:cap-sen/pad:sink:0" "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap"
> > +	$ tree /configfs
> > +	/configfs
> > +	`-- vimc
> > +	    `-- mdev
> > +	        |-- hotplug
> > +	        |-- vimc-capture:cap-sen
> > +	        |   `-- pad:sink:0
> > +	        `-- vimc-sensor:sen
> > +	            `-- pad:source:0
> > +	                `-- to-cap
> > +	                    |-- enabled
> > +	                    |-- immutable
> > +	                    `-- pad:sink:0 -> ../../../../../vimc/mdev/vimc-capture:cap-sen/pad:sink:0
> > +
> > +The `enabled` and `immutable` are two boolean attributes of the link corresponding to the link flags
> > +
> > +
> > +Flag values are described in :ref:`Documentation/media/uapi/mediactl/media-types.rst <media-link-flag>`
> > +( seek for ``MEDIA_LNK_FL_*``)
> > +
> > +1 - Enabled
> > +	Indicates that 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.
> > +
> > +Change an attribute of the link by writing "on" or "1" to set it on , and "off" or "0" to set it off
> > +
> > +Example:
> > +::
> > +
> > +	$ echo "on" > /configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap/immutable
> > +
> > +Activating/Deactivating device
> > +------------------------------
> > +
> > +To activate the device, write one of "plugged", "plug" or "1" to the file
> > +``/configfs/vimc/<mdev-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 the file
> > +``/configfs/vimc/<ndev-name>/hotplug``
> > +
> > +Example:
> > +::
> > +
> > +	$ echo unplugged > /configfs/vimc/mdev/hotplug
> > +
> > +Topology Configuration - Full Example
> > +-------------------------------------
> > +
> > +Here is a full example of a simple topology configuration:
> > +
> > +.. code-block:: bash
> > +
> > +    # Creating the entities
> > +    mkdir "/configfs/vimc/mdev"
> > +    mkdir "/configfs/vimc/mdev/vimc-sensor:sen"
> > +    mkdir "/configfs/vimc/mdev/vimc-debayer:deb"
> > +    mkdir "/configfs/vimc/mdev/vimc-scaler:sca"
> > +    mkdir "/configfs/vimc/mdev/vimc-capture:cap-sca" #/dev/video2
> > +    mkdir "/configfs/vimc/mdev/vimc-capture:cap-sen" #/dev/video1
> > +    mkdir "/configfs/vimc/mdev/vimc-capture:cap-deb" #/dev/video0
> > +
> > +    # Creating the links
> > +    #sen -> deb
> > +    mkdir "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-deb"
> > +    ln -s "/configfs/vimc/mdev/vimc-debayer:deb/pad:sink:0" "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-deb"
> > +    echo on > "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-deb/immutable"
> 
> If you set immutable to on, then it should automatically set enabled to on as well,
> so no need for the next line.

Andrzej Pietrasiewicz suggested not to allow setting the 'immutable' to on if 'enabled' is off
and not to allow setting 'enabled' to off if 'immutable' it on.
He says this is better than an interface where a change in one file cause a change in another file.

> 
> > +    echo on > "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-deb/enabled"
> > +
> > +    #deb -> sca
> > +    mkdir "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-sca"
> > +    ln -s "/configfs/vimc/mdev/vimc-scaler:sca/pad:sink:0" "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-sca"
> > +    echo on > "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-sca/immutable"
> > +    echo on > "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-sca/enabled"
> > +
> > +    #sca -> cap-sca
> > +    mkdir "/configfs/vimc/mdev/vimc-scaler:sca/pad:source:1/to-cap"
> 
> Same in this example: use to-cap-sca
> 
> > +    ln -s "/configfs/vimc/mdev/vimc-capture:cap-sca/pad:sink:0" "/configfs/vimc/mdev/vimc-scaler:sca/pad:source:1/to-cap"
> > +    echo on > "/configfs/vimc/mdev/vimc-scaler:sca/pad:source:1/to-cap/immutable"
> > +    echo on > "/configfs/vimc/mdev/vimc-scaler:sca/pad:source:1/to-cap/enabled"
> > +
> > +    #sen -> cap-sen
> > +    mkdir "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap"
> > +    ln -s "/configfs/vimc/mdev/vimc-capture:cap-sen/pad:sink:0" "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap"
> > +    echo on > "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap/immutable"
> > +    echo on > "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap/enabled"
> > +
> > +    #deb -> cap-deb
> > +    mkdir "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-cap"
> > +    ln -s "/configfs/vimc/mdev/vimc-capture:cap-deb/pad:sink:0" "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-cap"
> > +    echo on > "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-cap/immutable"
> > +    echo on > "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-cap/enabled"
> >  
> > -The topology is hardcoded, although you could modify it in vimc-core and
> > -recompile the driver to achieve your own topology. This is the default topology:
> >  
> >  .. _vimc_topology_graph:
> >  
> >  .. kernel-figure:: vimc.dot
> > -    :alt:   Diagram of the default media pipeline topology
> > +    :alt:   Diagram of the configured simple topology in the example
> >      :align: center
> >  
> > -    Media pipeline graph on vimc
> > +    Simple Media pipeline graph on vimc configured through configfs
> >  
> > -Configuring the topology
> > -~~~~~~~~~~~~~~~~~~~~~~~~
> > +Configuring the pipeline formats
> > +================================
> >  
> > -Each subdevice will come with its default configuration (pixelformat, height,
> > -width, ...). One needs to configure the topology in order to match the
> > +Each subdevice has a default format configuration (pixelformat, height,
> > +width, ...). You should configure the formats in order to match the
> >  configuration on each linked subdevice to stream frames through the pipeline.
> > -If the configuration doesn't match, the stream will fail. The ``v4l-utils``
> > +If the configuration doesn't match, streaming will fail. The ``v4l-utils``
> >  package is a bundle of user-space applications, that comes with ``media-ctl`` and
> > -``v4l2-ctl`` that can be used to configure the vimc configuration. This sequence
> > -of commands fits for the default topology:
> > +``v4l2-ctl`` that can be used to configure the formats of the entities. This sequence
> > +of commands fits the simple topology created in the full example of topology configuration:
> >  
> >  .. code-block:: bash
> >  
> > -        media-ctl -d platform:vimc -V '"Sensor A":0[fmt:SBGGR8_1X8/640x480]'
> > -        media-ctl -d platform:vimc -V '"Debayer A":0[fmt:SBGGR8_1X8/640x480]'
> > -        media-ctl -d platform:vimc -V '"Sensor B":0[fmt:SBGGR8_1X8/640x480]'
> > -        media-ctl -d platform:vimc -V '"Debayer B":0[fmt:SBGGR8_1X8/640x480]'
> > -        v4l2-ctl -z platform:vimc -d "RGB/YUV Capture" -v width=1920,height=1440
> > -        v4l2-ctl -z platform:vimc -d "Raw Capture 0" -v pixelformat=BA81
> > -        v4l2-ctl -z platform:vimc -d "Raw Capture 1" -v pixelformat=BA81
> > +	media-ctl -d platform:vimc-000 -V '"sen":0[fmt:SBGGR8_1X8/640x480]'
> > +	media-ctl -d platform:vimc-000 -V '"deb":0[fmt:SBGGR8_1X8/640x480]'
> > +	media-ctl -d platform:vimc-000 -V '"deb":1[fmt:RGB888_1X24/640x480]'
> > +	media-ctl -d platform:vimc-000 -V '"sca":0[fmt:RGB888_1X24/640x480]'
> > +	media-ctl -d platform:vimc-000 -V '"sca":1[fmt:RGB888_1X24/640x480]'
> > +	v4l2-ctl -z platform:vimc-000 -d "cap-sen" -v pixelformat=BA81
> > +	v4l2-ctl -z platform:vimc-000 -d "cap-deb" -v pixelformat=RGB3
> > +	# The default scaling value of the scaler is 3, so need to set its capture accordingly
> > +	v4l2-ctl -z platform:vimc -d "cap-sca" -v pixelformat=RGB3,width=1920,height=1440
> >  
> >  Subdevices
> >  ----------
> > @@ -61,8 +241,8 @@ vimc-debayer:
> >  	* 1 Pad source
> >  
> >  vimc-scaler:
> > -	Scale up the image by a factor of 3. E.g.: a 640x480 image becomes a
> > -        1920x1440 image. (this value can be configured, see at
> > +	Scales up the image by a factor of 3. E.g.: a 640x480 image becomes a
> > +        1920x1440 image. (this value can be configured, see
> 
> Note that this might change with this patch:
> 
> https://patchwork.kernel.org/patch/11146175/
> 
> Just so you are aware of this.
> 
> >          `Module options`_).
> >  	Exposes:
> >  
> > @@ -77,12 +257,10 @@ vimc-capture:
> >  	* 1 Pad source
> >  
> >  
> > -        Module options
> > ----------------
> > -
> > -Vimc has a few module parameters to configure the driver.
> > +Module options
> > +==============
> >  
> > -        param=value
> > +Vimc has 2 module parameters to configure the driver.
> >  
> >  * ``sca_mult=<unsigned int>``
> >  
> > @@ -98,10 +276,10 @@ Vimc has a few module parameters to configure the driver.
> >          otherwise the next odd number is considered (the default value is 3).
> >  
> >  Source code documentation
> > --------------------------
> > +=========================
> >  
> >  vimc-streamer
> > -~~~~~~~~~~~~~
> > +-------------
> >  
> >  .. kernel-doc:: drivers/media/platform/vimc/vimc-streamer.h
> >     :internal:
> > 
> 
> Regards,
> 
> 	Hans

Thanks,

Dafna


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 2/5] docs: media: vimc: Documenting vimc topology configuration using configfs
  2019-09-23  9:29     ` Dafna Hirschfeld
@ 2019-09-23  9:50       ` Hans Verkuil
  2019-09-27  5:56         ` Andrzej Pietrasiewicz
  0 siblings, 1 reply; 20+ messages in thread
From: Hans Verkuil @ 2019-09-23  9:50 UTC (permalink / raw)
  To: Dafna Hirschfeld, linux-media
  Cc: laurent.pinchart, helen.koike, ezequiel, andre.almeida, skhan,
	kernel, dafna3

On 9/23/19 11:29 AM, Dafna Hirschfeld wrote:
> On Fri, 2019-09-20 at 15:39 +0200, Hans Verkuil wrote:
>> On 9/19/19 10:32 PM, Dafna Hirschfeld wrote:
>>> Add explanation of how to use configfs in order to create a
>>> vimc device with a given topology.
>>>
>>> Signed-off-by: Dafna Hirschfeld <dafna.hirschfeld@collabora.com>
>>> ---
>>>  Documentation/media/v4l-drivers/vimc.dot |  28 ++-
>>>  Documentation/media/v4l-drivers/vimc.rst | 240 ++++++++++++++++++++---
>>>  2 files changed, 220 insertions(+), 48 deletions(-)
>>>
>>> diff --git a/Documentation/media/v4l-drivers/vimc.dot b/Documentation/media/v4l-drivers/vimc.dot
>>> index 57863a13fa39..e3b41ac2bc46 100644
>>> --- a/Documentation/media/v4l-drivers/vimc.dot
>>> +++ b/Documentation/media/v4l-drivers/vimc.dot
>>> @@ -2,21 +2,15 @@
>>>  
>>>  digraph board {
>>>  	rankdir=TB
>>> -	n00000001 [label="{{} | Sensor A\n/dev/v4l-subdev0 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green]
>>> -	n00000001:port0 -> n00000005:port0 [style=bold]
>>> -	n00000001:port0 -> n0000000b [style=bold]
>>> -	n00000003 [label="{{} | Sensor B\n/dev/v4l-subdev1 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green]
>>> -	n00000003:port0 -> n00000008:port0 [style=bold]
>>> -	n00000003:port0 -> n0000000f [style=bold]
>>> -	n00000005 [label="{{<port0> 0} | Debayer A\n/dev/v4l-subdev2 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
>>> -	n00000005:port1 -> n00000017:port0
>>> -	n00000008 [label="{{<port0> 0} | Debayer B\n/dev/v4l-subdev3 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
>>> -	n00000008:port1 -> n00000017:port0 [style=dashed]
>>> -	n0000000b [label="Raw Capture 0\n/dev/video0", shape=box, style=filled, fillcolor=yellow]
>>> -	n0000000f [label="Raw Capture 1\n/dev/video1", shape=box, style=filled, fillcolor=yellow]
>>> -	n00000013 [label="RGB/YUV Input\n/dev/video2", shape=box, style=filled, fillcolor=yellow]
>>> -	n00000013 -> n00000017:port0 [style=dashed]
>>> -	n00000017 [label="{{<port0> 0} | Scaler\n/dev/v4l-subdev4 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
>>> -	n00000017:port1 -> n0000001a [style=bold]
>>> -	n0000001a [label="RGB/YUV Capture\n/dev/video3", shape=box, style=filled, fillcolor=yellow]
>>> +	n00000001 [label="cap-deb\n/dev/video0", shape=box, style=filled, fillcolor=yellow]
>>> +	n00000005 [label="cap-sen\n/dev/video1", shape=box, style=filled, fillcolor=yellow]
>>> +	n00000009 [label="cap-sca\n/dev/video2", shape=box, style=filled, fillcolor=yellow]
>>> +	n0000000d [label="{{<port0> 0} | sca\n/dev/v4l-subdev0 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
>>> +	n0000000d:port1 -> n00000009 [style=bold]
>>> +	n00000010 [label="{{<port0> 0} | deb\n/dev/v4l-subdev1 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
>>> +	n00000010:port1 -> n00000001 [style=bold]
>>> +	n00000010:port1 -> n0000000d:port0 [style=bold]
>>> +	n00000013 [label="{{} | sen\n/dev/v4l-subdev2 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green]
>>> +	n00000013:port0 -> n00000005 [style=bold]
>>> +	n00000013:port0 -> n00000010:port0 [style=bold]
>>>  }
>>> diff --git a/Documentation/media/v4l-drivers/vimc.rst b/Documentation/media/v4l-drivers/vimc.rst
>>> index a582af0509ee..e5636883545f 100644
>>> --- a/Documentation/media/v4l-drivers/vimc.rst
>>> +++ b/Documentation/media/v4l-drivers/vimc.rst
>>> @@ -1,45 +1,225 @@
>>>  .. SPDX-License-Identifier: GPL-2.0
>>>  
>>> +==========================================
>>>  The Virtual Media Controller Driver (vimc)
>>>  ==========================================
>>>  
>>> -The vimc driver emulates complex video hardware using the V4L2 API and the Media
>>> -API. It has a capture device and three subdevices: sensor, debayer and scaler.
>>> +The vimc driver emulates complex video hardware topologies using the V4L2 API
>>> +and the Media API. It has a capture device and three subdevices:
>>> +sensor, debayer and scaler. It exposes media devices through /dev/mediaX nodes,
>>> +video capture devices through /dev/videoX and sub-devices through /dev/v4l-subdevX.
>>> +
>>> +
>>> +To configure a media device of a given topology, a ConfigFS API is provided.
>>> +
>>> +Configuring a topology through ConfigFS (Experimental)
>>> +======================================================
>>> +
>>> +.. note:: This API is under development and might change in the future.
>>> +
>>> +Mount configfs:
>>> +::
>>> +
>>> +	$ mkdir /configfs
>>> +	$ mount -t configfs none /configfs
>>> +
>>> +When loading the module, you will see a folder named vimc
>>> +::
>>> +
>>> +	$ tree /configfs/
>>> +	/configfs/
>>> +	`-- vimc
>>> +
>>> +Creating a media device
>>> +-----------------------
>>> +
>>> +To create a media device create a new folder under /configfs/vimc/
>>> +
>>> +Example:
>>> +::
>>> +
>>> +	$ mkdir /configfs/vimc/mdev
>>> +	$ tree /configfs/vimc/mdev
>>> +	/configfs/
>>> +	`-- vimc
>>> +	    `-- mdev
>>> +	        `-- hotplug
>>> +
>>> +Creating entities
>>> +-----------------
>>> +
>>> +To create an entity in the media device's topology, create a folder under
>>> +``/configfs/vimc/<mdev-name>/`` with the following format:
>>> +
>>> +	<entity-type>:<entity-name>
>>
>> I suspect that there are restrictions to the characters that can be used in
>> these names. For one the use of ':' in the entity-name is probably a bad idea.
>> Also the entity name should be unique, i.e. you can't have a sensor and a scaler
>> with the same entity name.
>>
> Why is the use of ':' bad idea?
> To which char you suggest to replace it?

You are right, ':' is allowed in the entity-name. So ignore that comment.

But entity-name must be unique within the mdev directory. That's a MC requirement.

> 
>> Such restrictions should be documented here and also tested in the driver!
>>
>>> +
>>> +Where <entity-type> is one of the following:
>>> +
>>> +- vimc-sensor
>>> +- vimc-scaler
>>> +- vimc-debayer
>>> +- vimc-capture
>>> +
>>> +Example:
>>> +::
>>> +
>>> +	$ mkdir /configfs/vimc/mdev/vimc-sensor:sen
>>> +	$ mkdir /configfs/vimc/mdev/vimc-capture:cap-sen
>>> +	$ tree /configfs/
>>> +	/configfs/
>>> +	`-- vimc
>>> +	    `-- mdev
>>> +		|-- hotplug
>>> +		|-- vimc-capture:cap-sen
>>> +		|   `-- pad:sink:0
>>> +		`-- vimc-sensor:sen
>>> +                    `-- pad:source:0
>>
>> Do we need the 'pad:' prefix here? Isn't 'sink:' or 'source:' sufficient?
>> I think pad:sink:0 is rather cumbersome.
>>
>>> +
>>> +Default folders are created under the entity directory for each pad of the entity.
>>> +
>>> +Creating links
>>> +--------------
>>> +
>>> +To create a link between two entities, you should create a directory for the link
>>> +under the source pad of the link and then set it to be a symbolic link to the sink pad:
>>>  
>>> -Topology
>>> ---------
>>> +Example:
>>> +::
>>> +
>>> +	$ mkdir "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap"
>>
>> I'd call the directory 'to-cap-sen': it's clearer that this points to the
>> cap-sen entity. It makes the example easier to understand.
>>
>>> +	$ ln -s "/configfs/vimc/mdev/vimc-capture:cap-sen/pad:sink:0" "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap"
>>> +	$ tree /configfs
>>> +	/configfs
>>> +	`-- vimc
>>> +	    `-- mdev
>>> +	        |-- hotplug
>>> +	        |-- vimc-capture:cap-sen
>>> +	        |   `-- pad:sink:0
>>> +	        `-- vimc-sensor:sen
>>> +	            `-- pad:source:0
>>> +	                `-- to-cap
>>> +	                    |-- enabled
>>> +	                    |-- immutable
>>> +	                    `-- pad:sink:0 -> ../../../../../vimc/mdev/vimc-capture:cap-sen/pad:sink:0
>>> +
>>> +The `enabled` and `immutable` are two boolean attributes of the link corresponding to the link flags
>>> +
>>> +
>>> +Flag values are described in :ref:`Documentation/media/uapi/mediactl/media-types.rst <media-link-flag>`
>>> +( seek for ``MEDIA_LNK_FL_*``)
>>> +
>>> +1 - Enabled
>>> +	Indicates that 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.
>>> +
>>> +Change an attribute of the link by writing "on" or "1" to set it on , and "off" or "0" to set it off
>>> +
>>> +Example:
>>> +::
>>> +
>>> +	$ echo "on" > /configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap/immutable
>>> +
>>> +Activating/Deactivating device
>>> +------------------------------
>>> +
>>> +To activate the device, write one of "plugged", "plug" or "1" to the file
>>> +``/configfs/vimc/<mdev-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 the file
>>> +``/configfs/vimc/<ndev-name>/hotplug``
>>> +
>>> +Example:
>>> +::
>>> +
>>> +	$ echo unplugged > /configfs/vimc/mdev/hotplug
>>> +
>>> +Topology Configuration - Full Example
>>> +-------------------------------------
>>> +
>>> +Here is a full example of a simple topology configuration:
>>> +
>>> +.. code-block:: bash
>>> +
>>> +    # Creating the entities
>>> +    mkdir "/configfs/vimc/mdev"
>>> +    mkdir "/configfs/vimc/mdev/vimc-sensor:sen"
>>> +    mkdir "/configfs/vimc/mdev/vimc-debayer:deb"
>>> +    mkdir "/configfs/vimc/mdev/vimc-scaler:sca"
>>> +    mkdir "/configfs/vimc/mdev/vimc-capture:cap-sca" #/dev/video2
>>> +    mkdir "/configfs/vimc/mdev/vimc-capture:cap-sen" #/dev/video1
>>> +    mkdir "/configfs/vimc/mdev/vimc-capture:cap-deb" #/dev/video0
>>> +
>>> +    # Creating the links
>>> +    #sen -> deb
>>> +    mkdir "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-deb"
>>> +    ln -s "/configfs/vimc/mdev/vimc-debayer:deb/pad:sink:0" "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-deb"
>>> +    echo on > "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-deb/immutable"
>>
>> If you set immutable to on, then it should automatically set enabled to on as well,
>> so no need for the next line.
> 
> Andrzej Pietrasiewicz suggested not to allow setting the 'immutable' to on if 'enabled' is off
> and not to allow setting 'enabled' to off if 'immutable' it on.
> He says this is better than an interface where a change in one file cause a change in another file.

I wonder if this shouldn't be changed to just a single file called 'type'. And it
can be set to 'immutable', 'enabled'/'on' and 'disabled'/'off'. It defaults to
'off'.

That way you don't have this dependency between two properties.

It feels cleaner that way.

> 
>>
>>> +    echo on > "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-deb/enabled"
>>> +
>>> +    #deb -> sca
>>> +    mkdir "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-sca"
>>> +    ln -s "/configfs/vimc/mdev/vimc-scaler:sca/pad:sink:0" "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-sca"
>>> +    echo on > "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-sca/immutable"
>>> +    echo on > "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-sca/enabled"
>>> +
>>> +    #sca -> cap-sca
>>> +    mkdir "/configfs/vimc/mdev/vimc-scaler:sca/pad:source:1/to-cap"
>>
>> Same in this example: use to-cap-sca
>>
>>> +    ln -s "/configfs/vimc/mdev/vimc-capture:cap-sca/pad:sink:0" "/configfs/vimc/mdev/vimc-scaler:sca/pad:source:1/to-cap"
>>> +    echo on > "/configfs/vimc/mdev/vimc-scaler:sca/pad:source:1/to-cap/immutable"
>>> +    echo on > "/configfs/vimc/mdev/vimc-scaler:sca/pad:source:1/to-cap/enabled"
>>> +
>>> +    #sen -> cap-sen
>>> +    mkdir "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap"
>>> +    ln -s "/configfs/vimc/mdev/vimc-capture:cap-sen/pad:sink:0" "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap"
>>> +    echo on > "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap/immutable"
>>> +    echo on > "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap/enabled"
>>> +
>>> +    #deb -> cap-deb
>>> +    mkdir "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-cap"
>>> +    ln -s "/configfs/vimc/mdev/vimc-capture:cap-deb/pad:sink:0" "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-cap"
>>> +    echo on > "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-cap/immutable"
>>> +    echo on > "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-cap/enabled"
>>>  
>>> -The topology is hardcoded, although you could modify it in vimc-core and
>>> -recompile the driver to achieve your own topology. This is the default topology:
>>>  
>>>  .. _vimc_topology_graph:
>>>  
>>>  .. kernel-figure:: vimc.dot
>>> -    :alt:   Diagram of the default media pipeline topology
>>> +    :alt:   Diagram of the configured simple topology in the example
>>>      :align: center
>>>  
>>> -    Media pipeline graph on vimc
>>> +    Simple Media pipeline graph on vimc configured through configfs
>>>  
>>> -Configuring the topology
>>> -~~~~~~~~~~~~~~~~~~~~~~~~
>>> +Configuring the pipeline formats
>>> +================================
>>>  
>>> -Each subdevice will come with its default configuration (pixelformat, height,
>>> -width, ...). One needs to configure the topology in order to match the
>>> +Each subdevice has a default format configuration (pixelformat, height,
>>> +width, ...). You should configure the formats in order to match the
>>>  configuration on each linked subdevice to stream frames through the pipeline.
>>> -If the configuration doesn't match, the stream will fail. The ``v4l-utils``
>>> +If the configuration doesn't match, streaming will fail. The ``v4l-utils``
>>>  package is a bundle of user-space applications, that comes with ``media-ctl`` and
>>> -``v4l2-ctl`` that can be used to configure the vimc configuration. This sequence
>>> -of commands fits for the default topology:
>>> +``v4l2-ctl`` that can be used to configure the formats of the entities. This sequence
>>> +of commands fits the simple topology created in the full example of topology configuration:
>>>  
>>>  .. code-block:: bash
>>>  
>>> -        media-ctl -d platform:vimc -V '"Sensor A":0[fmt:SBGGR8_1X8/640x480]'
>>> -        media-ctl -d platform:vimc -V '"Debayer A":0[fmt:SBGGR8_1X8/640x480]'
>>> -        media-ctl -d platform:vimc -V '"Sensor B":0[fmt:SBGGR8_1X8/640x480]'
>>> -        media-ctl -d platform:vimc -V '"Debayer B":0[fmt:SBGGR8_1X8/640x480]'
>>> -        v4l2-ctl -z platform:vimc -d "RGB/YUV Capture" -v width=1920,height=1440
>>> -        v4l2-ctl -z platform:vimc -d "Raw Capture 0" -v pixelformat=BA81
>>> -        v4l2-ctl -z platform:vimc -d "Raw Capture 1" -v pixelformat=BA81
>>> +	media-ctl -d platform:vimc-000 -V '"sen":0[fmt:SBGGR8_1X8/640x480]'
>>> +	media-ctl -d platform:vimc-000 -V '"deb":0[fmt:SBGGR8_1X8/640x480]'
>>> +	media-ctl -d platform:vimc-000 -V '"deb":1[fmt:RGB888_1X24/640x480]'
>>> +	media-ctl -d platform:vimc-000 -V '"sca":0[fmt:RGB888_1X24/640x480]'
>>> +	media-ctl -d platform:vimc-000 -V '"sca":1[fmt:RGB888_1X24/640x480]'
>>> +	v4l2-ctl -z platform:vimc-000 -d "cap-sen" -v pixelformat=BA81
>>> +	v4l2-ctl -z platform:vimc-000 -d "cap-deb" -v pixelformat=RGB3
>>> +	# The default scaling value of the scaler is 3, so need to set its capture accordingly
>>> +	v4l2-ctl -z platform:vimc -d "cap-sca" -v pixelformat=RGB3,width=1920,height=1440
>>>  
>>>  Subdevices
>>>  ----------
>>> @@ -61,8 +241,8 @@ vimc-debayer:
>>>  	* 1 Pad source
>>>  
>>>  vimc-scaler:
>>> -	Scale up the image by a factor of 3. E.g.: a 640x480 image becomes a
>>> -        1920x1440 image. (this value can be configured, see at
>>> +	Scales up the image by a factor of 3. E.g.: a 640x480 image becomes a
>>> +        1920x1440 image. (this value can be configured, see
>>
>> Note that this might change with this patch:
>>
>> https://patchwork.kernel.org/patch/11146175/
>>
>> Just so you are aware of this.
>>
>>>          `Module options`_).
>>>  	Exposes:
>>>  
>>> @@ -77,12 +257,10 @@ vimc-capture:
>>>  	* 1 Pad source
>>>  
>>>  
>>> -        Module options
>>> ----------------
>>> -
>>> -Vimc has a few module parameters to configure the driver.
>>> +Module options
>>> +==============
>>>  
>>> -        param=value
>>> +Vimc has 2 module parameters to configure the driver.
>>>  
>>>  * ``sca_mult=<unsigned int>``
>>>  
>>> @@ -98,10 +276,10 @@ Vimc has a few module parameters to configure the driver.
>>>          otherwise the next odd number is considered (the default value is 3).
>>>  
>>>  Source code documentation
>>> --------------------------
>>> +=========================
>>>  
>>>  vimc-streamer
>>> -~~~~~~~~~~~~~
>>> +-------------
>>>  
>>>  .. kernel-doc:: drivers/media/platform/vimc/vimc-streamer.h
>>>     :internal:
>>>
>>
>> Regards,
>>
>> 	Hans
> 
> Thanks,
> 
> Dafna
> 

Regards,

	Hans

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/5] media: vimc: upon streaming, check that the pipeline starts with a source entity
  2019-09-19 20:32 ` [PATCH 1/5] media: vimc: upon streaming, check that the pipeline starts with a source entity Dafna Hirschfeld
@ 2019-09-26 14:32   ` Shuah Khan
  2019-10-08 13:34     ` Dafna Hirschfeld
  0 siblings, 1 reply; 20+ messages in thread
From: Shuah Khan @ 2019-09-26 14:32 UTC (permalink / raw)
  To: Dafna Hirschfeld, linux-media
  Cc: laurent.pinchart, helen.koike, ezequiel, andre.almeida, hverkuil,
	kernel, dafna3, skh >> Shuah Khan

On 9/19/19 2:32 PM, Dafna Hirschfeld wrote:
> Userspace can disable links and create pipelines that
> do not start with a source entity. Trying to stream
> from such a pipeline should fail with -EPIPE
> currently this is not handled and cause kernel crash.
> 

Minor: Can you make these 75 long. Makes it easier to read.

> Reproducing the crash:
> media-ctl -d0 -l "5:1->21:0[0]" -v
> v4l2-ctl -z platform:vimc -d "RGB/YUV Capture" -v width=1920,height=1440
> v4l2-ctl --stream-mmap --stream-count=100 -d /dev/video2

I really would like to see the panic message so it can checked during
testing.

If you are fixing a panic, please include the panic info. in the future.

> 
> Signed-off-by: Dafna Hirschfeld <dafna.hirschfeld@collabora.com>
> ---
>   drivers/media/platform/vimc/vimc-streamer.c | 39 +++++++++++++++------
>   1 file changed, 28 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/media/platform/vimc/vimc-streamer.c b/drivers/media/platform/vimc/vimc-streamer.c
> index faa2879c25df..d0a9f8a0f26a 100644
> --- a/drivers/media/platform/vimc/vimc-streamer.c
> +++ b/drivers/media/platform/vimc/vimc-streamer.c
> @@ -12,6 +12,19 @@
>   
>   #include "vimc-streamer.h"
>   
> +/**
> + * Check if the entity has only source pads
> + */
> +static bool vimc_is_source(struct media_entity *ent)
> +{
> +	int i;
> +
> +	for (i = 0; i < ent->num_pads; i++)
> +		if (ent->pads[i].flags & MEDIA_PAD_FL_SINK)
> +			return false;
> +	return true;
> +}
> +

Why not make this a common routine and add it to vimc-common.c?

>   /**
>    * vimc_get_source_entity - get the entity connected with the first sink pad
>    *
> @@ -82,14 +95,12 @@ static int vimc_streamer_pipeline_init(struct vimc_stream *stream,
>   	struct media_entity *entity;
>   	struct video_device *vdev;
>   	struct v4l2_subdev *sd;
> -	int ret = 0;
> +	int ret = -EINVAL;
>   
>   	stream->pipe_size = 0;
>   	while (stream->pipe_size < VIMC_STREAMER_PIPELINE_MAX_SIZE) {
> -		if (!ved) {
> -			vimc_streamer_pipeline_terminate(stream);
> -			return -EINVAL;
> -		}
> +		if (!ved)
> +			break;
>   		stream->ved_pipeline[stream->pipe_size++] = ved;
>   
>   		if (is_media_entity_v4l2_subdev(ved->ent)) {
> @@ -98,15 +109,22 @@ static int vimc_streamer_pipeline_init(struct vimc_stream *stream,
>   			if (ret && ret != -ENOIOCTLCMD) {
>   				pr_err("subdev_call error %s\n",
>   				       ved->ent->name);

While you are at it, can you make this a dev_err() instead? I think we
have access to dev here.

> -				vimc_streamer_pipeline_terminate(stream);
> -				return ret;
> +				break;
>   			}
>   		}
>   
>   		entity = vimc_get_source_entity(ved->ent);
> -		/* Check if the end of the pipeline was reached*/
> -		if (!entity)
> +		/* Check if the end of the pipeline was reached */
> +		if (!entity) {
> +			/* the first entity of the pipe should be source only */
> +			if (!vimc_is_source(ved->ent)) {
> +				pr_err("first entity in the pipe '%s' is not a source\n",
> +				       ved->ent->name);

Same commnet about dev_err() here.

> +				ret = -EPIPE;
> +				break;
> +			}
>   			return 0;
> +		}
>   
>   		/* Get the next device in the pipeline */
>   		if (is_media_entity_v4l2_subdev(entity)) {
> @@ -119,9 +137,8 @@ static int vimc_streamer_pipeline_init(struct vimc_stream *stream,
>   			ved = video_get_drvdata(vdev);
>   		}
>   	}
> -
>   	vimc_streamer_pipeline_terminate(stream);
> -	return -EINVAL;
> +	return ret;
>   }
>   
>   /**
> 

thanks,
-- Shuah

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 2/5] docs: media: vimc: Documenting vimc topology configuration using configfs
  2019-09-19 20:32 ` [PATCH 2/5] docs: media: vimc: Documenting vimc topology configuration using configfs Dafna Hirschfeld
  2019-09-20 13:39   ` Hans Verkuil
@ 2019-09-26 18:59   ` Shuah Khan
  1 sibling, 0 replies; 20+ messages in thread
From: Shuah Khan @ 2019-09-26 18:59 UTC (permalink / raw)
  To: Dafna Hirschfeld, linux-media
  Cc: laurent.pinchart, helen.koike, ezequiel, andre.almeida, hverkuil,
	kernel, dafna3, Shuah Khan

On 9/19/19 2:32 PM, Dafna Hirschfeld wrote:
> Add explanation of how to use configfs in order to create a
> vimc device with a given topology.
> 

Can you add more detail on the problem configfs solves and what
value it adds.

> Signed-off-by: Dafna Hirschfeld <dafna.hirschfeld@collabora.com>
> ---
>   Documentation/media/v4l-drivers/vimc.dot |  28 ++-
>   Documentation/media/v4l-drivers/vimc.rst | 240 ++++++++++++++++++++---
>   2 files changed, 220 insertions(+), 48 deletions(-)
> 
> diff --git a/Documentation/media/v4l-drivers/vimc.dot b/Documentation/media/v4l-drivers/vimc.dot
> index 57863a13fa39..e3b41ac2bc46 100644
> --- a/Documentation/media/v4l-drivers/vimc.dot
> +++ b/Documentation/media/v4l-drivers/vimc.dot
> @@ -2,21 +2,15 @@
>   
>   digraph board {
>   	rankdir=TB
> -	n00000001 [label="{{} | Sensor A\n/dev/v4l-subdev0 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green]
> -	n00000001:port0 -> n00000005:port0 [style=bold]
> -	n00000001:port0 -> n0000000b [style=bold]
> -	n00000003 [label="{{} | Sensor B\n/dev/v4l-subdev1 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green]
> -	n00000003:port0 -> n00000008:port0 [style=bold]
> -	n00000003:port0 -> n0000000f [style=bold]
> -	n00000005 [label="{{<port0> 0} | Debayer A\n/dev/v4l-subdev2 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
> -	n00000005:port1 -> n00000017:port0
> -	n00000008 [label="{{<port0> 0} | Debayer B\n/dev/v4l-subdev3 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
> -	n00000008:port1 -> n00000017:port0 [style=dashed]
> -	n0000000b [label="Raw Capture 0\n/dev/video0", shape=box, style=filled, fillcolor=yellow]
> -	n0000000f [label="Raw Capture 1\n/dev/video1", shape=box, style=filled, fillcolor=yellow]
> -	n00000013 [label="RGB/YUV Input\n/dev/video2", shape=box, style=filled, fillcolor=yellow]
> -	n00000013 -> n00000017:port0 [style=dashed]
> -	n00000017 [label="{{<port0> 0} | Scaler\n/dev/v4l-subdev4 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
> -	n00000017:port1 -> n0000001a [style=bold]
> -	n0000001a [label="RGB/YUV Capture\n/dev/video3", shape=box, style=filled, fillcolor=yellow]
> +	n00000001 [label="cap-deb\n/dev/video0", shape=box, style=filled, fillcolor=yellow]
> +	n00000005 [label="cap-sen\n/dev/video1", shape=box, style=filled, fillcolor=yellow]
> +	n00000009 [label="cap-sca\n/dev/video2", shape=box, style=filled, fillcolor=yellow]
> +	n0000000d [label="{{<port0> 0} | sca\n/dev/v4l-subdev0 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
> +	n0000000d:port1 -> n00000009 [style=bold]
> +	n00000010 [label="{{<port0> 0} | deb\n/dev/v4l-subdev1 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
> +	n00000010:port1 -> n00000001 [style=bold]
> +	n00000010:port1 -> n0000000d:port0 [style=bold]
> +	n00000013 [label="{{} | sen\n/dev/v4l-subdev2 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green]
> +	n00000013:port0 -> n00000005 [style=bold]
> +	n00000013:port0 -> n00000010:port0 [style=bold]
>   }
> diff --git a/Documentation/media/v4l-drivers/vimc.rst b/Documentation/media/v4l-drivers/vimc.rst
> index a582af0509ee..e5636883545f 100644
> --- a/Documentation/media/v4l-drivers/vimc.rst
> +++ b/Documentation/media/v4l-drivers/vimc.rst
> @@ -1,45 +1,225 @@
>   .. SPDX-License-Identifier: GPL-2.0
>   
> +==========================================
>   The Virtual Media Controller Driver (vimc)
>   ==========================================
>   
> -The vimc driver emulates complex video hardware using the V4L2 API and the Media
> -API. It has a capture device and three subdevices: sensor, debayer and scaler.
> +The vimc driver emulates complex video hardware topologies using the V4L2 API
> +and the Media API. It has a capture device and three subdevices:

Add blank line in between - helps with readability

> +sensor, debayer and scaler. It exposes media devices through /dev/mediaX nodes,
> +video capture devices through /dev/videoX and sub-devices through /dev/v4l-subdevX.
> +
> +
> +To configure a media device of a given topology, a ConfigFS API is provided.

ConfigFS API enables the ability dynamically configure a media device
and its topology. This will help customize topology for specific testing
needs (?)

Assuming that is the goal for this work.

> +
> +Configuring a topology through ConfigFS (Experimental)
> +======================================================
> +
> +.. note:: This API is under development and might change in the future. > +
> +Mount configfs:
> +::
> +
> +	$ mkdir /configfs
> +	$ mount -t configfs none /configfs
> +
> +When loading the module, you will see a folder named vimc
> +::
> +
> +	$ tree /configfs/
> +	/configfs/
> +	`-- vimc
> +
> +Creating a media device
> +-----------------------
> +
> +To create a media device create a new folder under /configfs/vimc/
Change this to:

How to create a media device or flip the sentence around:

Create a folder under /configfs/vimc/ to create media device.

> +
> +Example:
> +::
> +
> +	$ mkdir /configfs/vimc/mdev
> +	$ tree /configfs/vimc/mdev
> +	/configfs/
> +	`-- vimc
> +	    `-- mdev
> +	        `-- hotplug
> +
> +Creating entities
> +-----------------
> +
> +To create an entity in the media device's topology, create a folder under

Same comment as above.

> +``/configfs/vimc/<mdev-name>/`` with the following format:
> +
> +	<entity-type>:<entity-name>
> +
> +Where <entity-type> is one of the following:
> +
> +- vimc-sensor
> +- vimc-scaler
> +- vimc-debayer
> +- vimc-capture
> +
> +Example:
> +::
> +
> +	$ mkdir /configfs/vimc/mdev/vimc-sensor:sen
> +	$ mkdir /configfs/vimc/mdev/vimc-capture:cap-sen
> +	$ tree /configfs/
> +	/configfs/
> +	`-- vimc
> +	    `-- mdev
> +		|-- hotplug
> +		|-- vimc-capture:cap-sen
> +		|   `-- pad:sink:0
> +		`-- vimc-sensor:sen
> +                    `-- pad:source:0
> +
> +Default folders are created under the entity directory for each pad of the entity.
> +
> +Creating links
> +--------------
> +
> +To create a link between two entities, you should create a directory for the link
> +under the source pad of the link and then set it to be a symbolic link to the sink pad:

Same comment as above.

>   
> -Topology
> ---------
> +Example:
> +::
> +
> +	$ mkdir "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap"
> +	$ ln -s "/configfs/vimc/mdev/vimc-capture:cap-sen/pad:sink:0" "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap"
> +	$ tree /configfs
> +	/configfs
> +	`-- vimc
> +	    `-- mdev
> +	        |-- hotplug
> +	        |-- vimc-capture:cap-sen
> +	        |   `-- pad:sink:0
> +	        `-- vimc-sensor:sen
> +	            `-- pad:source:0
> +	                `-- to-cap
> +	                    |-- enabled
> +	                    |-- immutable
> +	                    `-- pad:sink:0 -> ../../../../../vimc/mdev/vimc-capture:cap-sen/pad:sink:0
> +
> +The `enabled` and `immutable` are two boolean attributes of the link corresponding to the link flags
> +
> +
> +Flag values are described in :ref:`Documentation/media/uapi/mediactl/media-types.rst <media-link-flag>`
> +( seek for ``MEDIA_LNK_FL_*``)
> +
> +1 - Enabled
> +	Indicates that 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.
> +
> +Change an attribute of the link by writing "on" or "1" to set it on , and "off" or "0" to set it off

You can change

> +
> +Example:
> +::
> +
> +	$ echo "on" > /configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap/immutable
> +
> +Activating/Deactivating device
> +------------------------------
> +
> +To activate the device, write one of "plugged", "plug" or "1" to the file

Same comment as above.

> +``/configfs/vimc/<mdev-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 the file
> +``/configfs/vimc/<ndev-name>/hotplug``

Same comment as above. In general don't start sentence with "To"

> +
> +Example:
> +::
> +
> +	$ echo unplugged > /configfs/vimc/mdev/hotplug
> +
> +Topology Configuration - Full Example
> +-------------------------------------
> +
> +Here is a full example of a simple topology configuration:
> +
> +.. code-block:: bash
> +
> +    # Creating the entities
> +    mkdir "/configfs/vimc/mdev"
> +    mkdir "/configfs/vimc/mdev/vimc-sensor:sen"
> +    mkdir "/configfs/vimc/mdev/vimc-debayer:deb"
> +    mkdir "/configfs/vimc/mdev/vimc-scaler:sca"
> +    mkdir "/configfs/vimc/mdev/vimc-capture:cap-sca" #/dev/video2
> +    mkdir "/configfs/vimc/mdev/vimc-capture:cap-sen" #/dev/video1
> +    mkdir "/configfs/vimc/mdev/vimc-capture:cap-deb" #/dev/video0
> +
> +    # Creating the links
> +    #sen -> deb
> +    mkdir "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-deb"
> +    ln -s "/configfs/vimc/mdev/vimc-debayer:deb/pad:sink:0" "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-deb"
> +    echo on > "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-deb/immutable"
> +    echo on > "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-deb/enabled"
> +
> +    #deb -> sca
> +    mkdir "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-sca"
> +    ln -s "/configfs/vimc/mdev/vimc-scaler:sca/pad:sink:0" "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-sca"
> +    echo on > "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-sca/immutable"
> +    echo on > "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-sca/enabled"
> +
> +    #sca -> cap-sca
> +    mkdir "/configfs/vimc/mdev/vimc-scaler:sca/pad:source:1/to-cap"
> +    ln -s "/configfs/vimc/mdev/vimc-capture:cap-sca/pad:sink:0" "/configfs/vimc/mdev/vimc-scaler:sca/pad:source:1/to-cap"
> +    echo on > "/configfs/vimc/mdev/vimc-scaler:sca/pad:source:1/to-cap/immutable"
> +    echo on > "/configfs/vimc/mdev/vimc-scaler:sca/pad:source:1/to-cap/enabled"
> +
> +    #sen -> cap-sen
> +    mkdir "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap"
> +    ln -s "/configfs/vimc/mdev/vimc-capture:cap-sen/pad:sink:0" "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap"
> +    echo on > "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap/immutable"
> +    echo on > "/configfs/vimc/mdev/vimc-sensor:sen/pad:source:0/to-cap/enabled"
> +
> +    #deb -> cap-deb
> +    mkdir "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-cap"
> +    ln -s "/configfs/vimc/mdev/vimc-capture:cap-deb/pad:sink:0" "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-cap"
> +    echo on > "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-cap/immutable"
> +    echo on > "/configfs/vimc/mdev/vimc-debayer:deb/pad:source:1/to-cap/enabled"
>   
> -The topology is hardcoded, although you could modify it in vimc-core and
> -recompile the driver to achieve your own topology. This is the default topology:

Does this mean, there is no default topology. I think default topology
should still be an option even if it is a module option.

>   
>   .. _vimc_topology_graph:
>   
>   .. kernel-figure:: vimc.dot
> -    :alt:   Diagram of the default media pipeline topology
> +    :alt:   Diagram of the configured simple topology in the example
>       :align: center
>   
> -    Media pipeline graph on vimc
> +    Simple Media pipeline graph on vimc configured through configfs
>   
> -Configuring the topology
> -~~~~~~~~~~~~~~~~~~~~~~~~
> +Configuring the pipeline formats
> +================================
>   
> -Each subdevice will come with its default configuration (pixelformat, height,
> -width, ...). One needs to configure the topology in order to match the
> +Each subdevice has a default format configuration (pixelformat, height,
> +width, ...). You should configure the formats in order to match the
>   configuration on each linked subdevice to stream frames through the pipeline.
> -If the configuration doesn't match, the stream will fail. The ``v4l-utils``
> +If the configuration doesn't match, streaming will fail. The ``v4l-utils``
>   package is a bundle of user-space applications, that comes with ``media-ctl`` and

replace "that comes with" with "which includes"

> -``v4l2-ctl`` that can be used to configure the vimc configuration. This sequence
> -of commands fits for the default topology:
> +``v4l2-ctl`` that can be used to configure the formats of the entities. This sequence
> +of commands fits the simple topology created in the full example of topology configuration:
>   
>   .. code-block:: bash
>   
> -        media-ctl -d platform:vimc -V '"Sensor A":0[fmt:SBGGR8_1X8/640x480]'
> -        media-ctl -d platform:vimc -V '"Debayer A":0[fmt:SBGGR8_1X8/640x480]'
> -        media-ctl -d platform:vimc -V '"Sensor B":0[fmt:SBGGR8_1X8/640x480]'
> -        media-ctl -d platform:vimc -V '"Debayer B":0[fmt:SBGGR8_1X8/640x480]'
> -        v4l2-ctl -z platform:vimc -d "RGB/YUV Capture" -v width=1920,height=1440
> -        v4l2-ctl -z platform:vimc -d "Raw Capture 0" -v pixelformat=BA81
> -        v4l2-ctl -z platform:vimc -d "Raw Capture 1" -v pixelformat=BA81
> +	media-ctl -d platform:vimc-000 -V '"sen":0[fmt:SBGGR8_1X8/640x480]'
> +	media-ctl -d platform:vimc-000 -V '"deb":0[fmt:SBGGR8_1X8/640x480]'
> +	media-ctl -d platform:vimc-000 -V '"deb":1[fmt:RGB888_1X24/640x480]'
> +	media-ctl -d platform:vimc-000 -V '"sca":0[fmt:RGB888_1X24/640x480]'
> +	media-ctl -d platform:vimc-000 -V '"sca":1[fmt:RGB888_1X24/640x480]'
> +	v4l2-ctl -z platform:vimc-000 -d "cap-sen" -v pixelformat=BA81
> +	v4l2-ctl -z platform:vimc-000 -d "cap-deb" -v pixelformat=RGB3
> +	# The default scaling value of the scaler is 3, so need to set its capture accordingly
> +	v4l2-ctl -z platform:vimc -d "cap-sca" -v pixelformat=RGB3,width=1920,height=1440
>   
>   Subdevices
>   ----------
> @@ -61,8 +241,8 @@ vimc-debayer:
>   	* 1 Pad source
>   
>   vimc-scaler:
> -	Scale up the image by a factor of 3. E.g.: a 640x480 image becomes a
> -        1920x1440 image. (this value can be configured, see at
> +	Scales up the image by a factor of 3. E.g.: a 640x480 image becomes a
> +        1920x1440 image. (this value can be configured, see
>           `Module options`_).
>   	Exposes:
>   
> @@ -77,12 +257,10 @@ vimc-capture:
>   	* 1 Pad source
>   
>   
> -        Module options
> ----------------
> -
> -Vimc has a few module parameters to configure the driver.
> +Module options
> +==============
>   
> -        param=value
> +Vimc has 2 module parameters to configure the driver.
>   
>   * ``sca_mult=<unsigned int>``
>   
> @@ -98,10 +276,10 @@ Vimc has a few module parameters to configure the driver.
>           otherwise the next odd number is considered (the default value is 3).
>   

Keep this close to the top of the file and then add the configfs stuff.

>   Source code documentation
> --------------------------
> +=========================
>   
>   vimc-streamer
> -~~~~~~~~~~~~~
> +-------------
>   
>   .. kernel-doc:: drivers/media/platform/vimc/vimc-streamer.h
>      :internal:
> 

thanks,
-- Shuah

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 3/5] media: vimc: Add the implementation for the configfs api
  2019-09-19 20:32 ` [PATCH 3/5] media: vimc: Add the implementation for the configfs api Dafna Hirschfeld
@ 2019-09-26 19:25   ` Shuah Khan
  2019-11-04 15:28     ` Dafna Hirschfeld
  2019-09-27  7:55   ` Andrzej Pietrasiewicz
  1 sibling, 1 reply; 20+ messages in thread
From: Shuah Khan @ 2019-09-26 19:25 UTC (permalink / raw)
  To: Dafna Hirschfeld, linux-media
  Cc: laurent.pinchart, helen.koike, ezequiel, andre.almeida, hverkuil,
	kernel, dafna3, Shuah Khan

On 9/19/19 2:32 PM, Dafna Hirschfeld wrote:
> Add the code that implements the usage of configfs in order
> to create and configure a device topology from userspace.
> The code is only added in this patch but is not used.
> It will be used in next patch in the series.
> 

This somehow doesn't read right. It isn't clear what you are adding.
Can you pleas rephrase it.

Also, I would like to see the overview of the design. I see that you
are adding _init routines e.g vimc_cap_init(). It will be easier to
review if you described the details of the overall design a bit.


> Signed-off-by: Helen Koike <helen.koike@collabora.com>
> [refactored for upstream]

What does this mean?

> Signed-off-by: Dafna Hirschfeld <dafna.hirschfeld@collabora.com>
> ---
>   drivers/media/platform/vimc/Kconfig         |   9 +-
>   drivers/media/platform/vimc/Makefile        |   2 +-
>   drivers/media/platform/vimc/vimc-capture.c  |  21 +
>   drivers/media/platform/vimc/vimc-common.h   |  46 ++
>   drivers/media/platform/vimc/vimc-configfs.c | 656 ++++++++++++++++++++
>   drivers/media/platform/vimc/vimc-configfs.h |  41 ++
>   drivers/media/platform/vimc/vimc-core.c     |  21 +-
>   drivers/media/platform/vimc/vimc-debayer.c  |  22 +
>   drivers/media/platform/vimc/vimc-scaler.c   |  26 +-
>   drivers/media/platform/vimc/vimc-sensor.c   |  21 +
>   10 files changed, 856 insertions(+), 9 deletions(-)
>   create mode 100644 drivers/media/platform/vimc/vimc-configfs.c
>   create mode 100644 drivers/media/platform/vimc/vimc-configfs.h
> 
> diff --git a/drivers/media/platform/vimc/Kconfig b/drivers/media/platform/vimc/Kconfig
> index bd221d3e1a4a..6d7836d58ef4 100644
> --- a/drivers/media/platform/vimc/Kconfig
> +++ b/drivers/media/platform/vimc/Kconfig
> @@ -1,15 +1,14 @@
>   # SPDX-License-Identifier: GPL-2.0-only
>   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
>   	help
> -	  Skeleton driver for Virtual Media Controller
> +	  Virtual Media Controller Driver
>   
> -	  This driver can be compared to the vivid driver for emulating
> +	  This driver emulates

Why a short line, combine with the next?

>   	  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 the configfs API.
>   
>   	  When in doubt, say N.
> diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile
> index a53b2b532e9f..eb03d487f308 100644
> --- a/drivers/media/platform/vimc/Makefile
> +++ b/drivers/media/platform/vimc/Makefile
> @@ -1,6 +1,6 @@
>   # SPDX-License-Identifier: GPL-2.0
>   vimc-y := vimc-core.o vimc-common.o vimc-streamer.o vimc-capture.o \
> -		vimc-debayer.o vimc-scaler.o vimc-sensor.o
> +		vimc-debayer.o vimc-scaler.o vimc-sensor.o  vimc-configfs.o
>   
>   obj-$(CONFIG_VIDEO_VIMC) += vimc.o
>   
> diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c
> index 602f80323031..5cc419e76ce7 100644
> --- a/drivers/media/platform/vimc/vimc-capture.c
> +++ b/drivers/media/platform/vimc/vimc-capture.c
> @@ -9,6 +9,7 @@
>   #include <media/videobuf2-core.h>
>   #include <media/videobuf2-vmalloc.h>
>   
> +#include "vimc-configfs.h"
>   #include "vimc-common.h"
>   #include "vimc-streamer.h"
>   
> @@ -488,3 +489,23 @@ struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc,
>   
>   	return NULL;
>   }
> +
> +static void vimc_cap_create_cfs_pads(struct config_group *ent_group)
> +{
> +	vimc_cfs_add_sink_pad_api(ent_group, 0, VIMC_CFS_SINK_PAD_NUM(0));
> +}
> +
> +struct vimc_cfs_drv vimc_cap_cfs_drv = {
> +	.name = VIMC_CAP_NAME,
> +	.create_pads = vimc_cap_create_cfs_pads,
> +};
> +
> +__exit void vimc_cap_exit(void)
> +{
> +	vimc_cfs_drv_unregister(&vimc_cap_cfs_drv);
> +}
> +
> +__init void vimc_cap_init(void)
> +{
> +	vimc_cfs_drv_register(&vimc_cap_cfs_drv);
> +}
> diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h
> index 698db7c07645..e0e3b3ab7b19 100644
> --- a/drivers/media/platform/vimc/vimc-common.h
> +++ b/drivers/media/platform/vimc/vimc-common.h
> @@ -14,6 +14,7 @@
>   #include <media/v4l2-device.h>
>   
>   #define VIMC_PDEV_NAME "vimc"
> +#define VIMC_MAX_NAME_LEN V4L2_SUBDEV_NAME_SIZE
>   
>   /* VIMC-specific controls */
>   #define VIMC_CID_VIMC_BASE		(0x00f00000 | 0xf000)
> @@ -31,6 +32,11 @@
>   #define VIMC_IS_SRC(pad)	(pad)
>   #define VIMC_IS_SINK(pad)	(!(pad))
>   
> +#define VIMC_DEB_NAME "vimc-debayer"
> +#define VIMC_SEN_NAME "vimc-sensor"
> +#define VIMC_SCA_NAME "vimc-scaler"
> +#define VIMC_CAP_NAME "vimc-capture"
> +
>   /**
>    * struct vimc_colorimetry_clamp - Adjust colorimetry parameters
>    *
> @@ -72,6 +78,18 @@ struct vimc_platform_data {
>   	char entity_name[32];
>   };
>   
> +/**
> + * struct vimc_platform_data_core - platform data to the core
> + *
> + * @ents: list of vimc_entity_data objects allocated by the configfs
> + * @links: list of vimc_links objects allocated by the configfs
> + *
> + */
> +struct vimc_platform_data_core {
> +	struct list_head *ents;
> +	struct list_head *links;
> +};
> +
>   /**
>    * struct vimc_pix_map - maps media bus code with v4l2 pixel format
>    *
> @@ -92,6 +110,8 @@ struct vimc_pix_map {
>   /**
>    * struct vimc_ent_device - core struct that represents a node in the topology
>    *
> + * @name:		the name of the entity
> + * @drv_name:		the name of the driver of the entity

Please align it with rest. Extra tab perhaps.

>    * @ent:		the pointer to struct media_entity for the node
>    * @pads:		the list of pads of the node
>    * @process_frame:	callback send a frame to that node
> @@ -108,12 +128,30 @@ struct vimc_pix_map {
>    * media_entity
>    */
>   struct vimc_ent_device {
> +	char name[VIMC_MAX_NAME_LEN];
> +	char drv_name[VIMC_MAX_NAME_LEN];
>   	struct media_entity *ent;
>   	struct media_pad *pads;
>   	void * (*process_frame)(struct vimc_ent_device *ved,
>   				const void *frame);
>   	void (*vdev_get_format)(struct vimc_ent_device *ved,
>   			      struct v4l2_pix_format *fmt);
> +	struct list_head list;
> +};
> +
> +struct vimc_entity_data {
> +	char name[VIMC_MAX_NAME_LEN];
> +	char drv_name[VIMC_MAX_NAME_LEN];
> +	struct list_head list;
> +};
> +
> +struct vimc_link {
> +	char source_name[VIMC_MAX_NAME_LEN];
> +	char sink_name[VIMC_MAX_NAME_LEN];
> +	u16 source_pad;
> +	u16 sink_pad;
> +	u32 flags;
> +	struct list_head list;
>   };
>   
>   /**
> @@ -156,18 +194,26 @@ struct vimc_ent_config {
>   struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc,
>   				     const char *vcfg_name);
>   void vimc_cap_rm(struct vimc_device *vimc, struct vimc_ent_device *ved);
> +void vimc_cap_init(void);
> +void vimc_cap_exit(void);
>   
>   struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc,
>   				     const char *vcfg_name);
>   void vimc_deb_rm(struct vimc_device *vimc, struct vimc_ent_device *ved);
> +void vimc_deb_init(void);
> +void vimc_deb_exit(void);
>   
>   struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc,
>   				     const char *vcfg_name);
>   void vimc_sca_rm(struct vimc_device *vimc, struct vimc_ent_device *ved);
> +void vimc_sca_init(void);
> +void vimc_sca_exit(void);
>   
>   struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc,
>   				     const char *vcfg_name);
>   void vimc_sen_rm(struct vimc_device *vimc, struct vimc_ent_device *ved);
> +void vimc_sen_init(void);
> +void vimc_sen_exit(void);
>   
>   /**
>    * vimc_pads_init - initialize pads
> diff --git a/drivers/media/platform/vimc/vimc-configfs.c b/drivers/media/platform/vimc/vimc-configfs.c
> new file mode 100644
> index 000000000000..933aece0bb5f
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-configfs.c
> @@ -0,0 +1,656 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * vimc-configfs.c Virtual Media Controller Driver
> + *
> + * Copyright (C) 2018 Helen Koike <helen.koike@collabora.com>
> + */
> +
> +#include <linux/platform_device.h>
> +
> +#include "vimc-common.h"
> +#include "vimc-configfs.h"
> +
> +#define CHAR_SEPARATOR ':'
> +#define CFS_SUBSYS_NAME "vimc"
> +#define MAX_PAD_DIGI_NUM 4
> +
> +#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: %s:" pr_fmt(fmt), (ci)->ci_name, __func__, ##__VA_ARGS__)
> +#define cg_dbg(cg, ...) ci_dbg(&(cg)->cg_item, ##__VA_ARGS__)
> +
> +#define IS_PLUGGED(cfs) (!!(cfs)->pdev)
> +
> +// currently there is no entity with more than two pads, this will
> +// change when adding the splitter entity
> +#define VIMC_ENT_MAX_PADS 2
> +
> +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 list_head ents;
> +	struct list_head links;
> +	struct platform_device *pdev;
> +	struct vimc_platform_data_core pdata;
> +	struct config_group gdev;
> +};
> +
> +/* Structure which describes individual configuration for each entity */
> +struct vimc_cfs_ent {
> +	struct vimc_entity_data ent;
> +	struct config_group cg;
> +	struct config_group pad_groups[VIMC_ENT_MAX_PADS];
> +};
> +
> +struct vimc_cfs_link {
> +	struct vimc_link link;
> +	struct config_item ci;
> +};
> +
> +void vimc_cfs_drv_register(struct vimc_cfs_drv *c_drv)
> +{
> +	pr_debug("%s: adding driver %s\n", __func__, c_drv->name);
> +	list_add(&c_drv->list, &vimc_cfs_subsys.drvs);
> +}
> +EXPORT_SYMBOL_GPL(vimc_cfs_drv_register);

It appears we are adding drivers? Is this necessary. I would have
been helpful see the design overview in the commit log it is clear
why?


Can you please do that first. I will review the rest after that.

> +
> +void vimc_cfs_drv_unregister(struct vimc_cfs_drv *c_drv)
> +{
> +	pr_debug("%s: removing driver %s\n", __func__, c_drv->name);
> +	list_del(&c_drv->list);
> +}
> +EXPORT_SYMBOL_GPL(vimc_cfs_drv_unregister);
> +
> +/* --------------------------------------------------------------------------
> + * Platform Device builders
> + */
> +
> +static void vimc_cfs_device_unplug(struct vimc_cfs_device *cfs)
> +{
> +	dev_dbg(&cfs->pdev->dev, "Unplugging device\n");
> +	platform_device_unregister(cfs->pdev);
> +
> +	cfs->pdev = NULL;
> +}
> +
> +static int vimc_cfs_device_plug(struct vimc_cfs_device *cfs)
> +{
> +	cg_dbg(&cfs->gdev, "Plugging device\n");
> +
> +	if (list_empty(&cfs->ents)) {
> +		cg_warn(&cfs->gdev,
> +			"At least one entity is required to plug the device\n");
> +		return -EINVAL;
> +	}
> +
> +	cfs->pdev = platform_device_register_data(NULL, "vimc-core",
> +						  PLATFORM_DEVID_AUTO,
> +						  &cfs->pdata,
> +						  sizeof(cfs->pdata));
> +	if (IS_ERR(cfs->pdev)) {
> +		int ret = PTR_ERR(cfs->pdev);
> +
> +		cfs->pdev = NULL;
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +/* --------------------------------------------------------------------------
> + * Links
> + */
> +
> +static ssize_t vimc_cfs_link_store_flag(struct config_item *item,
> +					const char *buf,
> +					size_t size, u32 flag)
> +{
> +	struct vimc_cfs_link *c_link =
> +		container_of(item, struct vimc_cfs_link, ci);
> +
> +	if (!strncmp(buf, "on\n", 4) || !strncmp(buf, "1\n", 3)) {
> +		c_link->link.flags |= MEDIA_LNK_FL_ENABLED;
> +		return strlen(buf);
> +	} else if (!strncmp(buf, "off\n", 5) || !strncmp(buf, "0\n", 3)) {
> +		c_link->link.flags &= ~MEDIA_LNK_FL_ENABLED;
> +		return strlen(buf);
> +	}
> +	return -EINVAL;
> +}
> +
> +static ssize_t vimc_cfs_link_show_flag(struct config_item *item,
> +				       char *buf, u32 flag)
> +{
> +	struct vimc_cfs_link *c_link = container_of(item,
> +						    struct vimc_cfs_link, ci);
> +
> +	if (c_link->link.flags & flag)
> +		strscpy(buf, "on\n", 4);
> +	else
> +		strscpy(buf, "off\n", 5);
> +	return strlen(buf);
> +}
> +
> +static ssize_t vimc_cfs_link_enabled_store(struct config_item *item,
> +					   const char *buf,
> +					   size_t size)
> +{
> +	return vimc_cfs_link_store_flag(item, buf, size, MEDIA_LNK_FL_ENABLED);
> +}
> +
> +
> +static ssize_t vimc_cfs_link_enabled_show(struct config_item *item,
> +					  char *buf)
> +{
> +	return vimc_cfs_link_show_flag(item, buf, MEDIA_LNK_FL_ENABLED);
> +}
> +
> +CONFIGFS_ATTR(vimc_cfs_link_, enabled);
> +
> +static ssize_t vimc_cfs_link_immutable_show(struct config_item *item,
> +						 char *buf)
> +{
> +	return vimc_cfs_link_show_flag(item, buf, MEDIA_LNK_FL_IMMUTABLE);
> +}
> +
> +static ssize_t vimc_cfs_link_immutable_store(struct config_item *item,
> +						   const char *buf, size_t size)
> +{
> +	return vimc_cfs_link_store_flag(item, buf, size,
> +					MEDIA_LNK_FL_IMMUTABLE);
> +}
> +
> +CONFIGFS_ATTR(vimc_cfs_link_, immutable);
> +
> +static void vimc_cfs_fill_link_data(struct config_item *src,
> +				struct config_item *target)
> +{
> +	struct config_item *src_ent_ci = src->ci_parent->ci_parent;
> +	struct config_item *trgt_ent_ci = target->ci_parent;
> +	struct vimc_cfs_link *c_link =
> +			container_of(src, struct vimc_cfs_link, ci);
> +	struct vimc_cfs_ent *vimc_src_ent = container_of(src_ent_ci,
> +							 struct vimc_cfs_ent,
> +							 cg.cg_item);
> +	struct vimc_cfs_ent *vimc_trgt_ent = container_of(trgt_ent_ci,
> +							 struct vimc_cfs_ent,
> +							 cg.cg_item);
> +	struct vimc_cfs_device *cfs = container_of(src_ent_ci->ci_parent,
> +							 struct vimc_cfs_device,
> +							 gdev.cg_item);
> +
> +	if (IS_PLUGGED(cfs))
> +		vimc_cfs_device_unplug(cfs);
> +
> +	/* src and target validation already done in the allow_link callback,
> +	 * so there is no need to check sscanf result
> +	 */
> +	sscanf(src->ci_parent->ci_name, VIMC_CFS_SRC_PAD "%hu",
> +	       &c_link->link.source_pad);
> +	sscanf(target->ci_parent->ci_name, VIMC_CFS_SINK_PAD "%hu",
> +	       &c_link->link.sink_pad);
> +
> +	strscpy(c_link->link.source_name, vimc_src_ent->ent.name,
> +		sizeof(c_link->link.source_name));
> +	strscpy(c_link->link.sink_name, vimc_trgt_ent->ent.name,
> +		sizeof(c_link->link.sink_name));
> +
> +	cg_dbg(&cfs->gdev, "creating link %s:%u->%s:%u\n",
> +			c_link->link.source_name, c_link->link.source_pad,
> +			c_link->link.sink_name, c_link->link.sink_pad);
> +
> +	list_add(&c_link->link.list, &cfs->links);
> +}
> +
> +static void vimc_cfs_drop_link(struct config_item *src,
> +			       struct config_item *target)
> +{
> +	struct vimc_cfs_link *c_link = container_of(src,
> +						    struct vimc_cfs_link, ci);
> +
> +	ci_dbg(&c_link->ci, "dropping link %s:%u->%s:%u\n",
> +	       c_link->link.source_name, c_link->link.source_pad,
> +	       c_link->link.sink_name, c_link->link.sink_pad);
> +
> +	c_link->link.source_pad = 0;
> +	c_link->link.sink_pad = 0;
> +	memset(c_link->link.source_name, 0, sizeof(c_link->link.source_name));
> +	memset(c_link->link.sink_name, 0, sizeof(c_link->link.sink_name));
> +	list_del(&c_link->link.list);
> +}
> +
> +int vimc_cfs_allow_link(struct config_item *src, struct config_item *target)
> +{
> +	struct config_item *s = src;
> +	struct config_item *t = target;
> +	struct config_item *src_ent_ci, *trgt_ent_ci;
> +	int i;
> +	int ret = 0;
> +	struct configfs_subsystem *subsys = &vimc_cfs_subsys.subsys;
> +
> +	if (strncmp(target->ci_name, VIMC_CFS_SINK_PAD,
> +		    sizeof(VIMC_CFS_SINK_PAD) - 1)) {
> +		ci_warn(src, "link target (%s) does not start with '%s'\n",
> +			target->ci_name, VIMC_CFS_SINK_PAD);
> +		return -EINVAL;
> +	}
> +
> +	mutex_lock(&subsys->su_mutex);
> +	for (i = 0; i < 3; i++)
> +		s = s->ci_parent;
> +
> +	for (i = 0; i < 2; i++) {
> +		t = t->ci_parent;
> +		if (!t) {
> +			ci_warn(src, "link target (%s) is not a sink pad\n",
> +				target->ci_name);
> +			ret = -EINVAL;
> +			goto end;
> +		}
> +	}
> +
> +	if (s != t) {
> +		ci_warn(src, "not allow linking between different vimcs devices: (%s) and (%s)\n",
> +			s->ci_name, t->ci_name);
> +		ret = -EINVAL;
> +		goto end;
> +	}
> +	src_ent_ci = src->ci_parent->ci_parent;
> +	trgt_ent_ci = target->ci_parent;
> +	if (src_ent_ci == trgt_ent_ci) {
> +		ci_warn(src, "both pads belong to the same entity (%s) - this is not allowed\n",
> +				src_ent_ci->ci_name);
> +		ret = -EINVAL;
> +		goto end;
> +	}
> +
> +	vimc_cfs_fill_link_data(src, target);
> +end:
> +	mutex_unlock(&subsys->su_mutex);
> +	return ret;
> +}
> +
> +static void vimc_cfs_prepare_link_release(struct config_item *item)
> +{
> +	struct vimc_cfs_link *c_link = container_of(item,
> +						    struct vimc_cfs_link, ci);
> +
> +	ci_dbg(item, "releasing link '%s'", item->ci_name);
> +	kfree(c_link);
> +}
> +
> +static struct configfs_attribute *vimc_cfs_link_attrs[] = {
> +	&vimc_cfs_link_attr_enabled,
> +	&vimc_cfs_link_attr_immutable,
> +	NULL,
> +};
> +
> +static struct configfs_item_operations vimc_cfs_allow_link_item_ops = {
> +	.allow_link = vimc_cfs_allow_link,
> +	.drop_link = vimc_cfs_drop_link,
> +	.release = vimc_cfs_prepare_link_release,
> +};
> +
> +struct config_item_type vimc_cfs_allow_link_type = {
> +	.ct_owner = THIS_MODULE,
> +	.ct_item_ops = &vimc_cfs_allow_link_item_ops,
> +	.ct_attrs = vimc_cfs_link_attrs,
> +};
> +
> +/* --------------------------------------------------------------------------
> + * Source pad instance
> + */
> +
> +static void vimc_cfs_src_pad_prepare_link_drop_item(
> +		struct config_group *src_pad_group,
> +		struct config_item *link_item)
> +{
> +
> +	struct config_item *cfs_item;
> +	struct vimc_cfs_device *cfs;
> +
> +	cfs_item = src_pad_group->cg_item.ci_parent->ci_parent;
> +	cfs = container_of(cfs_item->ci_parent, struct vimc_cfs_device,
> +			   gdev.cg_item);
> +	cg_dbg(&cfs->gdev, "dropping prepared link '%s'\n",
> +	       link_item->ci_name);
> +	if (IS_PLUGGED(cfs))
> +		vimc_cfs_device_unplug(cfs);
> +	config_item_put(link_item);
> +}
> +
> +static struct config_item *vimc_cfs_src_pad_prepare_link_make_item(
> +			   struct config_group *group,
> +			   const char *name)
> +{
> +	struct vimc_cfs_link *c_link = kzalloc(sizeof(*c_link), GFP_KERNEL);
> +
> +	cg_dbg(group, "link name is '%s'\n", name);
> +	config_item_init_type_name(&c_link->ci, name,
> +				   &vimc_cfs_allow_link_type);
> +	return &c_link->ci;
> +}
> +struct config_item_type vimc_cfs_empty_type = {
> +	.ct_owner = THIS_MODULE,
> +};
> +
> +static struct configfs_group_operations vimc_cfs_src_pad_ops = {
> +	.make_item = vimc_cfs_src_pad_prepare_link_make_item,
> +	.drop_item = vimc_cfs_src_pad_prepare_link_drop_item
> +};
> +
> +struct config_item_type vimc_cfs_src_pad_type = {
> +	.ct_owner = THIS_MODULE,
> +	.ct_group_ops = &vimc_cfs_src_pad_ops,
> +};
> +
> +/* --------------------------------------------------------------------------
> + * Device instance
> + */
> +
> +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);
> +
> +	ci_dbg(item, "releasing entity '%s' of driver '%s'",
> +	       c_ent->ent.name,
> +	       c_ent->ent.drv_name);
> +	kfree(c_ent);
> +}
> +
> +static struct configfs_item_operations vimc_cfs_ent_item_ops = {
> +	.release	= vimc_cfs_ent_release,
> +};
> +
> +struct config_item_type vimc_cfs_ent_type = {
> +	.ct_owner = THIS_MODULE,
> +	.ct_item_ops = &vimc_cfs_ent_item_ops,
> +};
> +
> +void vimc_cfs_add_sink_pad_api(struct config_group *ent_group,
> +					int pad_idx, const char *name)
> +{
> +	struct vimc_cfs_ent *c_ent = container_of(ent_group,
> +						  struct vimc_cfs_ent, cg);
> +
> +	config_group_init_type_name(&c_ent->pad_groups[pad_idx], name,
> +				    &vimc_cfs_empty_type);
> +	configfs_add_default_group(&c_ent->pad_groups[pad_idx], ent_group);
> +}
> +EXPORT_SYMBOL_GPL(vimc_cfs_add_sink_pad_api);
> +
> +void vimc_cfs_add_source_pad_api(struct config_group *ent_group,
> +					  int pad_idx, const char *name)
> +{
> +	struct vimc_cfs_ent *c_ent = container_of(ent_group,
> +						  struct vimc_cfs_ent, cg);
> +
> +	config_group_init_type_name(&c_ent->pad_groups[pad_idx], name,
> +				    &vimc_cfs_src_pad_type);
> +	configfs_add_default_group(&c_ent->pad_groups[pad_idx], ent_group);
> +}
> +EXPORT_SYMBOL_GPL(vimc_cfs_add_source_pad_api);
> +
> +static void vimc_cfs_dev_drop_ent_item(struct config_group *dev_group,
> +					   struct config_item *ent_item)
> +{
> +	struct vimc_cfs_ent *c_ent = container_of(ent_item, struct vimc_cfs_ent,
> +						  cg.cg_item);
> +	struct vimc_cfs_device *cfs = container_of(dev_group,
> +						   struct vimc_cfs_device,
> +						   gdev);
> +
> +	if (IS_PLUGGED(cfs))
> +		vimc_cfs_device_unplug(cfs);
> +
> +	cg_dbg(&cfs->gdev, "dropping entity '%s' of driver '%s'",
> +	       c_ent->ent.name, c_ent->ent.drv_name);
> +	list_del(&c_ent->ent.list);
> +	config_item_put(ent_item);
> +}
> +
> +static struct config_group *vimc_cfs_dev_make_ent_group(
> +			struct config_group *group, const char *name)
> +{
> +	struct vimc_cfs_device *cfs = container_of(group,
> +						   struct vimc_cfs_device,
> +						   gdev);
> +	char *ent_name, *sep = strchr(name, CHAR_SEPARATOR);
> +	struct vimc_cfs_ent *c_ent;
> +	struct vimc_entity_data *ent;
> +	size_t drv_namelen;
> +	struct vimc_cfs_drv *c_drv = NULL;
> +
> +	cg_dbg(group, "trying to make entity '%s'\n", name);
> +	if (IS_PLUGGED(cfs))
> +		vimc_cfs_device_unplug(cfs);
> +
> +	/* Parse format "drv_name:ent_name" */
> +	if (!sep) {
> +		cg_warn(&cfs->gdev,
> +			"Could not find separator '%c'\n", CHAR_SEPARATOR);
> +		goto syntax_error;
> +	}
> +	drv_namelen = (size_t)(sep - name);
> +	ent_name = &sep[1];
> +	if (!*ent_name || !drv_namelen) {
> +		cg_warn(&cfs->gdev,
> +			"%s: Driver name and entity name can't be empty.\n",
> +		       name);
> +		goto syntax_error;
> +	}
> +	if (drv_namelen >= VIMC_MAX_NAME_LEN) {
> +		cg_err(&cfs->gdev,
> +		       "%s: Driver name length should be less than %u.\n",
> +		       name, VIMC_MAX_NAME_LEN);
> +		goto syntax_error;
> +	}
> +	list_for_each_entry(ent, &cfs->ents, list) {
> +		if (!strncmp(ent->name, ent_name, sizeof(ent->name))) {
> +			cg_err(&cfs->gdev, "entity `%s` already exist\n",
> +			       ent->name);
> +			goto syntax_error;
> +		}
> +	}
> +
> +	c_ent = kzalloc(sizeof(*c_ent), GFP_KERNEL);
> +	if (!c_ent)
> +		return ERR_PTR(-ENOMEM);
> +
> +	strscpy(c_ent->ent.drv_name, name, drv_namelen + 1);
> +	strscpy(c_ent->ent.name, ent_name, sizeof(c_ent->ent.name));
> +
> +	/* Configure group */
> +
> +	/* TODO: add support for hotplug at entity level */
> +	list_for_each_entry(c_drv, &vimc_cfs_subsys.drvs, list) {
> +		if (!strcmp(c_ent->ent.drv_name, c_drv->name)) {
> +			config_group_init_type_name(&c_ent->cg, name,
> +						    &vimc_cfs_ent_type);
> +			if (c_drv->create_pads)
> +				c_drv->create_pads(&c_ent->cg);
> +			list_add(&c_ent->ent.list, &cfs->ents);
> +			return &c_ent->cg;
> +		}
> +	}
> +	cg_warn(&cfs->gdev, "entity type %s not found\n", c_ent->ent.drv_name);
> +	kfree(c_ent);
> +	return ERR_PTR(-EINVAL);
> +
> +syntax_error:
> +	cg_err(&cfs->gdev,
> +		"couldn't create entity %s, wrong syntax.", name);
> +	return ERR_PTR(-EINVAL);
> +}
> +
> +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_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_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_, hotplug);
> +
> +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);
> +
> +	ci_dbg(item, "releasing dev %s\n", item->ci_name);
> +	kfree(cfs);
> +}
> +
> +static struct configfs_group_operations vimc_cfs_dev_group_ops = {
> +	.make_group = vimc_cfs_dev_make_ent_group,
> +	.drop_item = vimc_cfs_dev_drop_ent_item,
> +};
> +
> +static struct configfs_item_operations vimc_cfs_dev_item_ops = {
> +	.release = vimc_cfs_dev_release,
> +};
> +
> +static struct configfs_attribute *vimc_cfs_dev_attrs[] = {
> +	&vimc_cfs_dev_attr_hotplug,
> +	NULL,
> +};
> +
> +static struct config_item_type vimc_cfs_dev_type = {
> +	.ct_group_ops = &vimc_cfs_dev_group_ops,
> +	.ct_item_ops = &vimc_cfs_dev_item_ops,
> +	.ct_attrs = vimc_cfs_dev_attrs,
> +	.ct_owner = THIS_MODULE,
> +};
> +
> +/* --------------------------------------------------------------------------
> + * Subsystem
> + * --------------------------------------------------------------------------
> + */
> +
> +static void vimc_cfs_subsys_drop_dev_item(struct config_group *group,
> +				   struct config_item *item)
> +{
> +	struct vimc_cfs_device *cfs = container_of(to_config_group(item),
> +						   struct vimc_cfs_device,
> +						   gdev);
> +
> +	if (IS_PLUGGED(cfs))
> +		vimc_cfs_device_unplug(cfs);
> +	cg_dbg(&cfs->gdev, "dropping subsys '%s'", item->ci_name);
> +	config_item_put(item);
> +}
> +
> +static struct config_group *vimc_cfs_subsys_make_dev_group(
> +				struct config_group *group, const char *name)
> +{
> +	struct vimc_cfs_device *cfs = kzalloc(sizeof(*cfs), GFP_KERNEL);
> +
> +	if (!cfs)
> +		return ERR_PTR(-ENOMEM);
> +
> +	cg_dbg(&cfs->gdev, "making dev group '%s'", name);
> +	/* Configure platform data */
> +	INIT_LIST_HEAD(&cfs->ents);
> +	INIT_LIST_HEAD(&cfs->links);
> +	cfs->pdata.links = &cfs->links;
> +	cfs->pdata.ents = &cfs->ents;
> +
> +	config_group_init_type_name(&cfs->gdev, name, &vimc_cfs_dev_type);
> +
> +	return &cfs->gdev;
> +}
> +
> +static struct configfs_group_operations vimc_cfs_subsys_group_ops = {
> +	.make_group	= vimc_cfs_subsys_make_dev_group,
> +	.drop_item	= vimc_cfs_subsys_drop_dev_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..0d3afdb31fdd
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-configfs.h
> @@ -0,0 +1,41 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * vimc-configfs.h Virtual Media Controller Driver
> + *
> + * Copyright (C) 2018 Helen Koike <helen.koike@collabora.com>
> + */
> +
> +#ifndef _VIMC_CONFIGFS_H_
> +#define _VIMC_CONFIGFS_H_
> +
> +#include <linux/configfs.h>
> +
> +#define VIMC_CFS_SRC_PAD "pad:source:"
> +#define VIMC_CFS_SINK_PAD "pad:sink:"
> +
> +#define VIMC_CFS_SRC_PAD_NUM(n) "pad:source:" #n
> +#define VIMC_CFS_SINK_PAD_NUM(n) "pad:sink:" #n
> +
> +extern struct config_item_type vimc_default_cfs_pad_type;
> +
> +void vimc_cfs_add_source_pad_api(struct config_group *ent_group,
> +					int pad_idx, const char *name);
> +
> +void vimc_cfs_add_sink_pad_api(struct config_group *ent_group,
> +				      int pad_idx, const char *name);
> +struct vimc_cfs_drv {
> +	const char *name;
> +	struct list_head list;
> +
> +	void (*const create_pads)(struct config_group *parent);
> +};
> +
> +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 6e3e5c91ae39..476bad2cb2de 100644
> --- a/drivers/media/platform/vimc/vimc-core.c
> +++ b/drivers/media/platform/vimc/vimc-core.c
> @@ -15,6 +15,8 @@
>   
>   #define VIMC_MDEV_MODEL_NAME "VIMC MDEV"
>   
> +#include "vimc-configfs.h"
> +
>   #define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) {	\
>   	.src_ent = src,						\
>   	.src_pad = srcpad,					\
> @@ -335,13 +337,30 @@ static int __init vimc_init(void)
>   		return ret;
>   	}
>   
> +	ret = vimc_cfs_subsys_register();
> +	if (ret) {
> +		pr_err("%s: configfs subsys registration failed (%d)\n",
> +		       __func__, ret);
> +		platform_driver_unregister(&vimc_pdrv);
> +		return ret;
> +	}
> +
> +	vimc_sen_init();
> +	vimc_deb_init();
> +	vimc_sca_init();
> +	vimc_cap_init();
>   	return 0;
>   }
>   
>   static void __exit vimc_exit(void)
>   {
> -	platform_driver_unregister(&vimc_pdrv);
> +	vimc_sen_exit();
> +	vimc_deb_exit();
> +	vimc_sca_exit();
> +	vimc_cap_exit();
>   
> +	vimc_cfs_subsys_unregister();
> +	platform_driver_unregister(&vimc_pdrv);
>   	platform_device_unregister(&vimc_dev.pdev);
>   }
>   
> diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c
> index feac47d79449..e461b155e514 100644
> --- a/drivers/media/platform/vimc/vimc-debayer.c
> +++ b/drivers/media/platform/vimc/vimc-debayer.c
> @@ -12,6 +12,7 @@
>   #include <media/v4l2-subdev.h>
>   
>   #include "vimc-common.h"
> +#include "vimc-configfs.h"
>   
>   static unsigned int deb_mean_win_size = 3;
>   module_param(deb_mean_win_size, uint, 0000);
> @@ -533,3 +534,24 @@ struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc,
>   
>   	return &vdeb->ved;
>   }
> +
> +static void vimc_deb_create_cfs_pads(struct config_group *ent_group)
> +{
> +	vimc_cfs_add_source_pad_api(ent_group, 0, VIMC_CFS_SRC_PAD_NUM(1));
> +	vimc_cfs_add_sink_pad_api(ent_group, 1, VIMC_CFS_SINK_PAD_NUM(0));
> +}
> +
> +struct vimc_cfs_drv vimc_deb_cfs_drv = {
> +	.name = VIMC_DEB_NAME,
> +	.create_pads = vimc_deb_create_cfs_pads,
> +};
> +
> +__exit void vimc_deb_exit(void)
> +{
> +	vimc_cfs_drv_unregister(&vimc_deb_cfs_drv);
> +}
> +
> +__init void vimc_deb_init(void)
> +{
> +	vimc_cfs_drv_register(&vimc_deb_cfs_drv);
> +}
> diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c
> index a6a3cc5be872..e5cf0073d68a 100644
> --- a/drivers/media/platform/vimc/vimc-scaler.c
> +++ b/drivers/media/platform/vimc/vimc-scaler.c
> @@ -10,6 +10,7 @@
>   #include <linux/v4l2-mediabus.h>
>   #include <media/v4l2-subdev.h>
>   
> +#include "vimc-configfs.h"
>   #include "vimc-common.h"
>   
>   static unsigned int sca_mult = 3;
> @@ -373,14 +374,35 @@ struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc,
>   				   &vimc_sca_int_ops, &vimc_sca_ops);
>   	if (ret) {
>   		kfree(vsca);
> -		return NULL;
> +		return ERR_PTR(ret);
>   	}
>   
>   	vsca->ved.process_frame = vimc_sca_process_frame;
> -	vsca->dev = &vimc->pdev.dev;
> +	vsca->dev = vimc->mdev.dev;
>   
>   	/* Initialize the frame format */
>   	vsca->sink_fmt = sink_fmt_default;
>   
>   	return &vsca->ved;
>   }
> +
> +static void vimc_sca_create_cfs_pads(struct config_group *ent_group)
> +{
> +	vimc_cfs_add_source_pad_api(ent_group, 0, VIMC_CFS_SRC_PAD_NUM(1));
> +	vimc_cfs_add_sink_pad_api(ent_group, 1, VIMC_CFS_SINK_PAD_NUM(0));
> +}
> +
> +struct vimc_cfs_drv vimc_sca_cfs_drv = {
> +	.name = VIMC_SCA_NAME,
> +	.create_pads = vimc_sca_create_cfs_pads,
> +};
> +
> +__exit void vimc_sca_exit(void)
> +{
> +	vimc_cfs_drv_unregister(&vimc_sca_cfs_drv);
> +}
> +
> +__init void vimc_sca_init(void)
> +{
> +	vimc_cfs_drv_register(&vimc_sca_cfs_drv);
> +}
> diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c
> index 46dc6a535abe..22e3ad98c818 100644
> --- a/drivers/media/platform/vimc/vimc-sensor.c
> +++ b/drivers/media/platform/vimc/vimc-sensor.c
> @@ -12,6 +12,7 @@
>   #include <media/v4l2-subdev.h>
>   #include <media/tpg/v4l2-tpg.h>
>   
> +#include "vimc-configfs.h"
>   #include "vimc-common.h"
>   
>   struct vimc_sen_device {
> @@ -391,3 +392,23 @@ struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc,
>   
>   	return NULL;
>   }
> +
> +static void vimc_sen_create_cfs_pads(struct config_group *ent_group)
> +{
> +	vimc_cfs_add_source_pad_api(ent_group, 0, VIMC_CFS_SRC_PAD_NUM(0));
> +}
> +
> +struct vimc_cfs_drv vimc_sen_cfs_drv = {
> +	.name = VIMC_SEN_NAME,
> +	.create_pads = vimc_sen_create_cfs_pads,
> +};
> +
> +__exit void vimc_sen_exit(void)
> +{
> +	vimc_cfs_drv_unregister(&vimc_sen_cfs_drv);
> +}
> +
> +__init void vimc_sen_init(void)
> +{
> +	vimc_cfs_drv_register(&vimc_sen_cfs_drv);
> +}
> 


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 4/5] media: vimc: use configfs instead of having hardcoded configuration
  2019-09-19 20:32 ` [PATCH 4/5] media: vimc: use configfs instead of having hardcoded configuration Dafna Hirschfeld
@ 2019-09-26 19:41   ` Shuah Khan
  0 siblings, 0 replies; 20+ messages in thread
From: Shuah Khan @ 2019-09-26 19:41 UTC (permalink / raw)
  To: Dafna Hirschfeld, linux-media
  Cc: laurent.pinchart, helen.koike, ezequiel, andre.almeida, hverkuil,
	kernel, dafna3, skha >> Shuah Khan

On 9/19/19 2:32 PM, Dafna Hirschfeld wrote:
> Use configfs in order to create a device and set its topology
> and remove the hardcoded device.
> 
> Signed-off-by: Helen Koike <helen.koike@collabora.com>
> [refactored for upstream]

What does this mean?

> Signed-off-by: Dafna Hirschfeld <dafna.hirschfeld@collabora.com>
> ---
>   drivers/media/platform/vimc/vimc-capture.c  |  21 +-
>   drivers/media/platform/vimc/vimc-common.h   |  50 +--
>   drivers/media/platform/vimc/vimc-configfs.c |   6 +-
>   drivers/media/platform/vimc/vimc-core.c     | 319 ++++++++------------
>   drivers/media/platform/vimc/vimc-debayer.c  |  13 +-
>   drivers/media/platform/vimc/vimc-scaler.c   |   9 +-
>   drivers/media/platform/vimc/vimc-sensor.c   |  12 +-
>   7 files changed, 170 insertions(+), 260 deletions(-)
> 
> diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c
> index 5cc419e76ce7..6ccf879c0b75 100644
> --- a/drivers/media/platform/vimc/vimc-capture.c
> +++ b/drivers/media/platform/vimc/vimc-capture.c
> @@ -335,11 +335,12 @@ static void vimc_cap_release(struct video_device *vdev)
>   	kfree(vcap);
>   }
>   
> -void vimc_cap_rm(struct vimc_device *vimc, struct vimc_ent_device *ved)
> +void vimc_cap_rm(struct vimc_ent_device *ved)
>   {
> -	struct vimc_cap_device *vcap;
> +	struct vimc_cap_device *vcap = container_of(ved,
> +						    struct vimc_cap_device,
> +						    ved);
>   
> -	vcap = container_of(ved, struct vimc_cap_device, ved);
>   	vb2_queue_release(&vcap->queue);
>   	media_entity_cleanup(ved->ent);
>   	video_unregister_device(&vcap->vdev);
> @@ -397,8 +398,9 @@ struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc,
>   	/* Allocate the vimc_cap_device struct */
>   	vcap = kzalloc(sizeof(*vcap), GFP_KERNEL);
>   	if (!vcap)
> -		return NULL;
> +		return ERR_PTR(-ENOMEM);
>   
> +	strscpy(vcap->ved.name, vcfg_name, sizeof(vcap->ved.name));
>   	/* Allocate the pads */
>   	vcap->ved.pads =
>   		vimc_pads_init(1, (const unsigned long[1]) {MEDIA_PAD_FL_SINK});
> @@ -408,7 +410,7 @@ struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc,
>   	}
>   
>   	/* Initialize the media entity */
> -	vcap->vdev.entity.name = vcfg_name;
> +	vcap->vdev.entity.name = vcap->ved.name;
>   	vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L;
>   	ret = media_entity_pads_init(&vcap->vdev.entity,
>   				     1, vcap->ved.pads);
> @@ -432,7 +434,7 @@ struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc,
>   
>   	ret = vb2_queue_init(q);
>   	if (ret) {
> -		dev_err(&vimc->pdev.dev, "%s: vb2 queue init failed (err=%d)\n",
> +		dev_err(vimc->mdev.dev, "%s: vb2 queue init failed (err=%d)\n",
>   			vcfg_name, ret);
>   		goto err_clean_m_ent;
>   	}
> @@ -452,7 +454,8 @@ struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc,
>   	vcap->ved.ent = &vcap->vdev.entity;
>   	vcap->ved.process_frame = vimc_cap_process_frame;
>   	vcap->ved.vdev_get_format = vimc_cap_get_format;
> -	vcap->dev = &vimc->pdev.dev;
> +
> +	vcap->dev = vimc->mdev.dev;
>   
>   	/* Initialize the video_device struct */
>   	vdev = &vcap->vdev;
> @@ -471,7 +474,7 @@ struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc,
>   	/* Register the video_device with the v4l2 and the media framework */
>   	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
>   	if (ret) {
> -		dev_err(&vimc->pdev.dev, "%s: video register failed (err=%d)\n",
> +		dev_err(vimc->mdev.dev, "%s: video register failed (err=%d)\n",
>   			vcap->vdev.name, ret);
>   		goto err_release_queue;
>   	}
> @@ -487,7 +490,7 @@ struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc,
>   err_free_vcap:
>   	kfree(vcap);
>   
> -	return NULL;
> +	return ERR_PTR(ret);
>   }
>   
>   static void vimc_cap_create_cfs_pads(struct config_group *ent_group)
> diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h
> index e0e3b3ab7b19..5c7f451a7bf0 100644
> --- a/drivers/media/platform/vimc/vimc-common.h
> +++ b/drivers/media/platform/vimc/vimc-common.h
> @@ -63,21 +63,6 @@ do {									\
>   		(fmt)->xfer_func = V4L2_XFER_FUNC_DEFAULT;		\
>   } while (0)
>   
> -/**
> - * struct vimc_platform_data - platform data to components
> - *
> - * @entity_name:	The name of the entity to be created
> - *
> - * 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.
> - */
> -struct vimc_platform_data {
> -	char entity_name[32];
> -};
> -
>   /**
>    * struct vimc_platform_data_core - platform data to the core
>    *
> @@ -157,61 +142,56 @@ struct vimc_link {
>   /**
>    * struct vimc_device - main device for vimc driver
>    *
> - * @pdev	pointer to the platform device
> - * @pipe_cfg	pointer to the vimc pipeline configuration structure
> - * @ent_devs	array of vimc_ent_device pointers
>    * @mdev	the associated media_device parent
>    * @v4l2_dev	Internal v4l2 parent device
> + * @ents	list of vimc_ent_device objects
>    */
>   struct vimc_device {
> -	struct platform_device pdev;
> -	const struct vimc_pipeline_config *pipe_cfg;
> -	struct vimc_ent_device **ent_devs;
>   	struct media_device mdev;
>   	struct v4l2_device v4l2_dev;
> +
> +	struct list_head ents;
>   };
>   
>   /**
> - * struct vimc_ent_config	Structure which describes individual
> - *				configuration for each entity
> - *
> - * @name			entity name
> - * @ved				pointer to vimc_ent_device (a node in the
> - *					topology)
> + * struct ent_type		Structure for the add and remove callbacks
> + *				of entity type
> + * @name			entity type name
>    * @add				subdev add hook - initializes and registers
>    *					subdev called from vimc-core
>    * @rm				subdev rm hook - unregisters and frees
>    *					subdev called from vimc-core
>    */
> -struct vimc_ent_config {
> -	const char *name;
> -	struct vimc_ent_device *(*add)(struct vimc_device *vimc,
> +
> +struct vimc_ent_type {
> +	const char name[VIMC_MAX_NAME_LEN];
> +	struct vimc_ent_device*	(*add)(struct vimc_device *vimc,
>   				       const char *vcfg_name);
> -	void (*rm)(struct vimc_device *vimc, struct vimc_ent_device *ved);
> +	void (*rm)(struct vimc_ent_device *ent);
>   };
>   
>   /* prototypes for vimc_ent_config add and rm hooks */
>   struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc,
>   				     const char *vcfg_name);
> -void vimc_cap_rm(struct vimc_device *vimc, struct vimc_ent_device *ved);
> +void vimc_cap_rm(struct vimc_ent_device *ent);
>   void vimc_cap_init(void);
>   void vimc_cap_exit(void);
>   
>   struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc,
>   				     const char *vcfg_name);
> -void vimc_deb_rm(struct vimc_device *vimc, struct vimc_ent_device *ved);
> +void vimc_deb_rm(struct vimc_ent_device *ent);
>   void vimc_deb_init(void);
>   void vimc_deb_exit(void);
>   
>   struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc,
>   				     const char *vcfg_name);
> -void vimc_sca_rm(struct vimc_device *vimc, struct vimc_ent_device *ved);
> +void vimc_sca_rm(struct vimc_ent_device *ent);
>   void vimc_sca_init(void);
>   void vimc_sca_exit(void);
>   
>   struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc,
>   				     const char *vcfg_name);
> -void vimc_sen_rm(struct vimc_device *vimc, struct vimc_ent_device *ved);
> +void vimc_sen_rm(struct vimc_ent_device *ent);
>   void vimc_sen_init(void);
>   void vimc_sen_exit(void);
>   
> diff --git a/drivers/media/platform/vimc/vimc-configfs.c b/drivers/media/platform/vimc/vimc-configfs.c
> index 933aece0bb5f..e99f926d2f93 100644
> --- a/drivers/media/platform/vimc/vimc-configfs.c
> +++ b/drivers/media/platform/vimc/vimc-configfs.c
> @@ -335,8 +335,8 @@ static void vimc_cfs_src_pad_prepare_link_drop_item(
>   	cfs_item = src_pad_group->cg_item.ci_parent->ci_parent;
>   	cfs = container_of(cfs_item->ci_parent, struct vimc_cfs_device,
>   			   gdev.cg_item);
> -	cg_dbg(&cfs->gdev, "dropping prepared link '%s'\n",
> -	       link_item->ci_name);
> +	cg_dbg(&cfs->gdev, "dropping prepared link '%s' cfs=%p\n",
> +	       link_item->ci_name, cfs);
>   	if (IS_PLUGGED(cfs))
>   		vimc_cfs_device_unplug(cfs);
>   	config_item_put(link_item);
> @@ -614,7 +614,7 @@ static struct config_group *vimc_cfs_subsys_make_dev_group(
>   	if (!cfs)
>   		return ERR_PTR(-ENOMEM);
>   
> -	cg_dbg(&cfs->gdev, "making dev group '%s'", name);
> +	cg_dbg(&cfs->gdev, "making dev group '%s', cfs=%p", name, cfs);
>   	/* Configure platform data */
>   	INIT_LIST_HEAD(&cfs->ents);
>   	INIT_LIST_HEAD(&cfs->links);
> diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c
> index 476bad2cb2de..06a36cc1f00c 100644
> --- a/drivers/media/platform/vimc/vimc-core.c
> +++ b/drivers/media/platform/vimc/vimc-core.c
> @@ -17,178 +17,137 @@
>   
>   #include "vimc-configfs.h"
>   
> -#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,					\
> -}
> -
> -/* 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 struct vimc_ent_config ent_config[] = {
> -	{
> -		.name = "Sensor A",
> -		.add = vimc_sen_add,
> -		.rm = vimc_sen_rm,
> -	},
> +static struct vimc_ent_type ent_types[] = {
>   	{
> -		.name = "Sensor B",
> +		.name = VIMC_SEN_NAME,
>   		.add = vimc_sen_add,
>   		.rm = vimc_sen_rm,
>   	},
>   	{
> -		.name = "Debayer A",
> +		.name = VIMC_DEB_NAME,
>   		.add = vimc_deb_add,
>   		.rm = vimc_deb_rm,
>   	},
>   	{
> -		.name = "Debayer B",
> -		.add = vimc_deb_add,
> -		.rm = vimc_deb_rm,
> -	},
> -	{
> -		.name = "Raw Capture 0",
> -		.add = vimc_cap_add,
> -		.rm = vimc_cap_rm,
> -	},
> -	{
> -		.name = "Raw Capture 1",
> +		.name = VIMC_CAP_NAME,
>   		.add = vimc_cap_add,
>   		.rm = vimc_cap_rm,
>   	},
>   	{
> -		/* TODO: change this to vimc-input when it is implemented */
> -		.name = "RGB/YUV Input",
> -		.add = vimc_sen_add,
> -		.rm = vimc_sen_rm,
> -	},
> -	{
> -		.name = "Scaler",
> +		.name = VIMC_SCA_NAME,
>   		.add = vimc_sca_add,
>   		.rm = vimc_sca_rm,
>   	},
> -	{
> -		.name = "RGB/YUV Capture",
> -		.add = vimc_cap_add,
> -		.rm = vimc_cap_rm,
> -	},
>   };
>   
> -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 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 struct vimc_ent_type *vimc_get_ent_type(const char *drv_name)
> +{
> +	int i;
>   
> -/* -------------------------------------------------------------------------- */
> +	for (i = 0; i < ARRAY_SIZE(ent_types); i++)
> +		if (!strcmp(drv_name, ent_types[i].name))
> +			return &ent_types[i];
> +	return NULL;
> +}
>   
> -static void vimc_rm_links(struct vimc_device *vimc)
> +struct vimc_ent_device *vimc_get_ent_dev(const struct vimc_device *vimc,
> +					 const char *const name)
>   {
> -	unsigned int i;
> +	struct vimc_ent_device *ent;
>   
> -	for (i = 0; i < vimc->pipe_cfg->num_ents; i++)
> -		media_entity_remove_links(vimc->ent_devs[i]->ent);
> +	list_for_each_entry(ent, &vimc->ents, list) {
> +		if (!strcmp(ent->name, name))
> +			return ent;
> +	}
> +	return NULL;
>   }
> +//TODO - remove subdevs upon failure
> +static int vimc_core_links_create(const struct vimc_device *vimc,
> +				  const struct vimc_platform_data_core *pdata)
> +{
> +	struct vimc_link *link;
> +
> +	list_for_each_entry(link, pdata->links, list) {
> +		struct vimc_ent_device *source = vimc_get_ent_dev(vimc,
> +						 link->source_name);
> +		struct vimc_ent_device *sink = vimc_get_ent_dev(vimc,
> +					       link->sink_name);
> +		int ret;
> +
> +		ret = media_create_pad_link(source->ent,
> +					    link->source_pad,
> +					    sink->ent,
> +					    link->sink_pad,
> +					    link->flags);
>   
> -static int vimc_create_links(struct vimc_device *vimc)
> +		if (ret) {
> +			pr_err("%s: failed to create link %s:%u->%s:%u\n",
> +					__func__,
> +					source->name, link->source_pad,
> +					sink->name, link->sink_pad);
> +			return ret;
> +		}
> +		pr_debug("%s: created link %s:%u->%s:%u\n", __func__,
> +				source->name, link->source_pad,
> +				sink->name, link->sink_pad);
> +	}
> +	return 0;
> +}
> +static void vimc_rm_subdevs(struct vimc_device *vimc,
> +			    const struct vimc_platform_data_core *pdata)
>   {
> -	unsigned int i;
> -	int ret;
> +	struct vimc_ent_device *ent;
> +	struct vimc_ent_device *ent_tmp;
>   
> -	/* 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];
> +	list_for_each_entry_safe(ent, ent_tmp, &vimc->ents, list) {
>   
> -		struct vimc_ent_device *ved_src =
> -			vimc->ent_devs[link->src_ent];
> -		struct vimc_ent_device *ved_sink =
> -			vimc->ent_devs[link->sink_ent];
> +		struct vimc_ent_type *ent_type = vimc_get_ent_type(ent->drv_name);
>   
> -		ret = media_create_pad_link(ved_src->ent, link->src_pad,
> -					    ved_sink->ent, link->sink_pad,
> -					    link->flags);
> -		if (ret)
> -			goto err_rm_links;
> +		BUG_ON(!ent_type);

Please don't use BOG_ON here. We are trying to get rid of BUG_ONs in the
kernel.

> +		dev_dbg(vimc->mdev.dev, "removing entity %s:%s\n",
> +			ent->drv_name, ent->name);
> +		list_del(&ent->list);
> +		ent_type->rm(ent);
>   	}
> -
> -	return 0;
> -
> -err_rm_links:
> -	vimc_rm_links(vimc);
> -	return ret;
>   }
>   
> -static int vimc_add_subdevs(struct vimc_device *vimc)
> +static int vimc_add_subdevs(struct vimc_device *vimc,
> +		const struct vimc_platform_data_core *pdata)
>   {
> -	unsigned int i;
> -	struct vimc_ent_device *ved;
> -
> -	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
> -		dev_dbg(&vimc->pdev.dev, "new entity for %s\n",
> -			vimc->pipe_cfg->ents[i].name);
> -		ved = vimc->pipe_cfg->ents[i].add(vimc,
> -					vimc->pipe_cfg->ents[i].name);
> -		if (!ved) {
> -			dev_err(&vimc->pdev.dev, "add new entity for %s\n",
> -				vimc->pipe_cfg->ents[i].name);
> -			return -EINVAL;
> +	struct vimc_entity_data *ent;
> +	int ret;
> +
> +	list_for_each_entry(ent, pdata->ents, list) {
> +
> +		struct vimc_ent_type *ent_type =
> +			vimc_get_ent_type(ent->drv_name);
> +		struct vimc_ent_device *ent_dev;
> +
> +		BUG_ON(!ent_type);

Same comment as above. No BUD_ON Let's handle the error.

> +
> +		ent_dev = ent_type->add(vimc, ent->name);
> +		if (IS_ERR(ent_dev)) {
> +			ret = PTR_ERR(ent_dev);
> +			dev_err(vimc->mdev.dev, "failed to add entity %s:%s\n",
> +				ent->drv_name, ent->name);
> +			goto err;
>   		}
> -		vimc->ent_devs[i] = ved;
> +		strscpy(ent_dev->name, ent->name, sizeof(ent_dev->name));
> +		strscpy(ent_dev->drv_name, ent->drv_name,
> +			sizeof(ent_dev->drv_name));
> +		list_add(&ent_dev->list, &vimc->ents);
> +		dev_dbg(vimc->mdev.dev, "%s: entity %s:%s added\n", __func__,
> +				ent->drv_name, ent->name);
>   	}
>   	return 0;
> +err:
> +	vimc_rm_subdevs(vimc, pdata);
> +	return ret;
>   }
>   
> -static void vimc_rm_subdevs(struct vimc_device *vimc)
> -{
> -	unsigned int i;
> -
> -	for (i = 0; i < vimc->pipe_cfg->num_ents; i++)
> -		if (vimc->ent_devs[i])
> -			vimc->pipe_cfg->ents[i].rm(vimc, vimc->ent_devs[i]);
> -}
> -
> -static int vimc_register_devices(struct vimc_device *vimc)
> +static int vimc_register_devices(struct vimc_device *vimc,
> +				 const struct vimc_platform_data_core *pdata)
>   {
>   	int ret;
>   
> @@ -196,25 +155,15 @@ static int vimc_register_devices(struct vimc_device *vimc)
>   	ret = v4l2_device_register(vimc->mdev.dev, &vimc->v4l2_dev);
>   	if (ret) {
>   		dev_err(vimc->mdev.dev,
> -			"v4l2 device register failed (err=%d)\n", ret);
> +			"v4l2 device register failed (%d)\n", ret);
>   		return ret;
>   	}
>   
> -	/* allocate ent_devs */
> -	vimc->ent_devs = kmalloc_array(vimc->pipe_cfg->num_ents,
> -				       sizeof(*vimc->ent_devs),
> -				       GFP_KERNEL);
> -	if (!vimc->ent_devs)
> -		goto err_v4l2_unregister;
> -
> -	/* Invoke entity config hooks to initialize and register subdevs */
> -	ret = vimc_add_subdevs(vimc);
> +	ret = vimc_add_subdevs(vimc, pdata);
>   	if (ret)
> -		/* remove sundevs that got added */
> -		goto err_rm_subdevs;
> +		goto err_v4l2_unregister;
>   
> -	/* Initialize links */
> -	ret = vimc_create_links(vimc);
> +	ret = vimc_core_links_create(vimc, pdata);
>   	if (ret)
>   		goto err_rm_subdevs;
>   
> @@ -222,16 +171,15 @@ static int vimc_register_devices(struct vimc_device *vimc)
>   	ret = media_device_register(&vimc->mdev);
>   	if (ret) {
>   		dev_err(vimc->mdev.dev,
> -			"media device register failed (err=%d)\n", ret);
> +			"media device register failed (%d)\n", ret);
>   		goto err_rm_subdevs;
>   	}
>   
> -	/* Expose all subdev's nodes*/
> +	/* Expose all subdev's nodes */
>   	ret = v4l2_device_register_subdev_nodes(&vimc->v4l2_dev);
>   	if (ret) {
>   		dev_err(vimc->mdev.dev,
> -			"vimc subdev nodes registration failed (err=%d)\n",
> -			ret);
> +			"vimc subdev nodes registration failed (%d)\n", ret);
>   		goto err_mdev_unregister;
>   	}
>   
> @@ -241,30 +189,28 @@ static int vimc_register_devices(struct vimc_device *vimc)
>   	media_device_unregister(&vimc->mdev);
>   	media_device_cleanup(&vimc->mdev);
>   err_rm_subdevs:
> -	vimc_rm_subdevs(vimc);
> -	kfree(vimc->ent_devs);
> +	vimc_rm_subdevs(vimc, pdata);
>   err_v4l2_unregister:
>   	v4l2_device_unregister(&vimc->v4l2_dev);
>   
>   	return ret;
>   }
>   
> -static void vimc_unregister(struct vimc_device *vimc)
> -{
> -	media_device_unregister(&vimc->mdev);
> -	media_device_cleanup(&vimc->mdev);
> -	v4l2_device_unregister(&vimc->v4l2_dev);
> -	kfree(vimc->ent_devs);
> -}
> -
>   static int vimc_probe(struct platform_device *pdev)
>   {
> -	struct vimc_device *vimc = container_of(pdev, struct vimc_device, pdev);
> +	const struct vimc_platform_data_core *pdata = pdev->dev.platform_data;
> +	struct vimc_device *vimc;
>   	int ret;
>   
> -	dev_dbg(&pdev->dev, "probe");
> +	dev_dbg(&pdev->dev, "probe\n");
> +
> +	vimc = devm_kzalloc(&pdev->dev, sizeof(*vimc),
> +			GFP_KERNEL);
> +	if (!vimc)
> +		return -ENOMEM;
>   
>   	memset(&vimc->mdev, 0, sizeof(vimc->mdev));
> +	INIT_LIST_HEAD(&vimc->ents);
>   
>   	/* Link the media device within the v4l2_device */
>   	vimc->v4l2_dev.mdev = &vimc->mdev;
> @@ -277,44 +223,33 @@ static int vimc_probe(struct platform_device *pdev)
>   	vimc->mdev.dev = &pdev->dev;
>   	media_device_init(&vimc->mdev);
>   
> -	ret = vimc_register_devices(vimc);
> -	if (ret) {
> -		media_device_cleanup(&vimc->mdev);
> +	ret = vimc_register_devices(vimc, pdata);
> +	if (ret)
>   		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);
> +	struct vimc_platform_data_core *pdata = pdev->dev.platform_data;
>   
> -	dev_dbg(&pdev->dev, "remove");
> +	dev_dbg(&pdev->dev, "remove\n");
>   
> -	vimc_rm_subdevs(vimc);
> -	vimc_unregister(vimc);
> +	media_device_unregister(&vimc->mdev);
> +	media_device_cleanup(&vimc->mdev);
> +	vimc_rm_subdevs(vimc, pdata);
> +	v4l2_device_unregister(&vimc->v4l2_dev);
>   
>   	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",
>   	},
>   };
>   
> @@ -322,18 +257,9 @@ 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);
> -		return ret;
> -	}
> -
>   	ret = platform_driver_register(&vimc_pdrv);
>   	if (ret) {
> -		dev_err(&vimc_dev.pdev.dev,
> -			"platform driver registration failed (err=%d)\n", ret);
> -		platform_driver_unregister(&vimc_pdrv);
> +		pr_err("vimc init: platform driver register failed (%d)\n", ret);
>   		return ret;
>   	}
>   
> @@ -361,7 +287,6 @@ static void __exit vimc_exit(void)
>   
>   	vimc_cfs_subsys_unregister();
>   	platform_driver_unregister(&vimc_pdrv);
> -	platform_device_unregister(&vimc_dev.pdev);
>   }
>   
>   module_init(vimc_init);
> diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c
> index e461b155e514..2391ac3b5014 100644
> --- a/drivers/media/platform/vimc/vimc-debayer.c
> +++ b/drivers/media/platform/vimc/vimc-debayer.c
> @@ -486,11 +486,12 @@ static const struct v4l2_subdev_internal_ops vimc_deb_int_ops = {
>   	.release = vimc_deb_release,
>   };
>   
> -void vimc_deb_rm(struct vimc_device *vimc, struct vimc_ent_device *ved)
> +void vimc_deb_rm(struct vimc_ent_device *ved)
>   {
> -	struct vimc_deb_device *vdeb;
> +	struct vimc_deb_device *vdeb = container_of(ved,
> +						    struct vimc_deb_device,
> +						    ved);
>   
> -	vdeb = container_of(ved, struct vimc_deb_device, ved);
>   	vimc_ent_sd_unregister(ved, &vdeb->sd);
>   }
>   
> @@ -504,7 +505,7 @@ struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc,
>   	/* Allocate the vdeb struct */
>   	vdeb = kzalloc(sizeof(*vdeb), GFP_KERNEL);
>   	if (!vdeb)
> -		return NULL;
> +		return ERR_PTR(-ENOMEM);
>   
>   	/* Initialize ved and sd */
>   	ret = vimc_ent_sd_register(&vdeb->ved, &vdeb->sd, v4l2_dev,
> @@ -515,11 +516,11 @@ struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc,
>   				   &vimc_deb_int_ops, &vimc_deb_ops);
>   	if (ret) {
>   		kfree(vdeb);
> -		return NULL;
> +		return ERR_PTR(ret);
>   	}
>   
>   	vdeb->ved.process_frame = vimc_deb_process_frame;
> -	vdeb->dev = &vimc->pdev.dev;
> +	vdeb->dev = vimc->mdev.dev;
>   
>   	/* Initialize the frame format */
>   	vdeb->sink_fmt = sink_fmt_default;
> diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c
> index e5cf0073d68a..7d9726d0b39c 100644
> --- a/drivers/media/platform/vimc/vimc-scaler.c
> +++ b/drivers/media/platform/vimc/vimc-scaler.c
> @@ -345,11 +345,12 @@ static const struct v4l2_subdev_internal_ops vimc_sca_int_ops = {
>   	.release = vimc_sca_release,
>   };
>   
> -void vimc_sca_rm(struct vimc_device *vimc, struct vimc_ent_device *ved)
> +void vimc_sca_rm(struct vimc_ent_device *ved)
>   {
> -	struct vimc_sca_device *vsca;
> +	struct vimc_sca_device *vsca = container_of(ved,
> +						    struct vimc_sca_device,
> +						    ved);
>   
> -	vsca = container_of(ved, struct vimc_sca_device, ved);
>   	vimc_ent_sd_unregister(ved, &vsca->sd);
>   }
>   
> @@ -363,7 +364,7 @@ struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc,
>   	/* Allocate the vsca struct */
>   	vsca = kzalloc(sizeof(*vsca), GFP_KERNEL);
>   	if (!vsca)
> -		return NULL;
> +		return ERR_PTR(-ENOMEM);
>   
>   	/* Initialize ved and sd */
>   	ret = vimc_ent_sd_register(&vsca->ved, &vsca->sd, v4l2_dev,
> diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c
> index 22e3ad98c818..556d49f338ab 100644
> --- a/drivers/media/platform/vimc/vimc-sensor.c
> +++ b/drivers/media/platform/vimc/vimc-sensor.c
> @@ -300,12 +300,12 @@ static const struct v4l2_subdev_internal_ops vimc_sen_int_ops = {
>   	.release = vimc_sen_release,
>   };
>   
> -void vimc_sen_rm(struct vimc_device *vimc, struct vimc_ent_device *ved)
> +void vimc_sen_rm(struct vimc_ent_device *ent)
>   {
>   	struct vimc_sen_device *vsen;
>   
> -	vsen = container_of(ved, struct vimc_sen_device, ved);
> -	vimc_ent_sd_unregister(ved, &vsen->sd);
> +	vsen = container_of(ent, struct vimc_sen_device, ved);
> +	vimc_ent_sd_unregister(ent, &vsen->sd);
>   }
>   
>   /* Image Processing Controls */
> @@ -335,7 +335,7 @@ struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc,
>   	/* Allocate the vsen struct */
>   	vsen = kzalloc(sizeof(*vsen), GFP_KERNEL);
>   	if (!vsen)
> -		return NULL;
> +		return ERR_PTR(-ENOMEM);
>   
>   	v4l2_ctrl_handler_init(&vsen->hdl, 4);
>   
> @@ -369,7 +369,7 @@ struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc,
>   		goto err_free_hdl;
>   
>   	vsen->ved.process_frame = vimc_sen_process_frame;
> -	vsen->dev = &vimc->pdev.dev;
> +	vsen->dev = vimc->mdev.dev;
>   
>   	/* Initialize the frame format */
>   	vsen->mbus_format = fmt_default;
> @@ -390,7 +390,7 @@ struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc,
>   err_free_vsen:
>   	kfree(vsen);
>   
> -	return NULL;
> +	return ERR_PTR(ret);
>   }
>   
>   static void vimc_sen_create_cfs_pads(struct config_group *ent_group)
> 

thanks,
-- Shuah

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 2/5] docs: media: vimc: Documenting vimc topology configuration using configfs
  2019-09-23  9:50       ` Hans Verkuil
@ 2019-09-27  5:56         ` Andrzej Pietrasiewicz
  0 siblings, 0 replies; 20+ messages in thread
From: Andrzej Pietrasiewicz @ 2019-09-27  5:56 UTC (permalink / raw)
  To: Hans Verkuil, Dafna Hirschfeld, linux-media
  Cc: laurent.pinchart, helen.koike, ezequiel, andre.almeida, skhan,
	kernel, dafna3

Hello All,

W dniu 23.09.2019 o 11:50, Hans Verkuil pisze:
> On 9/23/19 11:29 AM, Dafna Hirschfeld wrote:
>> On Fri, 2019-09-20 at 15:39 +0200, Hans Verkuil wrote:
>>> On 9/19/19 10:32 PM, Dafna Hirschfeld wrote:
>>>> Add explanation of how to use configfs in order to create a
>>>> vimc device with a given topology.
>>>>
>>>> Signed-off-by: Dafna Hirschfeld <dafna.hirschfeld@collabora.com>
>>>> ---
>>>>   Documentation/media/v4l-drivers/vimc.dot |  28 ++-
>>>>   Documentation/media/v4l-drivers/vimc.rst | 240 ++++++++++++++++++++---
>>>>   2 files changed, 220 insertions(+), 48 deletions(-)
>>>>

When adding new sysfs or configfs directories/files one should add
appropriate entries to Documentation/ABI/*.

My feeling is that this patch should be merged with the patch actually
enabling the configfs interface, which is patch 4/5. The reason is twofold.

The first is consistency: if a documentation for an interface is added
in a different patch than the interface itself, then at the first of the
two commits in question we either have a documentation for a non-existing
interface, or an undocumented interface.

The second is that the documentation is not only for kernel developers
but also for users who don't have the faintest idea of what code is
inside the kernel, so even though patch 3/5 of this series adds
the code implementing the interface, it is not available to users
until patch 4/5.

Regards,

Andrzej

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 3/5] media: vimc: Add the implementation for the configfs api
  2019-09-19 20:32 ` [PATCH 3/5] media: vimc: Add the implementation for the configfs api Dafna Hirschfeld
  2019-09-26 19:25   ` Shuah Khan
@ 2019-09-27  7:55   ` Andrzej Pietrasiewicz
  1 sibling, 0 replies; 20+ messages in thread
From: Andrzej Pietrasiewicz @ 2019-09-27  7:55 UTC (permalink / raw)
  To: Dafna Hirschfeld, linux-media
  Cc: laurent.pinchart, helen.koike, ezequiel, andre.almeida, skhan,
	hverkuil, kernel, dafna3

Hi Dafna,

Please see inline.

W dniu 19.09.2019 o 22:32, Dafna Hirschfeld pisze:
> Add the code that implements the usage of configfs in order
> to create and configure a device topology from userspace.
> The code is only added in this patch but is not used.
> It will be used in next patch in the series.
> 
> Signed-off-by: Helen Koike <helen.koike@collabora.com>
> [refactored for upstream]
> Signed-off-by: Dafna Hirschfeld <dafna.hirschfeld@collabora.com>
> ---
>   drivers/media/platform/vimc/Kconfig         |   9 +-
>   drivers/media/platform/vimc/Makefile        |   2 +-
>   drivers/media/platform/vimc/vimc-capture.c  |  21 +
>   drivers/media/platform/vimc/vimc-common.h   |  46 ++
>   drivers/media/platform/vimc/vimc-configfs.c | 656 ++++++++++++++++++++
>   drivers/media/platform/vimc/vimc-configfs.h |  41 ++
>   drivers/media/platform/vimc/vimc-core.c     |  21 +-
>   drivers/media/platform/vimc/vimc-debayer.c  |  22 +
>   drivers/media/platform/vimc/vimc-scaler.c   |  26 +-
>   drivers/media/platform/vimc/vimc-sensor.c   |  21 +
>   10 files changed, 856 insertions(+), 9 deletions(-)
>   create mode 100644 drivers/media/platform/vimc/vimc-configfs.c
>   create mode 100644 drivers/media/platform/vimc/vimc-configfs.h
> 
> diff --git a/drivers/media/platform/vimc/Kconfig b/drivers/media/platform/vimc/Kconfig
> index bd221d3e1a4a..6d7836d58ef4 100644
> --- a/drivers/media/platform/vimc/Kconfig
> +++ b/drivers/media/platform/vimc/Kconfig
> @@ -1,15 +1,14 @@
>   # SPDX-License-Identifier: GPL-2.0-only
>   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
>   	help
> -	  Skeleton driver for Virtual Media Controller
> +	  Virtual Media Controller Driver
>   
> -	  This driver can be compared to the vivid driver for emulating
> +	  This driver emulates
>   	  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 the configfs API.
>   
>   	  When in doubt, say N.
> diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile
> index a53b2b532e9f..eb03d487f308 100644
> --- a/drivers/media/platform/vimc/Makefile
> +++ b/drivers/media/platform/vimc/Makefile
> @@ -1,6 +1,6 @@
>   # SPDX-License-Identifier: GPL-2.0
>   vimc-y := vimc-core.o vimc-common.o vimc-streamer.o vimc-capture.o \
> -		vimc-debayer.o vimc-scaler.o vimc-sensor.o
> +		vimc-debayer.o vimc-scaler.o vimc-sensor.o  vimc-configfs.o
>   
>   obj-$(CONFIG_VIDEO_VIMC) += vimc.o
>   
> diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c
> index 602f80323031..5cc419e76ce7 100644
> --- a/drivers/media/platform/vimc/vimc-capture.c
> +++ b/drivers/media/platform/vimc/vimc-capture.c
> @@ -9,6 +9,7 @@
>   #include <media/videobuf2-core.h>
>   #include <media/videobuf2-vmalloc.h>
>   
> +#include "vimc-configfs.h"
>   #include "vimc-common.h"
>   #include "vimc-streamer.h"
>   
> @@ -488,3 +489,23 @@ struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc,
>   
>   	return NULL;
>   }
> +
> +static void vimc_cap_create_cfs_pads(struct config_group *ent_group)
> +{
> +	vimc_cfs_add_sink_pad_api(ent_group, 0, VIMC_CFS_SINK_PAD_NUM(0));
> +}
> +
> +struct vimc_cfs_drv vimc_cap_cfs_drv = {
> +	.name = VIMC_CAP_NAME,
> +	.create_pads = vimc_cap_create_cfs_pads,
> +};
> +
> +__exit void vimc_cap_exit(void)
> +{
> +	vimc_cfs_drv_unregister(&vimc_cap_cfs_drv);
> +}
> +
> +__init void vimc_cap_init(void)
> +{
> +	vimc_cfs_drv_register(&vimc_cap_cfs_drv);
> +}
> diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h
> index 698db7c07645..e0e3b3ab7b19 100644
> --- a/drivers/media/platform/vimc/vimc-common.h
> +++ b/drivers/media/platform/vimc/vimc-common.h
> @@ -14,6 +14,7 @@
>   #include <media/v4l2-device.h>
>   
>   #define VIMC_PDEV_NAME "vimc"
> +#define VIMC_MAX_NAME_LEN V4L2_SUBDEV_NAME_SIZE
>   
>   /* VIMC-specific controls */
>   #define VIMC_CID_VIMC_BASE		(0x00f00000 | 0xf000)
> @@ -31,6 +32,11 @@
>   #define VIMC_IS_SRC(pad)	(pad)
>   #define VIMC_IS_SINK(pad)	(!(pad))
>   
> +#define VIMC_DEB_NAME "vimc-debayer"
> +#define VIMC_SEN_NAME "vimc-sensor"
> +#define VIMC_SCA_NAME "vimc-scaler"
> +#define VIMC_CAP_NAME "vimc-capture"
> +
>   /**
>    * struct vimc_colorimetry_clamp - Adjust colorimetry parameters
>    *
> @@ -72,6 +78,18 @@ struct vimc_platform_data {
>   	char entity_name[32];
>   };
>   
> +/**
> + * struct vimc_platform_data_core - platform data to the core
> + *
> + * @ents: list of vimc_entity_data objects allocated by the configfs
> + * @links: list of vimc_links objects allocated by the configfs
> + *
> + */
> +struct vimc_platform_data_core {
> +	struct list_head *ents;
> +	struct list_head *links;
> +};

What about changing *ents to ents and *links to links?
You seem to need this structure as platform data. No problem
with that. But then in struct vimc_cfs_device you in fact
duplicate ents and links even though the pdata member contains
almost the same kind of information. My impression is that you
need the above struct only to satisfy the requirements of
platform_device_register_data(), which wants to copy the passed
platform data. Is there any reason platform_device_register()
cannot be used instead with pdev->dev.platform_data set to the
address of the pdev member of struct vimc_cfs_device? This way
we would avoid a copy being made and we avoid duplicating ents
and links.

> +
>   /**
>    * struct vimc_pix_map - maps media bus code with v4l2 pixel format
>    *
> @@ -92,6 +110,8 @@ struct vimc_pix_map {
>   /**
>    * struct vimc_ent_device - core struct that represents a node in the topology
>    *
> + * @name:		the name of the entity
> + * @drv_name:		the name of the driver of the entity
>    * @ent:		the pointer to struct media_entity for the node
>    * @pads:		the list of pads of the node
>    * @process_frame:	callback send a frame to that node
> @@ -108,12 +128,30 @@ struct vimc_pix_map {
>    * media_entity
>    */
>   struct vimc_ent_device {
> +	char name[VIMC_MAX_NAME_LEN];
> +	char drv_name[VIMC_MAX_NAME_LEN];
>   	struct media_entity *ent;
>   	struct media_pad *pads;
>   	void * (*process_frame)(struct vimc_ent_device *ved,
>   				const void *frame);
>   	void (*vdev_get_format)(struct vimc_ent_device *ved,
>   			      struct v4l2_pix_format *fmt);
> +	struct list_head list;
> +};
> +
> +struct vimc_entity_data {
> +	char name[VIMC_MAX_NAME_LEN];
> +	char drv_name[VIMC_MAX_NAME_LEN];
> +	struct list_head list;
> +};
> +
> +struct vimc_link {
> +	char source_name[VIMC_MAX_NAME_LEN];
> +	char sink_name[VIMC_MAX_NAME_LEN];
> +	u16 source_pad;
> +	u16 sink_pad;
> +	u32 flags;
> +	struct list_head list;

Is the "list" member meant to be a head of some list, or an entry
of some other list? If entry, can you change it to "entry", please?
This comment refers to all other occurences of "struct list_head list;".

>   };

Can you document these structures, too?

>   
>   /**
> @@ -156,18 +194,26 @@ struct vimc_ent_config {
>   struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc,
>   				     const char *vcfg_name);
>   void vimc_cap_rm(struct vimc_device *vimc, struct vimc_ent_device *ved);
> +void vimc_cap_init(void);
> +void vimc_cap_exit(void);
>   
>   struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc,
>   				     const char *vcfg_name);
>   void vimc_deb_rm(struct vimc_device *vimc, struct vimc_ent_device *ved);
> +void vimc_deb_init(void);
> +void vimc_deb_exit(void);
>   
>   struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc,
>   				     const char *vcfg_name);
>   void vimc_sca_rm(struct vimc_device *vimc, struct vimc_ent_device *ved);
> +void vimc_sca_init(void);
> +void vimc_sca_exit(void);
>   
>   struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc,
>   				     const char *vcfg_name);
>   void vimc_sen_rm(struct vimc_device *vimc, struct vimc_ent_device *ved);
> +void vimc_sen_init(void);
> +void vimc_sen_exit(void);
>   
>   /**
>    * vimc_pads_init - initialize pads
> diff --git a/drivers/media/platform/vimc/vimc-configfs.c b/drivers/media/platform/vimc/vimc-configfs.c
> new file mode 100644
> index 000000000000..933aece0bb5f
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-configfs.c
> @@ -0,0 +1,656 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * vimc-configfs.c Virtual Media Controller Driver
> + *
> + * Copyright (C) 2018 Helen Koike <helen.koike@collabora.com>
> + */
> +
> +#include <linux/platform_device.h>
> +
> +#include "vimc-common.h"
> +#include "vimc-configfs.h"
> +
> +#define CHAR_SEPARATOR ':'
> +#define CFS_SUBSYS_NAME "vimc"
> +#define MAX_PAD_DIGI_NUM 4
> +
> +#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: %s:" pr_fmt(fmt), (ci)->ci_name, __func__, ##__VA_ARGS__)
> +#define cg_dbg(cg, ...) ci_dbg(&(cg)->cg_item, ##__VA_ARGS__)
> +
> +#define IS_PLUGGED(cfs) (!!(cfs)->pdev)
> +
> +// currently there is no entity with more than two pads, this will
> +// change when adding the splitter entity

This comment style is against the coding style. Please use /* */.

> +#define VIMC_ENT_MAX_PADS 2
> +
> +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;

If you already allocate this struct statically, why not initialize
the configfs subsystem statically? Please see drivers/usb/gadget/configfs.c.
The "drvs" list can also be initialized with LIST_HEAD_INIT.

> +
> +/* Structure which describes the whole topology */
> +struct vimc_cfs_device {
> +	struct list_head ents;
> +	struct list_head links;
> +	struct platform_device *pdev;
> +	struct vimc_platform_data_core pdata;
> +	struct config_group gdev;
> +};
> +
> +/* Structure which describes individual configuration for each entity */
> +struct vimc_cfs_ent {
> +	struct vimc_entity_data ent;
> +	struct config_group cg;
> +	struct config_group pad_groups[VIMC_ENT_MAX_PADS];
> +};
> +
> +struct vimc_cfs_link {
> +	struct vimc_link link;
> +	struct config_item ci;
> +};

Please consistently document the structs, at least with a simple /* .... */.

> +
> +void vimc_cfs_drv_register(struct vimc_cfs_drv *c_drv)
> +{
> +	pr_debug("%s: adding driver %s\n", __func__, c_drv->name);
> +	list_add(&c_drv->list, &vimc_cfs_subsys.drvs);
> +}
> +EXPORT_SYMBOL_GPL(vimc_cfs_drv_register);

Are the EXPORT_SYMBOL_GPL() needed provided that all these symbols
are used in the same vimc.ko module?

> +
> +void vimc_cfs_drv_unregister(struct vimc_cfs_drv *c_drv)
> +{
> +	pr_debug("%s: removing driver %s\n", __func__, c_drv->name);
> +	list_del(&c_drv->list);
> +}
> +EXPORT_SYMBOL_GPL(vimc_cfs_drv_unregister);
> +
> +/* --------------------------------------------------------------------------
> + * Platform Device builders
> + */
> +
> +static void vimc_cfs_device_unplug(struct vimc_cfs_device *cfs)
> +{
> +	dev_dbg(&cfs->pdev->dev, "Unplugging device\n");
> +	platform_device_unregister(cfs->pdev);
> +
> +	cfs->pdev = NULL;
> +}
> +
> +static int vimc_cfs_device_plug(struct vimc_cfs_device *cfs)
> +{
> +	cg_dbg(&cfs->gdev, "Plugging device\n");
> +
> +	if (list_empty(&cfs->ents)) {
> +		cg_warn(&cfs->gdev,
> +			"At least one entity is required to plug the device\n");
> +		return -EINVAL;
> +	}
> +
> +	cfs->pdev = platform_device_register_data(NULL, "vimc-core",
> +						  PLATFORM_DEVID_AUTO,
> +						  &cfs->pdata,
> +						  sizeof(cfs->pdata));

Please see above, platform_device_register_data() wants to copy
cfs->pdata. Do we need that copy?

> +	if (IS_ERR(cfs->pdev)) {
> +		int ret = PTR_ERR(cfs->pdev);
> +
> +		cfs->pdev = NULL;
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +/* --------------------------------------------------------------------------
> + * Links
> + */
> +
> +static ssize_t vimc_cfs_link_store_flag(struct config_item *item,
> +					const char *buf,
> +					size_t size, u32 flag)
> +{
> +	struct vimc_cfs_link *c_link =
> +		container_of(item, struct vimc_cfs_link, ci);
> +
> +	if (!strncmp(buf, "on\n", 4) || !strncmp(buf, "1\n", 3)) {
> +		c_link->link.flags |= MEDIA_LNK_FL_ENABLED;
> +		return strlen(buf);
> +	} else if (!strncmp(buf, "off\n", 5) || !strncmp(buf, "0\n", 3)) {
> +		c_link->link.flags &= ~MEDIA_LNK_FL_ENABLED;
> +		return strlen(buf);
> +	}

What about using strtobool()? [which is a wrapper around kstrtobool()]:

> +	return -EINVAL;
> +}
> +
> +static ssize_t vimc_cfs_link_show_flag(struct config_item *item,
> +				       char *buf, u32 flag)
> +{
> +	struct vimc_cfs_link *c_link = container_of(item,
> +						    struct vimc_cfs_link, ci);
> +
> +	if (c_link->link.flags & flag)
> +		strscpy(buf, "on\n", 4);
> +	else
> +		strscpy(buf, "off\n", 5);
> +	return strlen(buf);
> +}
> +
> +static ssize_t vimc_cfs_link_enabled_store(struct config_item *item,
> +					   const char *buf,
> +					   size_t size)
> +{
> +	return vimc_cfs_link_store_flag(item, buf, size, MEDIA_LNK_FL_ENABLED);
> +}
> +
> +
> +static ssize_t vimc_cfs_link_enabled_show(struct config_item *item,
> +					  char *buf)
> +{
> +	return vimc_cfs_link_show_flag(item, buf, MEDIA_LNK_FL_ENABLED);
> +}
> +
> +CONFIGFS_ATTR(vimc_cfs_link_, enabled);
> +
> +static ssize_t vimc_cfs_link_immutable_show(struct config_item *item,
> +						 char *buf)
> +{
> +	return vimc_cfs_link_show_flag(item, buf, MEDIA_LNK_FL_IMMUTABLE);
> +}
> +
> +static ssize_t vimc_cfs_link_immutable_store(struct config_item *item,
> +						   const char *buf, size_t size)
> +{
> +	return vimc_cfs_link_store_flag(item, buf, size,
> +					MEDIA_LNK_FL_IMMUTABLE);
> +}
> +
> +CONFIGFS_ATTR(vimc_cfs_link_, immutable);
> +
> +static void vimc_cfs_fill_link_data(struct config_item *src,
> +				struct config_item *target)
> +{
> +	struct config_item *src_ent_ci = src->ci_parent->ci_parent;
> +	struct config_item *trgt_ent_ci = target->ci_parent;
> +	struct vimc_cfs_link *c_link =
> +			container_of(src, struct vimc_cfs_link, ci);
> +	struct vimc_cfs_ent *vimc_src_ent = container_of(src_ent_ci,
> +							 struct vimc_cfs_ent,
> +							 cg.cg_item);
> +	struct vimc_cfs_ent *vimc_trgt_ent = container_of(trgt_ent_ci,
> +							 struct vimc_cfs_ent,
> +							 cg.cg_item);
> +	struct vimc_cfs_device *cfs = container_of(src_ent_ci->ci_parent,
> +							 struct vimc_cfs_device,
> +							 gdev.cg_item);

By accessing ci_parent you are effectively navigating the configfs hierarchy
(the hierarchy which mirrors the way config_items and config_groups are arranged
in the filesystem). This kind of access must be under the subsystem mutex as
per Documentation/filesystems/configfs/configfs.txt. This comments refers
also to other occurences of navigating the configfs hierarchy.

> +
> +	if (IS_PLUGGED(cfs))
> +		vimc_cfs_device_unplug(cfs);
> +
> +	/* src and target validation already done in the allow_link callback,
> +	 * so there is no need to check sscanf result
> +	 */
> +	sscanf(src->ci_parent->ci_name, VIMC_CFS_SRC_PAD "%hu",
> +	       &c_link->link.source_pad);
> +	sscanf(target->ci_parent->ci_name, VIMC_CFS_SINK_PAD "%hu",
> +	       &c_link->link.sink_pad);

Ditto.

> +
> +	strscpy(c_link->link.source_name, vimc_src_ent->ent.name,
> +		sizeof(c_link->link.source_name));
> +	strscpy(c_link->link.sink_name, vimc_trgt_ent->ent.name,
> +		sizeof(c_link->link.sink_name));
> +
> +	cg_dbg(&cfs->gdev, "creating link %s:%u->%s:%u\n",
> +			c_link->link.source_name, c_link->link.source_pad,
> +			c_link->link.sink_name, c_link->link.sink_pad);
> +
> +	list_add(&c_link->link.list, &cfs->links);

I'd prefer list_add(&c_link->link.entry, &cfs->links);
as pointed out above.

> +}
> +
> +static void vimc_cfs_drop_link(struct config_item *src,
> +			       struct config_item *target)
> +{
> +	struct vimc_cfs_link *c_link = container_of(src,
> +						    struct vimc_cfs_link, ci);
> +
> +	ci_dbg(&c_link->ci, "dropping link %s:%u->%s:%u\n",
> +	       c_link->link.source_name, c_link->link.source_pad,
> +	       c_link->link.sink_name, c_link->link.sink_pad);
> +
> +	c_link->link.source_pad = 0;
> +	c_link->link.sink_pad = 0;
> +	memset(c_link->link.source_name, 0, sizeof(c_link->link.source_name));
> +	memset(c_link->link.sink_name, 0, sizeof(c_link->link.sink_name));
> +	list_del(&c_link->link.list);
> +}
> +
> +int vimc_cfs_allow_link(struct config_item *src, struct config_item *target)
> +{
> +	struct config_item *s = src;
> +	struct config_item *t = target;
> +	struct config_item *src_ent_ci, *trgt_ent_ci;
> +	int i;
> +	int ret = 0;
> +	struct configfs_subsystem *subsys = &vimc_cfs_subsys.subsys;
> +
> +	if (strncmp(target->ci_name, VIMC_CFS_SINK_PAD,
> +		    sizeof(VIMC_CFS_SINK_PAD) - 1)) {
> +		ci_warn(src, "link target (%s) does not start with '%s'\n",
> +			target->ci_name, VIMC_CFS_SINK_PAD);
> +		return -EINVAL;
> +	}
> +
> +	mutex_lock(&subsys->su_mutex);
> +	for (i = 0; i < 3; i++)
> +		s = s->ci_parent;

Can you guarantee that there exist all 3 ancestors? If yes, then why not
s = s->ci_parent->ci_parent->ci_parent;

Also please add a comment what 3 levels up means (e.g. "access vimc device's
configfs root" /if really accessing vimc device's configfs root/)

> +
> +	for (i = 0; i < 2; i++) {
> +		t = t->ci_parent;
> +		if (!t) {
> +			ci_warn(src, "link target (%s) is not a sink pad\n",
> +				target->ci_name);
> +			ret = -EINVAL;
> +			goto end;
> +		}
> +	}

Please comment what accessing 2 levels up means.

> +
> +	if (s != t) {
> +		ci_warn(src, "not allow linking between different vimcs devices: (%s) and (%s)\n",

"linking between different vimc devices: (%s) and (%s) not allowed" ?

> +			s->ci_name, t->ci_name);
> +		ret = -EINVAL;
> +		goto end;
> +	}
> +	src_ent_ci = src->ci_parent->ci_parent;
> +	trgt_ent_ci = target->ci_parent;
> +	if (src_ent_ci == trgt_ent_ci) {
> +		ci_warn(src, "both pads belong to the same entity (%s) - this is not allowed\n",

"linking pads of the same entity (%s) not allowed" ?

> +				src_ent_ci->ci_name);
> +		ret = -EINVAL;
> +		goto end;
> +	}
> +
> +	vimc_cfs_fill_link_data(src, target);
> +end:
> +	mutex_unlock(&subsys->su_mutex);
> +	return ret;
> +}
> +
> +static void vimc_cfs_prepare_link_release(struct config_item *item)

I'd drop the "prepare_" substring from the function name.

> +{
> +	struct vimc_cfs_link *c_link = container_of(item,
> +						    struct vimc_cfs_link, ci);
> +
> +	ci_dbg(item, "releasing link '%s'", item->ci_name);
> +	kfree(c_link);
> +}
> +static struct configfs_attribute *vimc_cfs_link_attrs[] = {
> +	&vimc_cfs_link_attr_enabled,
> +	&vimc_cfs_link_attr_immutable,
> +	NULL,
> +};
> +
> +static struct configfs_item_operations vimc_cfs_allow_link_item_ops = {

I'd drop the "allow_" substring from the struct name, because it is
not only allow, but also actually drop. And release.

> +	.allow_link = vimc_cfs_allow_link,
> +	.drop_link = vimc_cfs_drop_link,
> +	.release = vimc_cfs_prepare_link_release,
> +};
> +
> +struct config_item_type vimc_cfs_allow_link_type = {

Ditto.

> +	.ct_owner = THIS_MODULE,
> +	.ct_item_ops = &vimc_cfs_allow_link_item_ops,
> +	.ct_attrs = vimc_cfs_link_attrs,
> +};
> +
> +/* --------------------------------------------------------------------------
> + * Source pad instance
> + */
> +
> +static void vimc_cfs_src_pad_prepare_link_drop_item(

Why "prepare_link_" ?

> +		struct config_group *src_pad_group,
> +		struct config_item *link_item)
> +{
> +
> +	struct config_item *cfs_item;
> +	struct vimc_cfs_device *cfs;
> +
> +	cfs_item = src_pad_group->cg_item.ci_parent->ci_parent;
> +	cfs = container_of(cfs_item->ci_parent, struct vimc_cfs_device,
> +			   gdev.cg_item);

Hierarchy navigation.

> +	cg_dbg(&cfs->gdev, "dropping prepared link '%s'\n",
> +	       link_item->ci_name);
> +	if (IS_PLUGGED(cfs))
> +		vimc_cfs_device_unplug(cfs);
> +	config_item_put(link_item);
> +}
> +
> +static struct config_item *vimc_cfs_src_pad_prepare_link_make_item(
> +			   struct config_group *group,
> +			   const char *name)
> +{
> +	struct vimc_cfs_link *c_link = kzalloc(sizeof(*c_link), GFP_KERNEL);
> +
> +	cg_dbg(group, "link name is '%s'\n", name);
> +	config_item_init_type_name(&c_link->ci, name,
> +				   &vimc_cfs_allow_link_type);
> +	return &c_link->ci;
> +}
> +struct config_item_type vimc_cfs_empty_type = {
> +	.ct_owner = THIS_MODULE,
> +};
> +
> +static struct configfs_group_operations vimc_cfs_src_pad_ops = {
> +	.make_item = vimc_cfs_src_pad_prepare_link_make_item,
> +	.drop_item = vimc_cfs_src_pad_prepare_link_drop_item
> +};
> +
> +struct config_item_type vimc_cfs_src_pad_type = {
> +	.ct_owner = THIS_MODULE,
> +	.ct_group_ops = &vimc_cfs_src_pad_ops,
> +};
> +
> +/* --------------------------------------------------------------------------
> + * Device instance
> + */
> +
> +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);
> +
> +	ci_dbg(item, "releasing entity '%s' of driver '%s'",
> +	       c_ent->ent.name,
> +	       c_ent->ent.drv_name);
> +	kfree(c_ent);
> +}
> +
> +static struct configfs_item_operations vimc_cfs_ent_item_ops = {
> +	.release	= vimc_cfs_ent_release,
> +};
> +
> +struct config_item_type vimc_cfs_ent_type = {
> +	.ct_owner = THIS_MODULE,
> +	.ct_item_ops = &vimc_cfs_ent_item_ops,
> +};
> +
> +void vimc_cfs_add_sink_pad_api(struct config_group *ent_group,
> +					int pad_idx, const char *name)

Why the "_api"?

> +{
> +	struct vimc_cfs_ent *c_ent = container_of(ent_group,
> +						  struct vimc_cfs_ent, cg);
> +
> +	config_group_init_type_name(&c_ent->pad_groups[pad_idx], name,
> +				    &vimc_cfs_empty_type);
> +	configfs_add_default_group(&c_ent->pad_groups[pad_idx], ent_group);
> +}
> +EXPORT_SYMBOL_GPL(vimc_cfs_add_sink_pad_api);
> +
> +void vimc_cfs_add_source_pad_api(struct config_group *ent_group,
> +					  int pad_idx, const char *name)
> +{
> +	struct vimc_cfs_ent *c_ent = container_of(ent_group,
> +						  struct vimc_cfs_ent, cg);
> +
> +	config_group_init_type_name(&c_ent->pad_groups[pad_idx], name,
> +				    &vimc_cfs_src_pad_type);
> +	configfs_add_default_group(&c_ent->pad_groups[pad_idx], ent_group);
> +}
> +EXPORT_SYMBOL_GPL(vimc_cfs_add_source_pad_api);
> +
> +static void vimc_cfs_dev_drop_ent_item(struct config_group *dev_group,
> +					   struct config_item *ent_item)
> +{
> +	struct vimc_cfs_ent *c_ent = container_of(ent_item, struct vimc_cfs_ent,
> +						  cg.cg_item);
> +	struct vimc_cfs_device *cfs = container_of(dev_group,
> +						   struct vimc_cfs_device,
> +						   gdev);
> +
> +	if (IS_PLUGGED(cfs))
> +		vimc_cfs_device_unplug(cfs);
> +
> +	cg_dbg(&cfs->gdev, "dropping entity '%s' of driver '%s'",
> +	       c_ent->ent.name, c_ent->ent.drv_name);
> +	list_del(&c_ent->ent.list);

Can something in vimc race with this removal?

> +	config_item_put(ent_item);
> +}
> +
> +static struct config_group *vimc_cfs_dev_make_ent_group(
> +			struct config_group *group, const char *name)
> +{
> +	struct vimc_cfs_device *cfs = container_of(group,
> +						   struct vimc_cfs_device,
> +						   gdev);
> +	char *ent_name, *sep = strchr(name, CHAR_SEPARATOR);
> +	struct vimc_cfs_ent *c_ent;
> +	struct vimc_entity_data *ent;
> +	size_t drv_namelen;
> +	struct vimc_cfs_drv *c_drv = NULL;
> +
> +	cg_dbg(group, "trying to make entity '%s'\n", name);
> +	if (IS_PLUGGED(cfs))
> +		vimc_cfs_device_unplug(cfs);

Perhaps delay this until after syntax is checked and memory allocated?
Actually, it seems until the beginning of body of the first nested if
inside list_for_each_entry below.

Otherwise if some of those fail then the side effect of invoking this
function will be vimc_cfs_device_unplug(cfs); is it expected?

> +
> +	/* Parse format "drv_name:ent_name" */
> +	if (!sep) {
> +		cg_warn(&cfs->gdev,
> +			"Could not find separator '%c'\n", CHAR_SEPARATOR);
> +		goto syntax_error;
> +	}
> +	drv_namelen = (size_t)(sep - name);
> +	ent_name = &sep[1];
> +	if (!*ent_name || !drv_namelen) {
> +		cg_warn(&cfs->gdev,
> +			"%s: Driver name and entity name can't be empty.\n",
> +		       name);
> +		goto syntax_error;
> +	}
> +	if (drv_namelen >= VIMC_MAX_NAME_LEN) {
> +		cg_err(&cfs->gdev,
> +		       "%s: Driver name length should be less than %u.\n",
> +		       name, VIMC_MAX_NAME_LEN);
> +		goto syntax_error;
> +	}
> +	list_for_each_entry(ent, &cfs->ents, list) {
> +		if (!strncmp(ent->name, ent_name, sizeof(ent->name))) {
> +			cg_err(&cfs->gdev, "entity `%s` already exist\n",
> +			       ent->name);
> +			goto syntax_error;
> +		}
> +	}
> +
> +	c_ent = kzalloc(sizeof(*c_ent), GFP_KERNEL);
> +	if (!c_ent)
> +		return ERR_PTR(-ENOMEM);
> +
> +	strscpy(c_ent->ent.drv_name, name, drv_namelen + 1);
> +	strscpy(c_ent->ent.name, ent_name, sizeof(c_ent->ent.name));
> +
> +	/* Configure group */
> +
> +	/* TODO: add support for hotplug at entity level */
> +	list_for_each_entry(c_drv, &vimc_cfs_subsys.drvs, list) {
> +		if (!strcmp(c_ent->ent.drv_name, c_drv->name)) {
> +			config_group_init_type_name(&c_ent->cg, name,
> +						    &vimc_cfs_ent_type);
> +			if (c_drv->create_pads)
> +				c_drv->create_pads(&c_ent->cg);
> +			list_add(&c_ent->ent.list, &cfs->ents);
> +			return &c_ent->cg;
> +		}
> +	}
> +	cg_warn(&cfs->gdev, "entity type %s not found\n", c_ent->ent.drv_name);
> +	kfree(c_ent);
> +	return ERR_PTR(-EINVAL);
> +
> +syntax_error:
> +	cg_err(&cfs->gdev,
> +		"couldn't create entity %s, wrong syntax.", name);
> +	return ERR_PTR(-EINVAL);
> +}
> +
> +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_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;
> +}

Is this helper really needed? The below function is short enough
to accommodate the above if statement.

> +
> +static ssize_t vimc_cfs_dev_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_, hotplug);
> +
> +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);
> +
> +	ci_dbg(item, "releasing dev %s\n", item->ci_name);
> +	kfree(cfs);
> +}
> +
> +static struct configfs_group_operations vimc_cfs_dev_group_ops = {
> +	.make_group = vimc_cfs_dev_make_ent_group,
> +	.drop_item = vimc_cfs_dev_drop_ent_item,
> +};
> +
> +static struct configfs_item_operations vimc_cfs_dev_item_ops = {
> +	.release = vimc_cfs_dev_release,
> +};
> +
> +static struct configfs_attribute *vimc_cfs_dev_attrs[] = {
> +	&vimc_cfs_dev_attr_hotplug,
> +	NULL,
> +};
> +
> +static struct config_item_type vimc_cfs_dev_type = {
> +	.ct_group_ops = &vimc_cfs_dev_group_ops,
> +	.ct_item_ops = &vimc_cfs_dev_item_ops,
> +	.ct_attrs = vimc_cfs_dev_attrs,
> +	.ct_owner = THIS_MODULE,
> +};
> +
> +/* --------------------------------------------------------------------------
> + * Subsystem
> + * --------------------------------------------------------------------------
> + */
> +
> +static void vimc_cfs_subsys_drop_dev_item(struct config_group *group,
> +				   struct config_item *item)
> +{
> +	struct vimc_cfs_device *cfs = container_of(to_config_group(item),
> +						   struct vimc_cfs_device,
> +						   gdev);
> +
> +	if (IS_PLUGGED(cfs))
> +		vimc_cfs_device_unplug(cfs);
> +	cg_dbg(&cfs->gdev, "dropping subsys '%s'", item->ci_name);
> +	config_item_put(item);
> +}
> +
> +static struct config_group *vimc_cfs_subsys_make_dev_group(
> +				struct config_group *group, const char *name)
> +{
> +	struct vimc_cfs_device *cfs = kzalloc(sizeof(*cfs), GFP_KERNEL);
> +
> +	if (!cfs)
> +		return ERR_PTR(-ENOMEM);
> +
> +	cg_dbg(&cfs->gdev, "making dev group '%s'", name);
> +	/* Configure platform data */
> +	INIT_LIST_HEAD(&cfs->ents);
> +	INIT_LIST_HEAD(&cfs->links);
> +	cfs->pdata.links = &cfs->links;
> +	cfs->pdata.ents = &cfs->ents;

See the comment in the beginning about platofrm data.

> +
> +	config_group_init_type_name(&cfs->gdev, name, &vimc_cfs_dev_type);
> +
> +	return &cfs->gdev;
> +}
> +
> +static struct configfs_group_operations vimc_cfs_subsys_group_ops = {
> +	.make_group	= vimc_cfs_subsys_make_dev_group,
> +	.drop_item	= vimc_cfs_subsys_drop_dev_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);

See one of the comments above for static initialization of the subsystem.

> +	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..0d3afdb31fdd
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-configfs.h
> @@ -0,0 +1,41 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * vimc-configfs.h Virtual Media Controller Driver
> + *
> + * Copyright (C) 2018 Helen Koike <helen.koike@collabora.com>
> + */
> +
> +#ifndef _VIMC_CONFIGFS_H_
> +#define _VIMC_CONFIGFS_H_
> +
> +#include <linux/configfs.h>
> +
> +#define VIMC_CFS_SRC_PAD "pad:source:"
> +#define VIMC_CFS_SINK_PAD "pad:sink:"
> +
> +#define VIMC_CFS_SRC_PAD_NUM(n) "pad:source:" #n
> +#define VIMC_CFS_SINK_PAD_NUM(n) "pad:sink:" #n
> +
> +extern struct config_item_type vimc_default_cfs_pad_type;
> +
> +void vimc_cfs_add_source_pad_api(struct config_group *ent_group,
> +					int pad_idx, const char *name);
> +
> +void vimc_cfs_add_sink_pad_api(struct config_group *ent_group,
> +				      int pad_idx, const char *name);
> +struct vimc_cfs_drv {
> +	const char *name;
> +	struct list_head list;

Entry or list?

> +
> +	void (*const create_pads)(struct config_group *parent);
> +};
> +
> +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 6e3e5c91ae39..476bad2cb2de 100644
> --- a/drivers/media/platform/vimc/vimc-core.c
> +++ b/drivers/media/platform/vimc/vimc-core.c
> @@ -15,6 +15,8 @@
>   
>   #define VIMC_MDEV_MODEL_NAME "VIMC MDEV"
>   
> +#include "vimc-configfs.h"
> +
>   #define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) {	\
>   	.src_ent = src,						\
>   	.src_pad = srcpad,					\
> @@ -335,13 +337,30 @@ static int __init vimc_init(void)
>   		return ret;
>   	}
>   
> +	ret = vimc_cfs_subsys_register();
> +	if (ret) {
> +		pr_err("%s: configfs subsys registration failed (%d)\n",
> +		       __func__, ret);
> +		platform_driver_unregister(&vimc_pdrv);
> +		return ret;
> +	}
> +
> +	vimc_sen_init();
> +	vimc_deb_init();
> +	vimc_sca_init();
> +	vimc_cap_init();
>   	return 0;
>   }
>   
>   static void __exit vimc_exit(void)
>   {
> -	platform_driver_unregister(&vimc_pdrv);
> +	vimc_sen_exit();
> +	vimc_deb_exit();
> +	vimc_sca_exit();
> +	vimc_cap_exit();
>   
> +	vimc_cfs_subsys_unregister();
> +	platform_driver_unregister(&vimc_pdrv);
>   	platform_device_unregister(&vimc_dev.pdev);
>   }
>   
> diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c
> index feac47d79449..e461b155e514 100644
> --- a/drivers/media/platform/vimc/vimc-debayer.c
> +++ b/drivers/media/platform/vimc/vimc-debayer.c
> @@ -12,6 +12,7 @@
>   #include <media/v4l2-subdev.h>
>   
>   #include "vimc-common.h"
> +#include "vimc-configfs.h"
>   
>   static unsigned int deb_mean_win_size = 3;
>   module_param(deb_mean_win_size, uint, 0000);
> @@ -533,3 +534,24 @@ struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc,
>   
>   	return &vdeb->ved;
>   }
> +
> +static void vimc_deb_create_cfs_pads(struct config_group *ent_group)
> +{
> +	vimc_cfs_add_source_pad_api(ent_group, 0, VIMC_CFS_SRC_PAD_NUM(1));
> +	vimc_cfs_add_sink_pad_api(ent_group, 1, VIMC_CFS_SINK_PAD_NUM(0));
> +}
> +
> +struct vimc_cfs_drv vimc_deb_cfs_drv = {
> +	.name = VIMC_DEB_NAME,
> +	.create_pads = vimc_deb_create_cfs_pads,
> +};
> +
> +__exit void vimc_deb_exit(void)
> +{
> +	vimc_cfs_drv_unregister(&vimc_deb_cfs_drv);
> +}
> +
> +__init void vimc_deb_init(void)
> +{
> +	vimc_cfs_drv_register(&vimc_deb_cfs_drv);
> +}
> diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c
> index a6a3cc5be872..e5cf0073d68a 100644
> --- a/drivers/media/platform/vimc/vimc-scaler.c
> +++ b/drivers/media/platform/vimc/vimc-scaler.c
> @@ -10,6 +10,7 @@
>   #include <linux/v4l2-mediabus.h>
>   #include <media/v4l2-subdev.h>
>   
> +#include "vimc-configfs.h"
>   #include "vimc-common.h"
>   
>   static unsigned int sca_mult = 3;
> @@ -373,14 +374,35 @@ struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc,
>   				   &vimc_sca_int_ops, &vimc_sca_ops);
>   	if (ret) {
>   		kfree(vsca);
> -		return NULL;
> +		return ERR_PTR(ret);
>   	}
>   
>   	vsca->ved.process_frame = vimc_sca_process_frame;
> -	vsca->dev = &vimc->pdev.dev;
> +	vsca->dev = vimc->mdev.dev;
>   
>   	/* Initialize the frame format */
>   	vsca->sink_fmt = sink_fmt_default;
>   
>   	return &vsca->ved;
>   }
> +
> +static void vimc_sca_create_cfs_pads(struct config_group *ent_group)
> +{
> +	vimc_cfs_add_source_pad_api(ent_group, 0, VIMC_CFS_SRC_PAD_NUM(1));
> +	vimc_cfs_add_sink_pad_api(ent_group, 1, VIMC_CFS_SINK_PAD_NUM(0));
> +}
> +
> +struct vimc_cfs_drv vimc_sca_cfs_drv = {
> +	.name = VIMC_SCA_NAME,
> +	.create_pads = vimc_sca_create_cfs_pads,
> +};
> +
> +__exit void vimc_sca_exit(void)
> +{
> +	vimc_cfs_drv_unregister(&vimc_sca_cfs_drv);
> +}
> +
> +__init void vimc_sca_init(void)
> +{
> +	vimc_cfs_drv_register(&vimc_sca_cfs_drv);
> +}
> diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c
> index 46dc6a535abe..22e3ad98c818 100644
> --- a/drivers/media/platform/vimc/vimc-sensor.c
> +++ b/drivers/media/platform/vimc/vimc-sensor.c
> @@ -12,6 +12,7 @@
>   #include <media/v4l2-subdev.h>
>   #include <media/tpg/v4l2-tpg.h>
>   
> +#include "vimc-configfs.h"
>   #include "vimc-common.h"
>   
>   struct vimc_sen_device {
> @@ -391,3 +392,23 @@ struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc,
>   
>   	return NULL;
>   }
> +
> +static void vimc_sen_create_cfs_pads(struct config_group *ent_group)
> +{
> +	vimc_cfs_add_source_pad_api(ent_group, 0, VIMC_CFS_SRC_PAD_NUM(0));
> +}
> +
> +struct vimc_cfs_drv vimc_sen_cfs_drv = {
> +	.name = VIMC_SEN_NAME,
> +	.create_pads = vimc_sen_create_cfs_pads,
> +};
> +
> +__exit void vimc_sen_exit(void)
> +{
> +	vimc_cfs_drv_unregister(&vimc_sen_cfs_drv);
> +}
> +
> +__init void vimc_sen_init(void)
> +{
> +	vimc_cfs_drv_register(&vimc_sen_cfs_drv);
> +}
> 

Andrzej

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/5] media: vimc: upon streaming, check that the pipeline starts with a source entity
  2019-09-26 14:32   ` Shuah Khan
@ 2019-10-08 13:34     ` Dafna Hirschfeld
  0 siblings, 0 replies; 20+ messages in thread
From: Dafna Hirschfeld @ 2019-10-08 13:34 UTC (permalink / raw)
  To: Shuah Khan, linux-media
  Cc: laurent.pinchart, helen.koike, ezequiel, andre.almeida, hverkuil,
	kernel, dafna3

Hi,
Thanks for the review,

On Thu, 2019-09-26 at 08:32 -0600, Shuah Khan wrote:
> On 9/19/19 2:32 PM, Dafna Hirschfeld wrote:
> > Userspace can disable links and create pipelines that
> > do not start with a source entity. Trying to stream
> > from such a pipeline should fail with -EPIPE
> > currently this is not handled and cause kernel crash.
> > 
> 
> Minor: Can you make these 75 long. Makes it easier to read.
> 
> > Reproducing the crash:
> > media-ctl -d0 -l "5:1->21:0[0]" -v
> > v4l2-ctl -z platform:vimc -d "RGB/YUV Capture" -v width=1920,height=1440
> > v4l2-ctl --stream-mmap --stream-count=100 -d /dev/video2
> 
> I really would like to see the panic message so it can checked during
> testing.
> 
> If you are fixing a panic, please include the panic info. in the future.
> 
> > Signed-off-by: Dafna Hirschfeld <dafna.hirschfeld@collabora.com>
> > ---
> >   drivers/media/platform/vimc/vimc-streamer.c | 39 +++++++++++++++------
> >   1 file changed, 28 insertions(+), 11 deletions(-)
> > 
> > diff --git a/drivers/media/platform/vimc/vimc-streamer.c b/drivers/media/platform/vimc/vimc-streamer.c
> > index faa2879c25df..d0a9f8a0f26a 100644
> > --- a/drivers/media/platform/vimc/vimc-streamer.c
> > +++ b/drivers/media/platform/vimc/vimc-streamer.c
> > @@ -12,6 +12,19 @@
> >   
> >   #include "vimc-streamer.h"
> >   
> > +/**
> > + * Check if the entity has only source pads
> > + */
> > +static bool vimc_is_source(struct media_entity *ent)
> > +{
> > +	int i;
> > +
> > +	for (i = 0; i < ent->num_pads; i++)
> > +		if (ent->pads[i].flags & MEDIA_PAD_FL_SINK)
> > +			return false;
> > +	return true;
> > +}
> > +
> 
> Why not make this a common routine and add it to vimc-common.c?
> 
> >   /**
> >    * vimc_get_source_entity - get the entity connected with the first sink pad
> >    *
> > @@ -82,14 +95,12 @@ static int vimc_streamer_pipeline_init(struct vimc_stream *stream,
> >   	struct media_entity *entity;
> >   	struct video_device *vdev;
> >   	struct v4l2_subdev *sd;
> > -	int ret = 0;
> > +	int ret = -EINVAL;
> >   
> >   	stream->pipe_size = 0;
> >   	while (stream->pipe_size < VIMC_STREAMER_PIPELINE_MAX_SIZE) {
> > -		if (!ved) {
> > -			vimc_streamer_pipeline_terminate(stream);
> > -			return -EINVAL;
> > -		}
> > +		if (!ved)
> > +			break;
> >   		stream->ved_pipeline[stream->pipe_size++] = ved;
> >   
> >   		if (is_media_entity_v4l2_subdev(ved->ent)) {
> > @@ -98,15 +109,22 @@ static int vimc_streamer_pipeline_init(struct vimc_stream *stream,
> >   			if (ret && ret != -ENOIOCTLCMD) {
> >   				pr_err("subdev_call error %s\n",
> >   				       ved->ent->name);
> 
> While you are at it, can you make this a dev_err() instead? I think we
> have access to dev here.

Actually, there is no access to dev here. It can be sent to the function as
an argument from the vimc-capture.c code, but maybe a better solution is to move the dev
pointer of every vimc entity to the common `vimc_ent_dev ved` field, since all entities have a pointer to it.

Thanks,
Dafna
> 
> > -				vimc_streamer_pipeline_terminate(stream);
> > -				return ret;
> > +				break;
> >   			}
> >   		}
> >   
> >   		entity = vimc_get_source_entity(ved->ent);
> > -		/* Check if the end of the pipeline was reached*/
> > -		if (!entity)
> > +		/* Check if the end of the pipeline was reached */
> > +		if (!entity) {
> > +			/* the first entity of the pipe should be source only */
> > +			if (!vimc_is_source(ved->ent)) {
> > +				pr_err("first entity in the pipe '%s' is not a source\n",
> > +				       ved->ent->name);
> 
> Same commnet about dev_err() here.
> 
> > +				ret = -EPIPE;
> > +				break;
> > +			}
> >   			return 0;
> > +		}
> >   
> >   		/* Get the next device in the pipeline */
> >   		if (is_media_entity_v4l2_subdev(entity)) {
> > @@ -119,9 +137,8 @@ static int vimc_streamer_pipeline_init(struct vimc_stream *stream,
> >   			ved = video_get_drvdata(vdev);
> >   		}
> >   	}
> > -
> >   	vimc_streamer_pipeline_terminate(stream);
> > -	return -EINVAL;
> > +	return ret;
> >   }
> >   
> >   /**
> > 
> 
> thanks,
> -- Shuah


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 3/5] media: vimc: Add the implementation for the configfs api
  2019-09-26 19:25   ` Shuah Khan
@ 2019-11-04 15:28     ` Dafna Hirschfeld
  0 siblings, 0 replies; 20+ messages in thread
From: Dafna Hirschfeld @ 2019-11-04 15:28 UTC (permalink / raw)
  To: Shuah Khan, linux-media

Hi,

On 9/26/19 9:25 PM, Shuah Khan wrote:
> On 9/19/19 2:32 PM, Dafna Hirschfeld wrote:
>> Add the code that implements the usage of configfs in order
>> to create and configure a device topology from userspace.
>> The code is only added in this patch but is not used.
>> It will be used in next patch in the series.
>>
> 
> This somehow doesn't read right. It isn't clear what you are adding.
> Can you pleas rephrase it.
> 
> Also, I would like to see the overview of the design. I see that you
> are adding _init routines e.g vimc_cap_init(). It will be easier to
> review if you described the details of the overall design a bit.
> 
Hi,
Will send a new version with a more verbose cover letter.
> 
>> Signed-off-by: Helen Koike <helen.koike@collabora.com>
>> [refactored for upstream]
> 
> What does this mean?

This patch is based on a patch of Helen Koike that was already sent to the mailing list
https://patchwork.kernel.org/patch/10718673/

Regards,
Dafna

> 
>> Signed-off-by: Dafna Hirschfeld <dafna.hirschfeld@collabora.com>
>> ---
>>   drivers/media/platform/vimc/Kconfig         |   9 +-
>>   drivers/media/platform/vimc/Makefile        |   2 +-
>>   drivers/media/platform/vimc/vimc-capture.c  |  21 +
>>   drivers/media/platform/vimc/vimc-common.h   |  46 ++
>>   drivers/media/platform/vimc/vimc-configfs.c | 656 ++++++++++++++++++++
>>   drivers/media/platform/vimc/vimc-configfs.h |  41 ++
>>   drivers/media/platform/vimc/vimc-core.c     |  21 +-
>>   drivers/media/platform/vimc/vimc-debayer.c  |  22 +
>>   drivers/media/platform/vimc/vimc-scaler.c   |  26 +-
>>   drivers/media/platform/vimc/vimc-sensor.c   |  21 +
>>   10 files changed, 856 insertions(+), 9 deletions(-)
>>   create mode 100644 drivers/media/platform/vimc/vimc-configfs.c
>>   create mode 100644 drivers/media/platform/vimc/vimc-configfs.h
>>
>> diff --git a/drivers/media/platform/vimc/Kconfig b/drivers/media/platform/vimc/Kconfig
>> index bd221d3e1a4a..6d7836d58ef4 100644
>> --- a/drivers/media/platform/vimc/Kconfig
>> +++ b/drivers/media/platform/vimc/Kconfig
>> @@ -1,15 +1,14 @@
>>   # SPDX-License-Identifier: GPL-2.0-only
>>   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
>>       help
>> -      Skeleton driver for Virtual Media Controller
>> +      Virtual Media Controller Driver
>> -      This driver can be compared to the vivid driver for emulating
>> +      This driver emulates
> 
> Why a short line, combine with the next?
> 
>>         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 the configfs API.
>>         When in doubt, say N.
>> diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile
>> index a53b2b532e9f..eb03d487f308 100644
>> --- a/drivers/media/platform/vimc/Makefile
>> +++ b/drivers/media/platform/vimc/Makefile
>> @@ -1,6 +1,6 @@
>>   # SPDX-License-Identifier: GPL-2.0
>>   vimc-y := vimc-core.o vimc-common.o vimc-streamer.o vimc-capture.o \
>> -        vimc-debayer.o vimc-scaler.o vimc-sensor.o
>> +        vimc-debayer.o vimc-scaler.o vimc-sensor.o  vimc-configfs.o
>>   obj-$(CONFIG_VIDEO_VIMC) += vimc.o
>> diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c
>> index 602f80323031..5cc419e76ce7 100644
>> --- a/drivers/media/platform/vimc/vimc-capture.c
>> +++ b/drivers/media/platform/vimc/vimc-capture.c
>> @@ -9,6 +9,7 @@
>>   #include <media/videobuf2-core.h>
>>   #include <media/videobuf2-vmalloc.h>
>> +#include "vimc-configfs.h"
>>   #include "vimc-common.h"
>>   #include "vimc-streamer.h"
>> @@ -488,3 +489,23 @@ struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc,
>>       return NULL;
>>   }
>> +
>> +static void vimc_cap_create_cfs_pads(struct config_group *ent_group)
>> +{
>> +    vimc_cfs_add_sink_pad_api(ent_group, 0, VIMC_CFS_SINK_PAD_NUM(0));
>> +}
>> +
>> +struct vimc_cfs_drv vimc_cap_cfs_drv = {
>> +    .name = VIMC_CAP_NAME,
>> +    .create_pads = vimc_cap_create_cfs_pads,
>> +};
>> +
>> +__exit void vimc_cap_exit(void)
>> +{
>> +    vimc_cfs_drv_unregister(&vimc_cap_cfs_drv);
>> +}
>> +
>> +__init void vimc_cap_init(void)
>> +{
>> +    vimc_cfs_drv_register(&vimc_cap_cfs_drv);
>> +}
>> diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h
>> index 698db7c07645..e0e3b3ab7b19 100644
>> --- a/drivers/media/platform/vimc/vimc-common.h
>> +++ b/drivers/media/platform/vimc/vimc-common.h
>> @@ -14,6 +14,7 @@
>>   #include <media/v4l2-device.h>
>>   #define VIMC_PDEV_NAME "vimc"
>> +#define VIMC_MAX_NAME_LEN V4L2_SUBDEV_NAME_SIZE
>>   /* VIMC-specific controls */
>>   #define VIMC_CID_VIMC_BASE        (0x00f00000 | 0xf000)
>> @@ -31,6 +32,11 @@
>>   #define VIMC_IS_SRC(pad)    (pad)
>>   #define VIMC_IS_SINK(pad)    (!(pad))
>> +#define VIMC_DEB_NAME "vimc-debayer"
>> +#define VIMC_SEN_NAME "vimc-sensor"
>> +#define VIMC_SCA_NAME "vimc-scaler"
>> +#define VIMC_CAP_NAME "vimc-capture"
>> +
>>   /**
>>    * struct vimc_colorimetry_clamp - Adjust colorimetry parameters
>>    *
>> @@ -72,6 +78,18 @@ struct vimc_platform_data {
>>       char entity_name[32];
>>   };
>> +/**
>> + * struct vimc_platform_data_core - platform data to the core
>> + *
>> + * @ents: list of vimc_entity_data objects allocated by the configfs
>> + * @links: list of vimc_links objects allocated by the configfs
>> + *
>> + */
>> +struct vimc_platform_data_core {
>> +    struct list_head *ents;
>> +    struct list_head *links;
>> +};
>> +
>>   /**
>>    * struct vimc_pix_map - maps media bus code with v4l2 pixel format
>>    *
>> @@ -92,6 +110,8 @@ struct vimc_pix_map {
>>   /**
>>    * struct vimc_ent_device - core struct that represents a node in the topology
>>    *
>> + * @name:        the name of the entity
>> + * @drv_name:        the name of the driver of the entity
> 
> Please align it with rest. Extra tab perhaps.
> 
>>    * @ent:        the pointer to struct media_entity for the node
>>    * @pads:        the list of pads of the node
>>    * @process_frame:    callback send a frame to that node
>> @@ -108,12 +128,30 @@ struct vimc_pix_map {
>>    * media_entity
>>    */
>>   struct vimc_ent_device {
>> +    char name[VIMC_MAX_NAME_LEN];
>> +    char drv_name[VIMC_MAX_NAME_LEN];
>>       struct media_entity *ent;
>>       struct media_pad *pads;
>>       void * (*process_frame)(struct vimc_ent_device *ved,
>>                   const void *frame);
>>       void (*vdev_get_format)(struct vimc_ent_device *ved,
>>                     struct v4l2_pix_format *fmt);
>> +    struct list_head list;
>> +};
>> +
>> +struct vimc_entity_data {
>> +    char name[VIMC_MAX_NAME_LEN];
>> +    char drv_name[VIMC_MAX_NAME_LEN];
>> +    struct list_head list;
>> +};
>> +
>> +struct vimc_link {
>> +    char source_name[VIMC_MAX_NAME_LEN];
>> +    char sink_name[VIMC_MAX_NAME_LEN];
>> +    u16 source_pad;
>> +    u16 sink_pad;
>> +    u32 flags;
>> +    struct list_head list;
>>   };
>>   /**
>> @@ -156,18 +194,26 @@ struct vimc_ent_config {
>>   struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc,
>>                        const char *vcfg_name);
>>   void vimc_cap_rm(struct vimc_device *vimc, struct vimc_ent_device *ved);
>> +void vimc_cap_init(void);
>> +void vimc_cap_exit(void);
>>   struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc,
>>                        const char *vcfg_name);
>>   void vimc_deb_rm(struct vimc_device *vimc, struct vimc_ent_device *ved);
>> +void vimc_deb_init(void);
>> +void vimc_deb_exit(void);
>>   struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc,
>>                        const char *vcfg_name);
>>   void vimc_sca_rm(struct vimc_device *vimc, struct vimc_ent_device *ved);
>> +void vimc_sca_init(void);
>> +void vimc_sca_exit(void);
>>   struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc,
>>                        const char *vcfg_name);
>>   void vimc_sen_rm(struct vimc_device *vimc, struct vimc_ent_device *ved);
>> +void vimc_sen_init(void);
>> +void vimc_sen_exit(void);
>>   /**
>>    * vimc_pads_init - initialize pads
>> diff --git a/drivers/media/platform/vimc/vimc-configfs.c b/drivers/media/platform/vimc/vimc-configfs.c
>> new file mode 100644
>> index 000000000000..933aece0bb5f
>> --- /dev/null
>> +++ b/drivers/media/platform/vimc/vimc-configfs.c
>> @@ -0,0 +1,656 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * vimc-configfs.c Virtual Media Controller Driver
>> + *
>> + * Copyright (C) 2018 Helen Koike <helen.koike@collabora.com>
>> + */
>> +
>> +#include <linux/platform_device.h>
>> +
>> +#include "vimc-common.h"
>> +#include "vimc-configfs.h"
>> +
>> +#define CHAR_SEPARATOR ':'
>> +#define CFS_SUBSYS_NAME "vimc"
>> +#define MAX_PAD_DIGI_NUM 4
>> +
>> +#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: %s:" pr_fmt(fmt), (ci)->ci_name, __func__, ##__VA_ARGS__)
>> +#define cg_dbg(cg, ...) ci_dbg(&(cg)->cg_item, ##__VA_ARGS__)
>> +
>> +#define IS_PLUGGED(cfs) (!!(cfs)->pdev)
>> +
>> +// currently there is no entity with more than two pads, this will
>> +// change when adding the splitter entity
>> +#define VIMC_ENT_MAX_PADS 2
>> +
>> +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 list_head ents;
>> +    struct list_head links;
>> +    struct platform_device *pdev;
>> +    struct vimc_platform_data_core pdata;
>> +    struct config_group gdev;
>> +};
>> +
>> +/* Structure which describes individual configuration for each entity */
>> +struct vimc_cfs_ent {
>> +    struct vimc_entity_data ent;
>> +    struct config_group cg;
>> +    struct config_group pad_groups[VIMC_ENT_MAX_PADS];
>> +};
>> +
>> +struct vimc_cfs_link {
>> +    struct vimc_link link;
>> +    struct config_item ci;
>> +};
>> +
>> +void vimc_cfs_drv_register(struct vimc_cfs_drv *c_drv)
>> +{
>> +    pr_debug("%s: adding driver %s\n", __func__, c_drv->name);
>> +    list_add(&c_drv->list, &vimc_cfs_subsys.drvs);
>> +}
>> +EXPORT_SYMBOL_GPL(vimc_cfs_drv_register);
> 
> It appears we are adding drivers? Is this necessary. I would have
> been helpful see the design overview in the commit log it is clear
> why?
> 
> 
> Can you please do that first. I will review the rest after that.
> 
>> +
>> +void vimc_cfs_drv_unregister(struct vimc_cfs_drv *c_drv)
>> +{
>> +    pr_debug("%s: removing driver %s\n", __func__, c_drv->name);
>> +    list_del(&c_drv->list);
>> +}
>> +EXPORT_SYMBOL_GPL(vimc_cfs_drv_unregister);
>> +
>> +/* --------------------------------------------------------------------------
>> + * Platform Device builders
>> + */
>> +
>> +static void vimc_cfs_device_unplug(struct vimc_cfs_device *cfs)
>> +{
>> +    dev_dbg(&cfs->pdev->dev, "Unplugging device\n");
>> +    platform_device_unregister(cfs->pdev);
>> +
>> +    cfs->pdev = NULL;
>> +}
>> +
>> +static int vimc_cfs_device_plug(struct vimc_cfs_device *cfs)
>> +{
>> +    cg_dbg(&cfs->gdev, "Plugging device\n");
>> +
>> +    if (list_empty(&cfs->ents)) {
>> +        cg_warn(&cfs->gdev,
>> +            "At least one entity is required to plug the device\n");
>> +        return -EINVAL;
>> +    }
>> +
>> +    cfs->pdev = platform_device_register_data(NULL, "vimc-core",
>> +                          PLATFORM_DEVID_AUTO,
>> +                          &cfs->pdata,
>> +                          sizeof(cfs->pdata));
>> +    if (IS_ERR(cfs->pdev)) {
>> +        int ret = PTR_ERR(cfs->pdev);
>> +
>> +        cfs->pdev = NULL;
>> +        return ret;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +/* --------------------------------------------------------------------------
>> + * Links
>> + */
>> +
>> +static ssize_t vimc_cfs_link_store_flag(struct config_item *item,
>> +                    const char *buf,
>> +                    size_t size, u32 flag)
>> +{
>> +    struct vimc_cfs_link *c_link =
>> +        container_of(item, struct vimc_cfs_link, ci);
>> +
>> +    if (!strncmp(buf, "on\n", 4) || !strncmp(buf, "1\n", 3)) {
>> +        c_link->link.flags |= MEDIA_LNK_FL_ENABLED;
>> +        return strlen(buf);
>> +    } else if (!strncmp(buf, "off\n", 5) || !strncmp(buf, "0\n", 3)) {
>> +        c_link->link.flags &= ~MEDIA_LNK_FL_ENABLED;
>> +        return strlen(buf);
>> +    }
>> +    return -EINVAL;
>> +}
>> +
>> +static ssize_t vimc_cfs_link_show_flag(struct config_item *item,
>> +                       char *buf, u32 flag)
>> +{
>> +    struct vimc_cfs_link *c_link = container_of(item,
>> +                            struct vimc_cfs_link, ci);
>> +
>> +    if (c_link->link.flags & flag)
>> +        strscpy(buf, "on\n", 4);
>> +    else
>> +        strscpy(buf, "off\n", 5);
>> +    return strlen(buf);
>> +}
>> +
>> +static ssize_t vimc_cfs_link_enabled_store(struct config_item *item,
>> +                       const char *buf,
>> +                       size_t size)
>> +{
>> +    return vimc_cfs_link_store_flag(item, buf, size, MEDIA_LNK_FL_ENABLED);
>> +}
>> +
>> +
>> +static ssize_t vimc_cfs_link_enabled_show(struct config_item *item,
>> +                      char *buf)
>> +{
>> +    return vimc_cfs_link_show_flag(item, buf, MEDIA_LNK_FL_ENABLED);
>> +}
>> +
>> +CONFIGFS_ATTR(vimc_cfs_link_, enabled);
>> +
>> +static ssize_t vimc_cfs_link_immutable_show(struct config_item *item,
>> +                         char *buf)
>> +{
>> +    return vimc_cfs_link_show_flag(item, buf, MEDIA_LNK_FL_IMMUTABLE);
>> +}
>> +
>> +static ssize_t vimc_cfs_link_immutable_store(struct config_item *item,
>> +                           const char *buf, size_t size)
>> +{
>> +    return vimc_cfs_link_store_flag(item, buf, size,
>> +                    MEDIA_LNK_FL_IMMUTABLE);
>> +}
>> +
>> +CONFIGFS_ATTR(vimc_cfs_link_, immutable);
>> +
>> +static void vimc_cfs_fill_link_data(struct config_item *src,
>> +                struct config_item *target)
>> +{
>> +    struct config_item *src_ent_ci = src->ci_parent->ci_parent;
>> +    struct config_item *trgt_ent_ci = target->ci_parent;
>> +    struct vimc_cfs_link *c_link =
>> +            container_of(src, struct vimc_cfs_link, ci);
>> +    struct vimc_cfs_ent *vimc_src_ent = container_of(src_ent_ci,
>> +                             struct vimc_cfs_ent,
>> +                             cg.cg_item);
>> +    struct vimc_cfs_ent *vimc_trgt_ent = container_of(trgt_ent_ci,
>> +                             struct vimc_cfs_ent,
>> +                             cg.cg_item);
>> +    struct vimc_cfs_device *cfs = container_of(src_ent_ci->ci_parent,
>> +                             struct vimc_cfs_device,
>> +                             gdev.cg_item);
>> +
>> +    if (IS_PLUGGED(cfs))
>> +        vimc_cfs_device_unplug(cfs);
>> +
>> +    /* src and target validation already done in the allow_link callback,
>> +     * so there is no need to check sscanf result
>> +     */
>> +    sscanf(src->ci_parent->ci_name, VIMC_CFS_SRC_PAD "%hu",
>> +           &c_link->link.source_pad);
>> +    sscanf(target->ci_parent->ci_name, VIMC_CFS_SINK_PAD "%hu",
>> +           &c_link->link.sink_pad);
>> +
>> +    strscpy(c_link->link.source_name, vimc_src_ent->ent.name,
>> +        sizeof(c_link->link.source_name));
>> +    strscpy(c_link->link.sink_name, vimc_trgt_ent->ent.name,
>> +        sizeof(c_link->link.sink_name));
>> +
>> +    cg_dbg(&cfs->gdev, "creating link %s:%u->%s:%u\n",
>> +            c_link->link.source_name, c_link->link.source_pad,
>> +            c_link->link.sink_name, c_link->link.sink_pad);
>> +
>> +    list_add(&c_link->link.list, &cfs->links);
>> +}
>> +
>> +static void vimc_cfs_drop_link(struct config_item *src,
>> +                   struct config_item *target)
>> +{
>> +    struct vimc_cfs_link *c_link = container_of(src,
>> +                            struct vimc_cfs_link, ci);
>> +
>> +    ci_dbg(&c_link->ci, "dropping link %s:%u->%s:%u\n",
>> +           c_link->link.source_name, c_link->link.source_pad,
>> +           c_link->link.sink_name, c_link->link.sink_pad);
>> +
>> +    c_link->link.source_pad = 0;
>> +    c_link->link.sink_pad = 0;
>> +    memset(c_link->link.source_name, 0, sizeof(c_link->link.source_name));
>> +    memset(c_link->link.sink_name, 0, sizeof(c_link->link.sink_name));
>> +    list_del(&c_link->link.list);
>> +}
>> +
>> +int vimc_cfs_allow_link(struct config_item *src, struct config_item *target)
>> +{
>> +    struct config_item *s = src;
>> +    struct config_item *t = target;
>> +    struct config_item *src_ent_ci, *trgt_ent_ci;
>> +    int i;
>> +    int ret = 0;
>> +    struct configfs_subsystem *subsys = &vimc_cfs_subsys.subsys;
>> +
>> +    if (strncmp(target->ci_name, VIMC_CFS_SINK_PAD,
>> +            sizeof(VIMC_CFS_SINK_PAD) - 1)) {
>> +        ci_warn(src, "link target (%s) does not start with '%s'\n",
>> +            target->ci_name, VIMC_CFS_SINK_PAD);
>> +        return -EINVAL;
>> +    }
>> +
>> +    mutex_lock(&subsys->su_mutex);
>> +    for (i = 0; i < 3; i++)
>> +        s = s->ci_parent;
>> +
>> +    for (i = 0; i < 2; i++) {
>> +        t = t->ci_parent;
>> +        if (!t) {
>> +            ci_warn(src, "link target (%s) is not a sink pad\n",
>> +                target->ci_name);
>> +            ret = -EINVAL;
>> +            goto end;
>> +        }
>> +    }
>> +
>> +    if (s != t) {
>> +        ci_warn(src, "not allow linking between different vimcs devices: (%s) and (%s)\n",
>> +            s->ci_name, t->ci_name);
>> +        ret = -EINVAL;
>> +        goto end;
>> +    }
>> +    src_ent_ci = src->ci_parent->ci_parent;
>> +    trgt_ent_ci = target->ci_parent;
>> +    if (src_ent_ci == trgt_ent_ci) {
>> +        ci_warn(src, "both pads belong to the same entity (%s) - this is not allowed\n",
>> +                src_ent_ci->ci_name);
>> +        ret = -EINVAL;
>> +        goto end;
>> +    }
>> +
>> +    vimc_cfs_fill_link_data(src, target);
>> +end:
>> +    mutex_unlock(&subsys->su_mutex);
>> +    return ret;
>> +}
>> +
>> +static void vimc_cfs_prepare_link_release(struct config_item *item)
>> +{
>> +    struct vimc_cfs_link *c_link = container_of(item,
>> +                            struct vimc_cfs_link, ci);
>> +
>> +    ci_dbg(item, "releasing link '%s'", item->ci_name);
>> +    kfree(c_link);
>> +}
>> +
>> +static struct configfs_attribute *vimc_cfs_link_attrs[] = {
>> +    &vimc_cfs_link_attr_enabled,
>> +    &vimc_cfs_link_attr_immutable,
>> +    NULL,
>> +};
>> +
>> +static struct configfs_item_operations vimc_cfs_allow_link_item_ops = {
>> +    .allow_link = vimc_cfs_allow_link,
>> +    .drop_link = vimc_cfs_drop_link,
>> +    .release = vimc_cfs_prepare_link_release,
>> +};
>> +
>> +struct config_item_type vimc_cfs_allow_link_type = {
>> +    .ct_owner = THIS_MODULE,
>> +    .ct_item_ops = &vimc_cfs_allow_link_item_ops,
>> +    .ct_attrs = vimc_cfs_link_attrs,
>> +};
>> +
>> +/* --------------------------------------------------------------------------
>> + * Source pad instance
>> + */
>> +
>> +static void vimc_cfs_src_pad_prepare_link_drop_item(
>> +        struct config_group *src_pad_group,
>> +        struct config_item *link_item)
>> +{
>> +
>> +    struct config_item *cfs_item;
>> +    struct vimc_cfs_device *cfs;
>> +
>> +    cfs_item = src_pad_group->cg_item.ci_parent->ci_parent;
>> +    cfs = container_of(cfs_item->ci_parent, struct vimc_cfs_device,
>> +               gdev.cg_item);
>> +    cg_dbg(&cfs->gdev, "dropping prepared link '%s'\n",
>> +           link_item->ci_name);
>> +    if (IS_PLUGGED(cfs))
>> +        vimc_cfs_device_unplug(cfs);
>> +    config_item_put(link_item);
>> +}
>> +
>> +static struct config_item *vimc_cfs_src_pad_prepare_link_make_item(
>> +               struct config_group *group,
>> +               const char *name)
>> +{
>> +    struct vimc_cfs_link *c_link = kzalloc(sizeof(*c_link), GFP_KERNEL);
>> +
>> +    cg_dbg(group, "link name is '%s'\n", name);
>> +    config_item_init_type_name(&c_link->ci, name,
>> +                   &vimc_cfs_allow_link_type);
>> +    return &c_link->ci;
>> +}
>> +struct config_item_type vimc_cfs_empty_type = {
>> +    .ct_owner = THIS_MODULE,
>> +};
>> +
>> +static struct configfs_group_operations vimc_cfs_src_pad_ops = {
>> +    .make_item = vimc_cfs_src_pad_prepare_link_make_item,
>> +    .drop_item = vimc_cfs_src_pad_prepare_link_drop_item
>> +};
>> +
>> +struct config_item_type vimc_cfs_src_pad_type = {
>> +    .ct_owner = THIS_MODULE,
>> +    .ct_group_ops = &vimc_cfs_src_pad_ops,
>> +};
>> +
>> +/* --------------------------------------------------------------------------
>> + * Device instance
>> + */
>> +
>> +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);
>> +
>> +    ci_dbg(item, "releasing entity '%s' of driver '%s'",
>> +           c_ent->ent.name,
>> +           c_ent->ent.drv_name);
>> +    kfree(c_ent);
>> +}
>> +
>> +static struct configfs_item_operations vimc_cfs_ent_item_ops = {
>> +    .release    = vimc_cfs_ent_release,
>> +};
>> +
>> +struct config_item_type vimc_cfs_ent_type = {
>> +    .ct_owner = THIS_MODULE,
>> +    .ct_item_ops = &vimc_cfs_ent_item_ops,
>> +};
>> +
>> +void vimc_cfs_add_sink_pad_api(struct config_group *ent_group,
>> +                    int pad_idx, const char *name)
>> +{
>> +    struct vimc_cfs_ent *c_ent = container_of(ent_group,
>> +                          struct vimc_cfs_ent, cg);
>> +
>> +    config_group_init_type_name(&c_ent->pad_groups[pad_idx], name,
>> +                    &vimc_cfs_empty_type);
>> +    configfs_add_default_group(&c_ent->pad_groups[pad_idx], ent_group);
>> +}
>> +EXPORT_SYMBOL_GPL(vimc_cfs_add_sink_pad_api);
>> +
>> +void vimc_cfs_add_source_pad_api(struct config_group *ent_group,
>> +                      int pad_idx, const char *name)
>> +{
>> +    struct vimc_cfs_ent *c_ent = container_of(ent_group,
>> +                          struct vimc_cfs_ent, cg);
>> +
>> +    config_group_init_type_name(&c_ent->pad_groups[pad_idx], name,
>> +                    &vimc_cfs_src_pad_type);
>> +    configfs_add_default_group(&c_ent->pad_groups[pad_idx], ent_group);
>> +}
>> +EXPORT_SYMBOL_GPL(vimc_cfs_add_source_pad_api);
>> +
>> +static void vimc_cfs_dev_drop_ent_item(struct config_group *dev_group,
>> +                       struct config_item *ent_item)
>> +{
>> +    struct vimc_cfs_ent *c_ent = container_of(ent_item, struct vimc_cfs_ent,
>> +                          cg.cg_item);
>> +    struct vimc_cfs_device *cfs = container_of(dev_group,
>> +                           struct vimc_cfs_device,
>> +                           gdev);
>> +
>> +    if (IS_PLUGGED(cfs))
>> +        vimc_cfs_device_unplug(cfs);
>> +
>> +    cg_dbg(&cfs->gdev, "dropping entity '%s' of driver '%s'",
>> +           c_ent->ent.name, c_ent->ent.drv_name);
>> +    list_del(&c_ent->ent.list);
>> +    config_item_put(ent_item);
>> +}
>> +
>> +static struct config_group *vimc_cfs_dev_make_ent_group(
>> +            struct config_group *group, const char *name)
>> +{
>> +    struct vimc_cfs_device *cfs = container_of(group,
>> +                           struct vimc_cfs_device,
>> +                           gdev);
>> +    char *ent_name, *sep = strchr(name, CHAR_SEPARATOR);
>> +    struct vimc_cfs_ent *c_ent;
>> +    struct vimc_entity_data *ent;
>> +    size_t drv_namelen;
>> +    struct vimc_cfs_drv *c_drv = NULL;
>> +
>> +    cg_dbg(group, "trying to make entity '%s'\n", name);
>> +    if (IS_PLUGGED(cfs))
>> +        vimc_cfs_device_unplug(cfs);
>> +
>> +    /* Parse format "drv_name:ent_name" */
>> +    if (!sep) {
>> +        cg_warn(&cfs->gdev,
>> +            "Could not find separator '%c'\n", CHAR_SEPARATOR);
>> +        goto syntax_error;
>> +    }
>> +    drv_namelen = (size_t)(sep - name);
>> +    ent_name = &sep[1];
>> +    if (!*ent_name || !drv_namelen) {
>> +        cg_warn(&cfs->gdev,
>> +            "%s: Driver name and entity name can't be empty.\n",
>> +               name);
>> +        goto syntax_error;
>> +    }
>> +    if (drv_namelen >= VIMC_MAX_NAME_LEN) {
>> +        cg_err(&cfs->gdev,
>> +               "%s: Driver name length should be less than %u.\n",
>> +               name, VIMC_MAX_NAME_LEN);
>> +        goto syntax_error;
>> +    }
>> +    list_for_each_entry(ent, &cfs->ents, list) {
>> +        if (!strncmp(ent->name, ent_name, sizeof(ent->name))) {
>> +            cg_err(&cfs->gdev, "entity `%s` already exist\n",
>> +                   ent->name);
>> +            goto syntax_error;
>> +        }
>> +    }
>> +
>> +    c_ent = kzalloc(sizeof(*c_ent), GFP_KERNEL);
>> +    if (!c_ent)
>> +        return ERR_PTR(-ENOMEM);
>> +
>> +    strscpy(c_ent->ent.drv_name, name, drv_namelen + 1);
>> +    strscpy(c_ent->ent.name, ent_name, sizeof(c_ent->ent.name));
>> +
>> +    /* Configure group */
>> +
>> +    /* TODO: add support for hotplug at entity level */
>> +    list_for_each_entry(c_drv, &vimc_cfs_subsys.drvs, list) {
>> +        if (!strcmp(c_ent->ent.drv_name, c_drv->name)) {
>> +            config_group_init_type_name(&c_ent->cg, name,
>> +                            &vimc_cfs_ent_type);
>> +            if (c_drv->create_pads)
>> +                c_drv->create_pads(&c_ent->cg);
>> +            list_add(&c_ent->ent.list, &cfs->ents);
>> +            return &c_ent->cg;
>> +        }
>> +    }
>> +    cg_warn(&cfs->gdev, "entity type %s not found\n", c_ent->ent.drv_name);
>> +    kfree(c_ent);
>> +    return ERR_PTR(-EINVAL);
>> +
>> +syntax_error:
>> +    cg_err(&cfs->gdev,
>> +        "couldn't create entity %s, wrong syntax.", name);
>> +    return ERR_PTR(-EINVAL);
>> +}
>> +
>> +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_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_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_, hotplug);
>> +
>> +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);
>> +
>> +    ci_dbg(item, "releasing dev %s\n", item->ci_name);
>> +    kfree(cfs);
>> +}
>> +
>> +static struct configfs_group_operations vimc_cfs_dev_group_ops = {
>> +    .make_group = vimc_cfs_dev_make_ent_group,
>> +    .drop_item = vimc_cfs_dev_drop_ent_item,
>> +};
>> +
>> +static struct configfs_item_operations vimc_cfs_dev_item_ops = {
>> +    .release = vimc_cfs_dev_release,
>> +};
>> +
>> +static struct configfs_attribute *vimc_cfs_dev_attrs[] = {
>> +    &vimc_cfs_dev_attr_hotplug,
>> +    NULL,
>> +};
>> +
>> +static struct config_item_type vimc_cfs_dev_type = {
>> +    .ct_group_ops = &vimc_cfs_dev_group_ops,
>> +    .ct_item_ops = &vimc_cfs_dev_item_ops,
>> +    .ct_attrs = vimc_cfs_dev_attrs,
>> +    .ct_owner = THIS_MODULE,
>> +};
>> +
>> +/* --------------------------------------------------------------------------
>> + * Subsystem
>> + * --------------------------------------------------------------------------
>> + */
>> +
>> +static void vimc_cfs_subsys_drop_dev_item(struct config_group *group,
>> +                   struct config_item *item)
>> +{
>> +    struct vimc_cfs_device *cfs = container_of(to_config_group(item),
>> +                           struct vimc_cfs_device,
>> +                           gdev);
>> +
>> +    if (IS_PLUGGED(cfs))
>> +        vimc_cfs_device_unplug(cfs);
>> +    cg_dbg(&cfs->gdev, "dropping subsys '%s'", item->ci_name);
>> +    config_item_put(item);
>> +}
>> +
>> +static struct config_group *vimc_cfs_subsys_make_dev_group(
>> +                struct config_group *group, const char *name)
>> +{
>> +    struct vimc_cfs_device *cfs = kzalloc(sizeof(*cfs), GFP_KERNEL);
>> +
>> +    if (!cfs)
>> +        return ERR_PTR(-ENOMEM);
>> +
>> +    cg_dbg(&cfs->gdev, "making dev group '%s'", name);
>> +    /* Configure platform data */
>> +    INIT_LIST_HEAD(&cfs->ents);
>> +    INIT_LIST_HEAD(&cfs->links);
>> +    cfs->pdata.links = &cfs->links;
>> +    cfs->pdata.ents = &cfs->ents;
>> +
>> +    config_group_init_type_name(&cfs->gdev, name, &vimc_cfs_dev_type);
>> +
>> +    return &cfs->gdev;
>> +}
>> +
>> +static struct configfs_group_operations vimc_cfs_subsys_group_ops = {
>> +    .make_group    = vimc_cfs_subsys_make_dev_group,
>> +    .drop_item    = vimc_cfs_subsys_drop_dev_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..0d3afdb31fdd
>> --- /dev/null
>> +++ b/drivers/media/platform/vimc/vimc-configfs.h
>> @@ -0,0 +1,41 @@
>> +/* SPDX-License-Identifier: GPL-2.0+ */
>> +/*
>> + * vimc-configfs.h Virtual Media Controller Driver
>> + *
>> + * Copyright (C) 2018 Helen Koike <helen.koike@collabora.com>
>> + */
>> +
>> +#ifndef _VIMC_CONFIGFS_H_
>> +#define _VIMC_CONFIGFS_H_
>> +
>> +#include <linux/configfs.h>
>> +
>> +#define VIMC_CFS_SRC_PAD "pad:source:"
>> +#define VIMC_CFS_SINK_PAD "pad:sink:"
>> +
>> +#define VIMC_CFS_SRC_PAD_NUM(n) "pad:source:" #n
>> +#define VIMC_CFS_SINK_PAD_NUM(n) "pad:sink:" #n
>> +
>> +extern struct config_item_type vimc_default_cfs_pad_type;
>> +
>> +void vimc_cfs_add_source_pad_api(struct config_group *ent_group,
>> +                    int pad_idx, const char *name);
>> +
>> +void vimc_cfs_add_sink_pad_api(struct config_group *ent_group,
>> +                      int pad_idx, const char *name);
>> +struct vimc_cfs_drv {
>> +    const char *name;
>> +    struct list_head list;
>> +
>> +    void (*const create_pads)(struct config_group *parent);
>> +};
>> +
>> +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 6e3e5c91ae39..476bad2cb2de 100644
>> --- a/drivers/media/platform/vimc/vimc-core.c
>> +++ b/drivers/media/platform/vimc/vimc-core.c
>> @@ -15,6 +15,8 @@
>>   #define VIMC_MDEV_MODEL_NAME "VIMC MDEV"
>> +#include "vimc-configfs.h"
>> +
>>   #define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) {    \
>>       .src_ent = src,                        \
>>       .src_pad = srcpad,                    \
>> @@ -335,13 +337,30 @@ static int __init vimc_init(void)
>>           return ret;
>>       }
>> +    ret = vimc_cfs_subsys_register();
>> +    if (ret) {
>> +        pr_err("%s: configfs subsys registration failed (%d)\n",
>> +               __func__, ret);
>> +        platform_driver_unregister(&vimc_pdrv);
>> +        return ret;
>> +    }
>> +
>> +    vimc_sen_init();
>> +    vimc_deb_init();
>> +    vimc_sca_init();
>> +    vimc_cap_init();
>>       return 0;
>>   }
>>   static void __exit vimc_exit(void)
>>   {
>> -    platform_driver_unregister(&vimc_pdrv);
>> +    vimc_sen_exit();
>> +    vimc_deb_exit();
>> +    vimc_sca_exit();
>> +    vimc_cap_exit();
>> +    vimc_cfs_subsys_unregister();
>> +    platform_driver_unregister(&vimc_pdrv);
>>       platform_device_unregister(&vimc_dev.pdev);
>>   }
>> diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c
>> index feac47d79449..e461b155e514 100644
>> --- a/drivers/media/platform/vimc/vimc-debayer.c
>> +++ b/drivers/media/platform/vimc/vimc-debayer.c
>> @@ -12,6 +12,7 @@
>>   #include <media/v4l2-subdev.h>
>>   #include "vimc-common.h"
>> +#include "vimc-configfs.h"
>>   static unsigned int deb_mean_win_size = 3;
>>   module_param(deb_mean_win_size, uint, 0000);
>> @@ -533,3 +534,24 @@ struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc,
>>       return &vdeb->ved;
>>   }
>> +
>> +static void vimc_deb_create_cfs_pads(struct config_group *ent_group)
>> +{
>> +    vimc_cfs_add_source_pad_api(ent_group, 0, VIMC_CFS_SRC_PAD_NUM(1));
>> +    vimc_cfs_add_sink_pad_api(ent_group, 1, VIMC_CFS_SINK_PAD_NUM(0));
>> +}
>> +
>> +struct vimc_cfs_drv vimc_deb_cfs_drv = {
>> +    .name = VIMC_DEB_NAME,
>> +    .create_pads = vimc_deb_create_cfs_pads,
>> +};
>> +
>> +__exit void vimc_deb_exit(void)
>> +{
>> +    vimc_cfs_drv_unregister(&vimc_deb_cfs_drv);
>> +}
>> +
>> +__init void vimc_deb_init(void)
>> +{
>> +    vimc_cfs_drv_register(&vimc_deb_cfs_drv);
>> +}
>> diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c
>> index a6a3cc5be872..e5cf0073d68a 100644
>> --- a/drivers/media/platform/vimc/vimc-scaler.c
>> +++ b/drivers/media/platform/vimc/vimc-scaler.c
>> @@ -10,6 +10,7 @@
>>   #include <linux/v4l2-mediabus.h>
>>   #include <media/v4l2-subdev.h>
>> +#include "vimc-configfs.h"
>>   #include "vimc-common.h"
>>   static unsigned int sca_mult = 3;
>> @@ -373,14 +374,35 @@ struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc,
>>                      &vimc_sca_int_ops, &vimc_sca_ops);
>>       if (ret) {
>>           kfree(vsca);
>> -        return NULL;
>> +        return ERR_PTR(ret);
>>       }
>>       vsca->ved.process_frame = vimc_sca_process_frame;
>> -    vsca->dev = &vimc->pdev.dev;
>> +    vsca->dev = vimc->mdev.dev;
>>       /* Initialize the frame format */
>>       vsca->sink_fmt = sink_fmt_default;
>>       return &vsca->ved;
>>   }
>> +
>> +static void vimc_sca_create_cfs_pads(struct config_group *ent_group)
>> +{
>> +    vimc_cfs_add_source_pad_api(ent_group, 0, VIMC_CFS_SRC_PAD_NUM(1));
>> +    vimc_cfs_add_sink_pad_api(ent_group, 1, VIMC_CFS_SINK_PAD_NUM(0));
>> +}
>> +
>> +struct vimc_cfs_drv vimc_sca_cfs_drv = {
>> +    .name = VIMC_SCA_NAME,
>> +    .create_pads = vimc_sca_create_cfs_pads,
>> +};
>> +
>> +__exit void vimc_sca_exit(void)
>> +{
>> +    vimc_cfs_drv_unregister(&vimc_sca_cfs_drv);
>> +}
>> +
>> +__init void vimc_sca_init(void)
>> +{
>> +    vimc_cfs_drv_register(&vimc_sca_cfs_drv);
>> +}
>> diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c
>> index 46dc6a535abe..22e3ad98c818 100644
>> --- a/drivers/media/platform/vimc/vimc-sensor.c
>> +++ b/drivers/media/platform/vimc/vimc-sensor.c
>> @@ -12,6 +12,7 @@
>>   #include <media/v4l2-subdev.h>
>>   #include <media/tpg/v4l2-tpg.h>
>> +#include "vimc-configfs.h"
>>   #include "vimc-common.h"
>>   struct vimc_sen_device {
>> @@ -391,3 +392,23 @@ struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc,
>>       return NULL;
>>   }
>> +
>> +static void vimc_sen_create_cfs_pads(struct config_group *ent_group)
>> +{
>> +    vimc_cfs_add_source_pad_api(ent_group, 0, VIMC_CFS_SRC_PAD_NUM(0));
>> +}
>> +
>> +struct vimc_cfs_drv vimc_sen_cfs_drv = {
>> +    .name = VIMC_SEN_NAME,
>> +    .create_pads = vimc_sen_create_cfs_pads,
>> +};
>> +
>> +__exit void vimc_sen_exit(void)
>> +{
>> +    vimc_cfs_drv_unregister(&vimc_sen_cfs_drv);
>> +}
>> +
>> +__init void vimc_sen_init(void)
>> +{
>> +    vimc_cfs_drv_register(&vimc_sen_cfs_drv);
>> +}
>>
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2019-11-04 15:28 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-09-19 20:32 [PATCH 0/5] media: vimc: use configfs in order to configure devices topologies Dafna Hirschfeld
2019-09-19 20:32 ` [PATCH 1/5] media: vimc: upon streaming, check that the pipeline starts with a source entity Dafna Hirschfeld
2019-09-26 14:32   ` Shuah Khan
2019-10-08 13:34     ` Dafna Hirschfeld
2019-09-19 20:32 ` [PATCH 2/5] docs: media: vimc: Documenting vimc topology configuration using configfs Dafna Hirschfeld
2019-09-20 13:39   ` Hans Verkuil
2019-09-23  9:29     ` Dafna Hirschfeld
2019-09-23  9:50       ` Hans Verkuil
2019-09-27  5:56         ` Andrzej Pietrasiewicz
2019-09-26 18:59   ` Shuah Khan
2019-09-19 20:32 ` [PATCH 3/5] media: vimc: Add the implementation for the configfs api Dafna Hirschfeld
2019-09-26 19:25   ` Shuah Khan
2019-11-04 15:28     ` Dafna Hirschfeld
2019-09-27  7:55   ` Andrzej Pietrasiewicz
2019-09-19 20:32 ` [PATCH 4/5] media: vimc: use configfs instead of having hardcoded configuration Dafna Hirschfeld
2019-09-26 19:41   ` Shuah Khan
2019-09-19 20:32 ` [PATCH 5/5] media: vimc: Add device index to the bus_info Dafna Hirschfeld
2019-09-20 13:17 ` [PATCH 0/5] media: vimc: use configfs in order to configure devices topologies Hans Verkuil
2019-09-20 15:07   ` Dafna Hirschfeld
2019-09-20 15:10     ` Hans Verkuil

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).