All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4] [media] vimc: Virtual Media Controller core, capture and sensor
@ 2016-05-31 15:02 Helen Koike
  2016-06-12  1:59 ` kbuild test robot
                   ` (2 more replies)
  0 siblings, 3 replies; 41+ messages in thread
From: Helen Koike @ 2016-05-31 15:02 UTC (permalink / raw)
  To: linux-media, laurent.pinchart, hverkuil, jgebben, mchehab; +Cc: Helen Fornazier

From: Helen Fornazier <helen.fornazier@gmail.com>

First version of the Virtual Media Controller.
Add a simple version of the core of the driver, the capture and
sensor nodes in the topology, generating a grey image in a hardcoded
format.

Signed-off-by: Helen Fornazier <helen.fornazier@gmail.com>

---
Changes since v3: fix rmmod crash and built-in compile
- Re-order unregister calls in vimc_device_unregister function (remove
rmmod issue)
- Call media_device_unregister_entity in vimc_raw_destroy
- Add depends on VIDEO_DEV && VIDEO_V4L2 and select VIDEOBUF2_VMALLOC
- Check *nplanes in queue_setup (this remove v4l2-compliance fail)
- Include <linux/kthread.h> in vimc-sensor.c
- Move include of <linux/slab.h> from vimc-core.c to vimc-core.h
- Generate 60 frames per sec instead of 1 in the sensor

Changes since v2: update with current media master tree
- Add struct media_pipeline in vimc_cap_device
- Use vb2_v4l2_buffer instead of vb2_buffer
- Typos
- Remove usage of entity->type and use entity->function instead
- Remove fmt argument from queue setup
- Use ktime_get_ns instead of v4l2_get_timestamp
- Iterate over link's list using list_for_each_entry
- Use media_device_{init, cleanup}
- Use entity->use_count to keep track of entities instead of the old
entity->id
- Replace media_entity_init by media_entity_pads_init

 drivers/media/platform/Kconfig             |   2 +
 drivers/media/platform/Makefile            |   1 +
 drivers/media/platform/vimc/Kconfig        |   8 +
 drivers/media/platform/vimc/Makefile       |   3 +
 drivers/media/platform/vimc/vimc-capture.c | 536 ++++++++++++++++++++++++++
 drivers/media/platform/vimc/vimc-capture.h |  28 ++
 drivers/media/platform/vimc/vimc-core.c    | 595 +++++++++++++++++++++++++++++
 drivers/media/platform/vimc/vimc-core.h    |  56 +++
 drivers/media/platform/vimc/vimc-sensor.c  | 278 ++++++++++++++
 drivers/media/platform/vimc/vimc-sensor.h  |  28 ++
 10 files changed, 1535 insertions(+)
 create mode 100644 drivers/media/platform/vimc/Kconfig
 create mode 100644 drivers/media/platform/vimc/Makefile
 create mode 100644 drivers/media/platform/vimc/vimc-capture.c
 create mode 100644 drivers/media/platform/vimc/vimc-capture.h
 create mode 100644 drivers/media/platform/vimc/vimc-core.c
 create mode 100644 drivers/media/platform/vimc/vimc-core.h
 create mode 100644 drivers/media/platform/vimc/vimc-sensor.c
 create mode 100644 drivers/media/platform/vimc/vimc-sensor.h

diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 84e041c..9e10d49 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -284,6 +284,8 @@ menuconfig V4L_TEST_DRIVERS
 
 if V4L_TEST_DRIVERS
 
+source "drivers/media/platform/vimc/Kconfig"
+
 source "drivers/media/platform/vivid/Kconfig"
 
 config VIDEO_VIM2M
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index bbb7bd1..e4508fe 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_VIDEO_OMAP3)	+= omap3isp/
 
 obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o
 
+obj-$(CONFIG_VIDEO_VIMC)		+= vimc/
 obj-$(CONFIG_VIDEO_VIVID)		+= vivid/
 obj-$(CONFIG_VIDEO_VIM2M)		+= vim2m.o
 
diff --git a/drivers/media/platform/vimc/Kconfig b/drivers/media/platform/vimc/Kconfig
new file mode 100644
index 0000000..c99666a
--- /dev/null
+++ b/drivers/media/platform/vimc/Kconfig
@@ -0,0 +1,8 @@
+config VIDEO_VIMC
+	tristate "Virtual Media Controller Driver (VIMC)"
+	depends on VIDEO_DEV && VIDEO_V4L2
+	select VIDEO_V4L2_SUBDEV_API
+	select VIDEOBUF2_VMALLOC
+	default n
+	---help---
+	  Skeleton driver for Virtual Media Controller
diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile
new file mode 100644
index 0000000..c45195e
--- /dev/null
+++ b/drivers/media/platform/vimc/Makefile
@@ -0,0 +1,3 @@
+vimc-objs := vimc-core.o vimc-capture.o vimc-sensor.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
new file mode 100644
index 0000000..82de8b89
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-capture.c
@@ -0,0 +1,536 @@
+/*
+ * vimc-capture.c Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015 Helen Fornazier <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-vmalloc.h>
+#include <media/v4l2-ioctl.h>
+
+#include "vimc-capture.h"
+
+struct vimc_cap_device {
+	struct vimc_ent_device ved;
+	struct video_device vdev;
+	struct v4l2_device *v4l2_dev;
+	struct device *dev;
+	struct v4l2_pix_format format;
+	struct vb2_queue queue;
+	struct list_head buf_list;
+	/* NOTE: in a real driver, a spin lock must be used to access the
+	 * queue because the frames are generated from a hardware interruption
+	 * and the isr is not allowed to sleep.
+	 * Even if it is not necessary a spinlock in the vimc driver, we
+	 * use it here as a code reference */
+	spinlock_t qlock;
+	struct mutex lock;
+	u32 sequence;
+	struct media_pipeline pipe;
+};
+
+struct vimc_cap_buffer {
+	/*
+	 * vb2_buffer must be the first element
+	 * the videobf2 framework will allocate this struct based on
+	 * buf_struct_size and use the first sizeof(struct vb2_buffer) bytes of
+	 * memory as a vb2_buffer
+	 */
+	struct vb2_v4l2_buffer vb2;
+	struct list_head list;
+};
+
+static int vimc_cap_querycap(struct file *file, void *priv,
+			     struct v4l2_capability *cap)
+{
+	struct vimc_cap_device *vcap = video_drvdata(file);
+
+	strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
+	strlcpy(cap->card, KBUILD_MODNAME, sizeof(cap->card));
+	snprintf(cap->bus_info, sizeof(cap->bus_info),
+		 "platform:%s", vcap->v4l2_dev->name);
+
+	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+	return 0;
+}
+
+static int vimc_cap_enum_input(struct file *file, void *priv,
+			       struct v4l2_input *i)
+{
+	/* We only have one input */
+	if (i->index > 0)
+		return -EINVAL;
+
+	i->type = V4L2_INPUT_TYPE_CAMERA;
+	strlcpy(i->name, "VIMC capture", sizeof(i->name));
+
+	return 0;
+}
+
+static int vimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
+{
+	/* We only have one input */
+	*i = 0;
+	return 0;
+}
+
+static int vimc_cap_s_input(struct file *file, void *priv, unsigned int i)
+{
+	/* We only have one input */
+	return i ? -EINVAL : 0;
+}
+
+static int vimc_cap_g_fmt_vid_cap(struct file *file, void *priv,
+				  struct v4l2_format *f)
+{
+	struct vimc_cap_device *vcap = video_drvdata(file);
+
+	f->fmt.pix = vcap->format;
+
+	return 0;
+}
+
+static int vimc_cap_enum_fmt_vid_cap(struct file *file, void *priv,
+				     struct v4l2_fmtdesc *f)
+{
+	struct vimc_cap_device *vcap = video_drvdata(file);
+
+	if (f->index > 0)
+		return -EINVAL;
+
+	/* We only support one format for now */
+	f->pixelformat = vcap->format.pixelformat;
+
+	return 0;
+}
+
+static const struct v4l2_file_operations vimc_cap_fops = {
+	.owner		= THIS_MODULE,
+	.open		= v4l2_fh_open,
+	.release	= vb2_fop_release,
+	.read           = vb2_fop_read,
+	.poll		= vb2_fop_poll,
+	.unlocked_ioctl = video_ioctl2,
+	.mmap           = vb2_fop_mmap,
+};
+
+static const struct v4l2_ioctl_ops vimc_cap_ioctl_ops = {
+	.vidioc_querycap = vimc_cap_querycap,
+
+	.vidioc_enum_input = vimc_cap_enum_input,
+	.vidioc_g_input = vimc_cap_g_input,
+	.vidioc_s_input = vimc_cap_s_input,
+
+	.vidioc_g_fmt_vid_cap = vimc_cap_g_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap = vimc_cap_g_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap = vimc_cap_g_fmt_vid_cap,
+	.vidioc_enum_fmt_vid_cap = vimc_cap_enum_fmt_vid_cap,
+
+	.vidioc_reqbufs = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs = vb2_ioctl_create_bufs,
+	.vidioc_querybuf = vb2_ioctl_querybuf,
+	.vidioc_qbuf = vb2_ioctl_qbuf,
+	.vidioc_dqbuf = vb2_ioctl_dqbuf,
+	.vidioc_expbuf = vb2_ioctl_expbuf,
+	.vidioc_streamon = vb2_ioctl_streamon,
+	.vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+static void vimc_cap_return_all_buffers(struct vimc_cap_device *vcap,
+					enum vb2_buffer_state state)
+{
+	struct vimc_cap_buffer *vbuf, *node;
+
+	spin_lock(&vcap->qlock);
+
+	list_for_each_entry_safe(vbuf, node, &vcap->buf_list, list) {
+		vb2_buffer_done(&vbuf->vb2.vb2_buf, state);
+		list_del(&vbuf->list);
+	}
+
+	spin_unlock(&vcap->qlock);
+}
+
+static int vimc_cap_pipeline_s_stream(struct vimc_cap_device *vcap, int enable)
+{
+	int ret;
+	struct media_pad *pad;
+	struct media_entity *entity;
+	struct v4l2_subdev *sd;
+
+	/* Start the stream in the subdevice direct connected */
+	entity = &vcap->vdev.entity;
+	pad = media_entity_remote_pad(&entity->pads[0]);
+
+	/* If we are not connected to any subdev node, it means there is nothing
+	 * to activate on the pipe (e.g. we can be connected with an input
+	 * device or we are not connected at all)
+	 */
+	if (pad == NULL || !is_media_entity_v4l2_subdev(pad->entity))
+		return 0;
+
+	entity = pad->entity;
+	sd = media_entity_to_v4l2_subdev(entity);
+
+	ret = v4l2_subdev_call(sd, video, s_stream, enable);
+	if (ret && ret != -ENOIOCTLCMD)
+		return ret;
+
+	return 0;
+}
+
+static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
+	struct media_entity *entity;
+	int ret;
+
+	vcap->sequence = 0;
+
+	/* Start the media pipeline */
+	entity = &vcap->vdev.entity;
+	ret = media_entity_pipeline_start(entity, &vcap->pipe);
+	if (ret) {
+		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
+		return ret;
+	}
+
+	/* Enable streaming from the pipe */
+	ret = vimc_cap_pipeline_s_stream(vcap, 1);
+	if (ret) {
+		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Stop the stream engine. Any remaining buffers in the stream queue are
+ * dequeued and passed on to the vb2 framework marked as STATE_ERROR.
+ */
+static void vimc_cap_stop_streaming(struct vb2_queue *vq)
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
+
+	/* Disable streaming from the pipe */
+	vimc_cap_pipeline_s_stream(vcap, 0);
+
+	/* Stop the media pipeline */
+	media_entity_pipeline_stop(&vcap->vdev.entity);
+
+	/* Release all active buffers */
+	vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_ERROR);
+}
+
+static void vimc_cap_buf_queue(struct vb2_buffer *vb2_buf)
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb2_buf->vb2_queue);
+	struct vimc_cap_buffer *buf = container_of(vb2_buf,
+					struct vimc_cap_buffer, vb2.vb2_buf);
+
+	/* If the buffer doesn't have enough size, mark it as error */
+	if (vb2_buf->planes[0].length < vcap->format.sizeimage) {
+		vb2_buffer_done(vb2_buf, VB2_BUF_STATE_ERROR);
+		return;
+	}
+
+	spin_lock(&vcap->qlock);
+
+	list_add_tail(&buf->list, &vcap->buf_list);
+
+	spin_unlock(&vcap->qlock);
+}
+
+static int vimc_cap_queue_setup(struct vb2_queue *vq,
+				unsigned int *nbuffers, unsigned int *nplanes,
+				unsigned int sizes[], void *alloc_ctxs[])
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
+
+	if (*nplanes)
+		return sizes[0] < vcap->format.sizeimage ? -EINVAL : 0;
+	/* We don't support multiplanes for now */
+	*nplanes = 1;
+	sizes[0] = vcap->format.sizeimage;
+
+	return 0;
+}
+
+static const struct vb2_ops vimc_cap_qops = {
+	.start_streaming	= vimc_cap_start_streaming,
+	.stop_streaming		= vimc_cap_stop_streaming,
+	.buf_queue		= vimc_cap_buf_queue,
+	.queue_setup		= vimc_cap_queue_setup,
+	/*
+	 * Since q->lock is set we can use the standard
+	 * vb2_ops_wait_prepare/finish helper functions.
+	 */
+	.wait_prepare		= vb2_ops_wait_prepare,
+	.wait_finish		= vb2_ops_wait_finish,
+};
+
+/* NOTE: this function is a copy of v4l2_subdev_link_validate_get_format
+ * maybe the v4l2 function should be public */
+static int vimc_cap_v4l2_subdev_link_validate_get_format(struct media_pad *pad,
+						struct v4l2_subdev_format *fmt)
+{
+	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity);
+
+	fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	fmt->pad = pad->index;
+	return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt);
+}
+
+static int vimc_cap_link_validate(struct media_link *link)
+{
+	struct v4l2_subdev_format source_fmt;
+	struct v4l2_pix_format *sink_fmt;
+	struct vimc_cap_device *vcap;
+	const struct vimc_pix_map *vpix;
+	int ret;
+
+	/* Retrieve the video capture device */
+	vcap = container_of(link->sink->entity,
+			    struct vimc_cap_device, vdev.entity);
+
+	/* If the connected node is not a subdevice type
+	 * then it's a raw node from vimc-core, ignore the link for now
+	 * TODO: remove this when there are no more raw nodes in the
+	 * core and return error instead */
+	if (!is_media_entity_v4l2_subdev(link->source->entity))
+		return 0;
+
+	/* Get the the format of the video device */
+	sink_fmt = &vcap->format;
+
+	/* Get the the format of the subdev */
+	ret = vimc_cap_v4l2_subdev_link_validate_get_format(link->source,
+							    &source_fmt);
+	if (ret)
+		return ret;
+
+	dev_dbg(vcap->dev,
+		"cap: %s: link validate formats src:%dx%d %d sink:%dx%d %d\n",
+		vcap->vdev.name,
+		source_fmt.format.width, source_fmt.format.height,
+		source_fmt.format.code,
+		sink_fmt->width, sink_fmt->height,
+		sink_fmt->pixelformat);
+
+	/* Validate the format */
+
+	vpix = vimc_pix_map_by_pixelformat(sink_fmt->pixelformat);
+	if (!vpix)
+		return -EINVAL;
+
+	/* The width, height and code must match. */
+	if (source_fmt.format.width != sink_fmt->width
+	    || source_fmt.format.height != sink_fmt->height
+	    || vpix->code != source_fmt.format.code)
+		return -EINVAL;
+
+	/* The field order must match, or the sink field order must be NONE
+	 * to support interlaced hardware connected to bridges that support
+	 * progressive formats only.
+	 */
+	if (source_fmt.format.field != sink_fmt->field &&
+	    sink_fmt->field != V4L2_FIELD_NONE)
+		return -EINVAL;
+
+	return 0;
+}
+
+static const struct media_entity_operations vimc_cap_mops = {
+	.link_validate		= vimc_cap_link_validate,
+};
+
+static void vimc_cap_destroy(struct vimc_ent_device *ved)
+{
+	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
+						    ved);
+
+	vb2_queue_release(&vcap->queue);
+	media_entity_cleanup(ved->ent);
+	video_unregister_device(&vcap->vdev);
+	vimc_pads_cleanup(vcap->ved.pads);
+	kfree(vcap);
+}
+
+static void vimc_cap_process_frame(struct vimc_ent_device *ved,
+				   struct media_pad *sink, const void *frame)
+{
+	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
+						    ved);
+	struct vimc_cap_buffer *vimc_buf;
+	void *vbuf;
+
+	/* If the stream in this node is not active, just return */
+	mutex_lock(&vcap->lock);
+	if (!vb2_is_busy(&vcap->queue)) {
+		mutex_unlock(&vcap->lock);
+		return;
+	}
+	mutex_unlock(&vcap->lock);
+
+	spin_lock(&vcap->qlock);
+
+	/* Get the first entry of the list */
+	vimc_buf = list_first_entry_or_null(&vcap->buf_list,
+					    typeof(*vimc_buf), list);
+	if (!vimc_buf) {
+		spin_unlock(&vcap->qlock);
+		return;
+	}
+
+	/* Remove this entry from the list */
+	list_del(&vimc_buf->list);
+
+	spin_unlock(&vcap->qlock);
+
+	/* Fill the buffer */
+	vimc_buf->vb2.vb2_buf.timestamp = ktime_get_ns();
+	vimc_buf->vb2.sequence = vcap->sequence++;
+	vimc_buf->vb2.field = vcap->format.field;
+
+	vbuf = vb2_plane_vaddr(&vimc_buf->vb2.vb2_buf, 0);
+
+	memcpy(vbuf, frame, vcap->format.sizeimage);
+
+	/* Set it as ready */
+	vb2_set_plane_payload(&vimc_buf->vb2.vb2_buf, 0,
+			      vcap->format.sizeimage);
+	vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag)
+{
+	int ret;
+	struct vb2_queue *q;
+	struct video_device *vdev;
+	struct vimc_cap_device *vcap;
+	const struct vimc_pix_map *vpix;
+
+	if (!v4l2_dev || !v4l2_dev->dev || !name || (num_pads && !pads_flag))
+		return ERR_PTR(-EINVAL);
+
+	/* Allocate the vimc_cap_device struct */
+	vcap = kzalloc(sizeof(*vcap), GFP_KERNEL);
+	if (!vcap)
+		return ERR_PTR(-ENOMEM);
+
+	/* Link the vimc_cap_device struct with v4l2 and dev parent */
+	vcap->v4l2_dev = v4l2_dev;
+	vcap->dev = v4l2_dev->dev;
+
+	/* Allocate the pads */
+	vcap->ved.pads = vimc_pads_init(num_pads, pads_flag);
+	if (IS_ERR(vcap->ved.pads)) {
+		ret = PTR_ERR(vcap->ved.pads);
+		goto err_free_vcap;
+	}
+
+	/* Initialize the media entity */
+	vcap->vdev.entity.name = name;
+	vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L;
+	ret = media_entity_pads_init(&vcap->vdev.entity,
+				     num_pads, vcap->ved.pads);
+	if (ret)
+		goto err_clean_pads;
+
+	/* Initialize the lock */
+	mutex_init(&vcap->lock);
+
+	/* Initialize the vb2 queue */
+	q = &vcap->queue;
+	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	q->io_modes = VB2_MMAP | VB2_DMABUF;
+	q->drv_priv = vcap;
+	q->buf_struct_size = sizeof(struct vimc_cap_buffer);
+	q->ops = &vimc_cap_qops;
+	q->mem_ops = &vb2_vmalloc_memops;
+	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	q->min_buffers_needed = 2;
+	q->lock = &vcap->lock;
+
+	ret = vb2_queue_init(q);
+	if (ret) {
+		dev_err(vcap->dev,
+			"vb2 queue init failed (err=%d)\n", ret);
+		goto err_clean_m_ent;
+	}
+
+	/* Initialize buffer list and its lock */
+	INIT_LIST_HEAD(&vcap->buf_list);
+	spin_lock_init(&vcap->qlock);
+
+	/* Set the frame format (this is hardcoded for now) */
+	vcap->format.width = 640;
+	vcap->format.height = 480;
+	vcap->format.pixelformat = V4L2_PIX_FMT_RGB24;
+	vcap->format.field = V4L2_FIELD_NONE;
+	vcap->format.colorspace = V4L2_COLORSPACE_SRGB;
+
+	vpix = vimc_pix_map_by_pixelformat(vcap->format.pixelformat);
+	/* This should never be NULL, as we won't allow any format
+	 * other then the ones in the vimc_pix_map_list table */
+	BUG_ON(!vpix);
+
+	vcap->format.bytesperline = vcap->format.width * vpix->bpp;
+	vcap->format.sizeimage = vcap->format.bytesperline * vcap->format.height;
+
+	/* Fill the vimc_ent_device struct */
+	vcap->ved.destroy = vimc_cap_destroy;
+	vcap->ved.ent = &vcap->vdev.entity;
+	vcap->ved.process_frame = vimc_cap_process_frame;
+
+	/* Initialize the video_device struct */
+	vdev = &vcap->vdev;
+	vdev->entity.ops = &vimc_cap_mops;
+	vdev->release = video_device_release_empty;
+	vdev->fops = &vimc_cap_fops;
+	vdev->ioctl_ops = &vimc_cap_ioctl_ops;
+	vdev->lock = &vcap->lock;
+	vdev->queue = q;
+	vdev->v4l2_dev = vcap->v4l2_dev;
+	vdev->vfl_dir = VFL_DIR_RX;
+	strlcpy(vdev->name, name, sizeof(vdev->name));
+	video_set_drvdata(vdev, vcap);
+
+	/* Register the video_device with the v4l2 and the media framework */
+	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+	if (ret) {
+		dev_err(vcap->dev,
+			"video register failed (err=%d)\n", ret);
+		goto err_release_queue;
+	}
+
+	return &vcap->ved;
+
+err_release_queue:
+	vb2_queue_release(q);
+err_clean_m_ent:
+	media_entity_cleanup(&vcap->vdev.entity);
+err_clean_pads:
+	vimc_pads_cleanup(vcap->ved.pads);
+err_free_vcap:
+	kfree(vcap);
+
+	return ERR_PTR(ret);
+}
diff --git a/drivers/media/platform/vimc/vimc-capture.h b/drivers/media/platform/vimc/vimc-capture.h
new file mode 100644
index 0000000..c52781a
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-capture.h
@@ -0,0 +1,28 @@
+/*
+ * vimc-capture.h Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015 Helen Fornazier <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIMC_CAPTURE_H_
+#define _VIMC_CAPTURE_H_
+
+#include "vimc-core.h"
+
+struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag);
+
+#endif
diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c
new file mode 100644
index 0000000..83826ca
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-core.c
@@ -0,0 +1,595 @@
+/*
+ * vimc-core.c Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015 Helen Fornazier <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <media/media-device.h>
+#include <media/v4l2-device.h>
+
+#include "vimc-capture.h"
+#include "vimc-core.h"
+#include "vimc-sensor.h"
+
+#define VIMC_PDEV_NAME "vimc"
+#define VIMC_MDEV_MODEL_NAME "VIMC MDEV"
+
+#define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) {	\
+	.src_ent = src,						\
+	.src_pad = srcpad,					\
+	.sink_ent = sink,					\
+	.sink_pad = sinkpad,					\
+	.flags = link_flags,					\
+}
+
+#define VIMC_PIX_MAP(_code, _bpp, _pixelformat) {	\
+	.code = _code,					\
+	.pixelformat = _pixelformat,			\
+	.bpp = _bpp,					\
+}
+
+struct vimc_device {
+	/* The pipeline configuration
+	 * (filled before calling vimc_device_register) */
+	const struct vimc_pipeline_config *pipe_cfg;
+
+	/* The Associated media_device parent */
+	struct media_device mdev;
+
+	/* Internal v4l2 parent device*/
+	struct v4l2_device v4l2_dev;
+
+	/* Internal topology */
+	struct vimc_ent_device **ved;
+};
+
+/**
+ * enum vimc_ent_node - Select the functionality of a node in the topology
+ * @VIMC_ENT_NODE_SENSOR:	A node of type SENSOR simulates a camera sensor
+ *				generating internal images in bayer format and
+ *				propagating those images through the pipeline
+ * @VIMC_ENT_NODE_CAPTURE:	A node of type CAPTURE is a v4l2 video_device
+ *				that exposes the received image from the
+ *				pipeline to the user space
+ * @VIMC_ENT_NODE_INPUT:	A node of type INPUT is a v4l2 video_device that
+ *				receives images from the user space and
+ *				propagates them through the pipeline
+ * @VIMC_ENT_NODE_DEBAYER:	A node type DEBAYER expects to receive a frame
+ *				in bayer format converts it to RGB
+ * @VIMC_ENT_NODE_SCALER:	A node of type SCALER scales the received image
+ *				by a given multiplier
+ *
+ * This enum is used in the entity configuration struct to allow the definition
+ * of a custom topology specifying the role of each node on it.
+ */
+enum vimc_ent_node {
+	VIMC_ENT_NODE_SENSOR,
+	VIMC_ENT_NODE_CAPTURE,
+	VIMC_ENT_NODE_INPUT,
+	VIMC_ENT_NODE_DEBAYER,
+	VIMC_ENT_NODE_SCALER,
+};
+
+/* Structure which describes individual configuration for each entity */
+struct vimc_ent_config {
+	const char *name;
+	size_t pads_qty;
+	const unsigned long *pads_flag;
+	enum vimc_ent_node node;
+};
+
+/* Structure which describes links between entities */
+struct vimc_ent_link {
+	unsigned int src_ent;
+	u16 src_pad;
+	unsigned int sink_ent;
+	u16 sink_pad;
+	u32 flags;
+};
+
+/* Structure which describes the whole topology */
+struct vimc_pipeline_config {
+	const struct vimc_ent_config *ents;
+	size_t num_ents;
+	const struct vimc_ent_link *links;
+	size_t num_links;
+};
+
+/* --------------------------------------------------------------------------
+ * Topology Configuration
+ */
+
+static const struct vimc_ent_config ent_config[] = {
+	{
+		.name = "Sensor A",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_SENSOR,
+	},
+	{
+		.name = "Sensor B",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_SENSOR,
+	},
+	{
+		.name = "Debayer A",
+		.pads_qty = 2,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
+						     MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_DEBAYER,
+	},
+	{
+		.name = "Debayer B",
+		.pads_qty = 2,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
+						     MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_DEBAYER,
+	},
+	{
+		.name = "Raw Capture 0",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
+		.node = VIMC_ENT_NODE_CAPTURE,
+	},
+	{
+		.name = "Raw Capture 1",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
+		.node = VIMC_ENT_NODE_CAPTURE,
+	},
+	{
+		.name = "RGB/YUV Input",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_INPUT,
+	},
+	{
+		.name = "Scaler",
+		.pads_qty = 2,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
+						     MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_SCALER,
+	},
+	{
+		.name = "RGB/YUV Capture",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
+		.node = VIMC_ENT_NODE_CAPTURE,
+	},
+};
+
+static const struct vimc_ent_link ent_links[] = {
+	/* Link: Sensor A (Pad 0)->(Pad 0) Debayer A */
+	VIMC_ENT_LINK(0, 0, 2, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Sensor A (Pad 0)->(Pad 0) Raw Capture 0 */
+	VIMC_ENT_LINK(0, 0, 4, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Sensor B (Pad 0)->(Pad 0) Debayer B */
+	VIMC_ENT_LINK(1, 0, 3, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Sensor B (Pad 0)->(Pad 0) Raw Capture 1 */
+	VIMC_ENT_LINK(1, 0, 5, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Debayer A (Pad 1)->(Pad 0) Scaler */
+	VIMC_ENT_LINK(2, 1, 7, 0, MEDIA_LNK_FL_ENABLED),
+	/* Link: Debayer B (Pad 1)->(Pad 0) Scaler */
+	VIMC_ENT_LINK(3, 1, 7, 0, 0),
+	/* Link: RGB/YUV Input (Pad 0)->(Pad 0) Scaler */
+	VIMC_ENT_LINK(6, 0, 7, 0, 0),
+	/* Link: Scaler (Pad 1)->(Pad 0) RGB/YUV Capture */
+	VIMC_ENT_LINK(7, 1, 8, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+};
+
+static const struct vimc_pipeline_config pipe_cfg = {
+	.ents		= ent_config,
+	.num_ents	= ARRAY_SIZE(ent_config),
+	.links		= ent_links,
+	.num_links	= ARRAY_SIZE(ent_links)
+};
+
+/* -------------------------------------------------------------------------- */
+
+static void vimc_dev_release(struct device *dev)
+{}
+
+static struct platform_device vimc_pdev = {
+	.name		= VIMC_PDEV_NAME,
+	.dev.release	= vimc_dev_release,
+};
+
+const struct vimc_pix_map vimc_pix_map_list[] = {
+	/* TODO: add all missing formats */
+
+	/* RGB formats */
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_BGR888_1X24, 3, V4L2_PIX_FMT_BGR24),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_RGB888_1X24, 3, V4L2_PIX_FMT_RGB24),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_ARGB8888_1X32, 4, V4L2_PIX_FMT_ARGB32),
+
+	/* Bayer formats */
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR8_1X8, 1, V4L2_PIX_FMT_SBGGR8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG8_1X8, 1, V4L2_PIX_FMT_SGBRG8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG8_1X8, 1, V4L2_PIX_FMT_SGRBG8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB8_1X8, 1, V4L2_PIX_FMT_SRGGB8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR10_1X10, 2, V4L2_PIX_FMT_SBGGR10),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG10_1X10, 2, V4L2_PIX_FMT_SGBRG10),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG10_1X10, 2, V4L2_PIX_FMT_SGRBG10),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB10_1X10, 2, V4L2_PIX_FMT_SRGGB10),
+	/* 10bit raw bayer a-law compressed to 8 bits */
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8, 1, V4L2_PIX_FMT_SBGGR10ALAW8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8, 1, V4L2_PIX_FMT_SGBRG10ALAW8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8, 1, V4L2_PIX_FMT_SGRBG10ALAW8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8, 1, V4L2_PIX_FMT_SRGGB10ALAW8),
+	/* 10bit raw bayer DPCM compressed to 8 bits */
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, 1, V4L2_PIX_FMT_SBGGR10DPCM8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, 1, V4L2_PIX_FMT_SGBRG10DPCM8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, 1, V4L2_PIX_FMT_SGRBG10DPCM8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, 1, V4L2_PIX_FMT_SRGGB10DPCM8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR12_1X12, 2, V4L2_PIX_FMT_SBGGR12),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG12_1X12, 2, V4L2_PIX_FMT_SGBRG12),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG12_1X12, 2, V4L2_PIX_FMT_SGRBG12),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB12_1X12, 2, V4L2_PIX_FMT_SRGGB12),
+
+	/* End */
+	{0, 0, 0}
+};
+
+const struct vimc_pix_map *vimc_pix_map_by_code(u32 code)
+{
+	unsigned int i;
+
+	for (i = 0; vimc_pix_map_list[i].bpp; i++) {
+		if (vimc_pix_map_list[i].code == code)
+			return &vimc_pix_map_list[i];
+	}
+	return NULL;
+}
+
+const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat)
+{
+	unsigned int i;
+
+	for (i = 0; vimc_pix_map_list[i].bpp; i++) {
+		if (vimc_pix_map_list[i].pixelformat == pixelformat)
+			return &vimc_pix_map_list[i];
+	}
+	return NULL;
+}
+
+int vimc_propagate_frame(struct device *dev,
+			 struct media_pad *src, const void *frame)
+{
+	struct media_link *link;
+	struct vimc_device *vimc = dev_get_drvdata(dev);
+
+	if (!(src->flags & MEDIA_PAD_FL_SOURCE))
+		return -EINVAL;
+
+	/* Send this frame to all sink pads that are direct linked */
+	list_for_each_entry(link, &src->entity->links, list) {
+		if (link->source == src &&
+		    (link->flags & MEDIA_LNK_FL_ENABLED)) {
+			struct vimc_ent_device *ved;
+
+			ved = vimc->ved[link->sink->entity->use_count];
+			if (ved && ved->process_frame)
+				ved->process_frame(ved, link->sink, frame);
+		}
+	}
+
+	return 0;
+}
+
+static void vimc_device_unregister(struct vimc_device *vimc)
+{
+	unsigned int i;
+
+	/* Cleanup (only initialized) entities */
+	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
+		if (vimc->ved[i] && vimc->ved[i]->destroy)
+			vimc->ved[i]->destroy(vimc->ved[i]);
+
+		vimc->ved[i] = NULL;
+	}
+	v4l2_device_unregister(&vimc->v4l2_dev);
+
+	media_device_unregister(&vimc->mdev);
+	media_device_cleanup(&vimc->mdev);
+}
+
+/* Helper function to allocate and initialize pads */
+struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag)
+{
+	unsigned int i;
+	struct media_pad *pads;
+
+	/* Allocate memory for the pads */
+	pads = kcalloc(num_pads, sizeof(*pads), GFP_KERNEL);
+	if (!pads)
+		return ERR_PTR(-ENOMEM);
+
+	/* Initialize the pads */
+	for (i = 0; i < num_pads; i++) {
+		pads[i].index = i;
+		pads[i].flags = pads_flag[i];
+	}
+
+	return pads;
+}
+
+/* TODO: remove this function when all the
+ * entities specific code are implemented */
+static void vimc_raw_destroy(struct vimc_ent_device *ved)
+{
+	media_device_unregister_entity(ved->ent);
+
+	media_entity_cleanup(ved->ent);
+
+	vimc_pads_cleanup(ved->pads);
+
+	kfree(ved->ent);
+
+	kfree(ved);
+}
+
+/* TODO: remove this function when all the
+ * entities specific code are implemented */
+static struct vimc_ent_device *vimc_raw_create(struct v4l2_device *v4l2_dev,
+					       const char *const name,
+					       u16 num_pads,
+					       const unsigned long *pads_flag)
+{
+	int ret;
+	struct vimc_ent_device *ved;
+
+	/* Allocate the main ved struct */
+	ved = kzalloc(sizeof(*ved), GFP_KERNEL);
+	if (!ved)
+		return ERR_PTR(-ENOMEM);
+
+	/* Allocate the media entity */
+	ved->ent = kzalloc(sizeof(*ved->ent), GFP_KERNEL);
+	if (!ved->ent) {
+		ret = -ENOMEM;
+		goto err_free_ved;
+	}
+
+	/* Allocate the pads */
+	ved->pads = vimc_pads_init(num_pads, pads_flag);
+	if (IS_ERR(ved->pads)) {
+		ret = PTR_ERR(ved->pads);
+		goto err_free_ent;
+	}
+
+	/* Initialize the media entity */
+	ved->ent->name = name;
+	ved->ent->function = MEDIA_ENT_F_IO_V4L;
+	ret = media_entity_pads_init(ved->ent, num_pads, ved->pads);
+	if (ret)
+		goto err_cleanup_pads;
+
+	/* Register the media entity */
+	ret = media_device_register_entity(v4l2_dev->mdev, ved->ent);
+	if (ret)
+		goto err_cleanup_entity;
+
+	/* Fill out the destroy function and return */
+	ved->destroy = vimc_raw_destroy;
+	return ved;
+
+err_cleanup_entity:
+	media_entity_cleanup(ved->ent);
+err_cleanup_pads:
+	vimc_pads_cleanup(ved->pads);
+err_free_ent:
+	kfree(ved->ent);
+err_free_ved:
+	kfree(ved);
+
+	return ERR_PTR(ret);
+}
+
+static int vimc_device_register(struct vimc_device *vimc)
+{
+	unsigned int i;
+	int ret = 0;
+
+	/* Allocate memory for the vimc_ent_devices pointers */
+	vimc->ved = devm_kcalloc(vimc->mdev.dev, vimc->pipe_cfg->num_ents,
+				 sizeof(*vimc->ved), GFP_KERNEL);
+	if (!vimc->ved)
+		return -ENOMEM;
+
+	/* Register the media device */
+	ret = media_device_register(&vimc->mdev);
+	if (ret) {
+		dev_err(vimc->mdev.dev,
+			"media device register failed (err=%d)\n", ret);
+		return ret;
+	}
+
+	/* Link the media device within the v4l2_device */
+	vimc->v4l2_dev.mdev = &vimc->mdev;
+
+	/* Register the v4l2 struct */
+	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);
+		return ret;
+	}
+
+	/* Initialize entities */
+	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
+		struct vimc_ent_device *(*create_func)(struct v4l2_device *,
+						       const char *const,
+						       u16,
+						       const unsigned long *);
+
+		/* Register the specific node */
+		switch (vimc->pipe_cfg->ents[i].node) {
+		case VIMC_ENT_NODE_SENSOR:
+			create_func = vimc_sen_create;
+			break;
+
+		case VIMC_ENT_NODE_CAPTURE:
+			create_func = vimc_cap_create;
+			break;
+
+		/* TODO: Instantiate the specific topology node */
+		case VIMC_ENT_NODE_INPUT:
+		case VIMC_ENT_NODE_DEBAYER:
+		case VIMC_ENT_NODE_SCALER:
+		default:
+			/* TODO: remove this when all the entities specific
+			 * code are implemented */
+			create_func = vimc_raw_create;
+			break;
+		}
+
+		vimc->ved[i] = create_func(&vimc->v4l2_dev,
+					   vimc->pipe_cfg->ents[i].name,
+					   vimc->pipe_cfg->ents[i].pads_qty,
+					   vimc->pipe_cfg->ents[i].pads_flag);
+		if (IS_ERR(vimc->ved[i])) {
+			ret = PTR_ERR(vimc->ved[i]);
+			vimc->ved[i] = NULL;
+			goto err;
+		}
+
+		/* Set use_count to keep track of the ved structure */
+		vimc->ved[i]->ent->use_count = i;
+	}
+
+	/* 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];
+
+		ret = media_create_pad_link(vimc->ved[link->src_ent]->ent,
+					    link->src_pad,
+					    vimc->ved[link->sink_ent]->ent,
+					    link->sink_pad,
+					    link->flags);
+		if (ret)
+			goto err;
+	}
+
+	/* 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);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	/* Destroy de so far created topology */
+	vimc_device_unregister(vimc);
+
+	return ret;
+}
+
+static int vimc_probe(struct platform_device *pdev)
+{
+	struct vimc_device *vimc;
+	int ret;
+
+	/* Prepare the vimc topology structure */
+
+	/* Allocate memory for the vimc structure */
+	vimc = devm_kzalloc(&pdev->dev, sizeof(*vimc), GFP_KERNEL);
+	if (!vimc)
+		return -ENOMEM;
+
+	/* Set the pipeline configuration struct */
+	vimc->pipe_cfg = &pipe_cfg;
+
+	/* Initialize media device */
+	strlcpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME,
+		sizeof(vimc->mdev.model));
+	vimc->mdev.dev = &pdev->dev;
+	media_device_init(&vimc->mdev);
+
+	/* Create vimc topology */
+	ret = vimc_device_register(vimc);
+	if (ret) {
+		dev_err(vimc->mdev.dev,
+			"vimc device registration failed (err=%d)\n", ret);
+		return ret;
+	}
+
+	/* Link the topology object with the platform device object */
+	platform_set_drvdata(pdev, vimc);
+
+	return 0;
+}
+
+static int vimc_remove(struct platform_device *pdev)
+{
+	struct vimc_device *vimc;
+
+	/* Get the topology object linked with the platform device object */
+	vimc = platform_get_drvdata(pdev);
+
+	/* Destroy all the topology */
+	vimc_device_unregister(vimc);
+
+	return 0;
+}
+
+static struct platform_driver vimc_pdrv = {
+	.probe		= vimc_probe,
+	.remove		= vimc_remove,
+	.driver		= {
+		.name	= VIMC_PDEV_NAME,
+	},
+};
+
+static int __init vimc_init(void)
+{
+	int ret;
+
+	ret = platform_device_register(&vimc_pdev);
+	if (ret) {
+		dev_err(&vimc_pdev.dev,
+			"platform device registration failed (err=%d)\n", ret);
+		return ret;
+	}
+
+	ret = platform_driver_register(&vimc_pdrv);
+	if (ret) {
+		dev_err(&vimc_pdev.dev,
+			"platform driver registration failed (err=%d)\n", ret);
+
+		platform_device_unregister(&vimc_pdev);
+	}
+
+	return ret;
+}
+
+static void __exit vimc_exit(void)
+{
+	platform_driver_unregister(&vimc_pdrv);
+
+	platform_device_unregister(&vimc_pdev);
+}
+
+module_init(vimc_init);
+module_exit(vimc_exit);
+
+MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC)");
+MODULE_AUTHOR("Helen Fornazier <helen.fornazier@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/vimc/vimc-core.h b/drivers/media/platform/vimc/vimc-core.h
new file mode 100644
index 0000000..924addd
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-core.h
@@ -0,0 +1,56 @@
+/*
+ * vimc-core.h Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015 Helen Fornazier <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIMC_CORE_H_
+#define _VIMC_CORE_H_
+
+#include <linux/slab.h>
+#include <media/v4l2-device.h>
+
+/* Struct which matches the MEDIA_BUS_FMT_ codes with the corresponding
+ * V4L2_PIX_FMT_ fourcc pixelformat and its bytes per pixel (bpp) */
+struct vimc_pix_map {
+	unsigned int code;
+	unsigned int bpp;
+	u32 pixelformat;
+};
+extern const struct vimc_pix_map vimc_pix_map_list[];
+
+struct vimc_ent_device {
+	struct media_entity *ent;
+	struct media_pad *pads;
+	void (*destroy)(struct vimc_ent_device *);
+	void (*process_frame)(struct vimc_ent_device *ved,
+			      struct media_pad *sink, const void *frame);
+};
+
+int vimc_propagate_frame(struct device *dev,
+			 struct media_pad *src, const void *frame);
+
+/* Helper functions to allocate/initialize pads and free them */
+struct media_pad *vimc_pads_init(u16 num_pads,
+				 const unsigned long *pads_flag);
+static inline void vimc_pads_cleanup(struct media_pad *pads)
+{
+	kfree(pads);
+}
+
+const struct vimc_pix_map *vimc_pix_map_by_code(u32 code);
+
+const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat);
+
+#endif
diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c
new file mode 100644
index 0000000..6f9c1d3
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-sensor.c
@@ -0,0 +1,278 @@
+/*
+ * vimc-sensor.c Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015 Helen Fornazier <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/freezer.h>
+#include <linux/kthread.h>
+#include <linux/vmalloc.h>
+#include <linux/v4l2-mediabus.h>
+#include <media/v4l2-subdev.h>
+
+#include "vimc-sensor.h"
+
+struct vimc_sen_device {
+	struct vimc_ent_device ved;
+	struct v4l2_subdev sd;
+	struct v4l2_device *v4l2_dev;
+	struct device *dev;
+	struct task_struct *kthread_sen;
+	u8 *frame;
+	/* The active format */
+	struct v4l2_mbus_framefmt mbus_format;
+	int frame_size;
+};
+
+static int vimc_sen_enum_mbus_code(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_pad_config *cfg,
+				   struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd);
+
+	/* Check if it is a valid pad */
+	if (code->pad >= vsen->sd.entity.num_pads)
+		return -EINVAL;
+
+	code->code = vsen->mbus_format.code;
+
+	return 0;
+}
+
+static int vimc_sen_enum_frame_size(struct v4l2_subdev *sd,
+				    struct v4l2_subdev_pad_config *cfg,
+				    struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd);
+
+	/* Check if it is a valid pad */
+	if (fse->pad >= vsen->sd.entity.num_pads ||
+	    !(vsen->sd.entity.pads[fse->pad].flags & MEDIA_PAD_FL_SOURCE))
+		return -EINVAL;
+
+	/* TODO: Add support to other formats */
+	if (fse->index)
+		return -EINVAL;
+
+	/* TODO: Add support for other codes */
+	if (fse->code != vsen->mbus_format.code)
+		return -EINVAL;
+
+	fse->min_width = vsen->mbus_format.width;
+	fse->max_width = vsen->mbus_format.width;
+	fse->min_height = vsen->mbus_format.height;
+	fse->max_height = vsen->mbus_format.height;
+
+	return 0;
+}
+
+static int vimc_sen_get_fmt(struct v4l2_subdev *sd,
+			    struct v4l2_subdev_pad_config *cfg,
+			    struct v4l2_subdev_format *format)
+{
+	struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd);
+
+	format->format = vsen->mbus_format;
+
+	return 0;
+}
+
+static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = {
+	.enum_mbus_code		= vimc_sen_enum_mbus_code,
+	.enum_frame_size	= vimc_sen_enum_frame_size,
+	.get_fmt		= vimc_sen_get_fmt,
+	/* TODO: Add support to other formats */
+	.set_fmt		= vimc_sen_get_fmt,
+};
+
+/* media operations */
+static const struct media_entity_operations vimc_sen_mops = {
+	.link_validate = v4l2_subdev_link_validate,
+};
+
+static int vimc_thread_sen(void *data)
+{
+	unsigned int i;
+	struct vimc_sen_device *vsen = data;
+
+	set_freezable();
+
+	for (;;) {
+		try_to_freeze();
+		if (kthread_should_stop())
+			break;
+
+		memset(vsen->frame, 100, vsen->frame_size);
+
+		/* Send the frame to all source pads */
+		for (i = 0; i < vsen->sd.entity.num_pads; i++)
+			if (vsen->sd.entity.pads[i].flags & MEDIA_PAD_FL_SOURCE)
+				vimc_propagate_frame(vsen->dev,
+						     &vsen->sd.entity.pads[i],
+						     vsen->frame);
+
+		/* 60 frames per second */
+		schedule_timeout_interruptible(HZ/60);
+	}
+
+	return 0;
+}
+
+static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	int ret;
+	struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd);
+
+	if (enable) {
+		const struct vimc_pix_map *vpix;
+
+		if (vsen->kthread_sen)
+			return -EINVAL;
+
+		/* Calculate the frame size */
+		vpix = vimc_pix_map_by_code(vsen->mbus_format.code);
+		/* This should never be NULL, as we won't allow any format
+		 * other then the ones in the vimc_pix_map_list table */
+		BUG_ON(!vpix);
+		vsen->frame_size = vsen->mbus_format.width * vpix->bpp *
+				   vsen->mbus_format.height;
+
+		/* Allocate the frame buffer. Use vmalloc to be able to
+		 * allocate a large amount of memory*/
+		vsen->frame = vmalloc(vsen->frame_size);
+		if (!vsen->frame)
+			return -ENOMEM;
+
+		/* Initialize the image generator thread */
+		vsen->kthread_sen = kthread_run(vimc_thread_sen, vsen,
+						"%s-sen", vsen->v4l2_dev->name);
+		if (IS_ERR(vsen->kthread_sen)) {
+			v4l2_err(vsen->v4l2_dev, "kernel_thread() failed\n");
+			vfree(vsen->frame);
+			vsen->frame = NULL;
+			return PTR_ERR(vsen->kthread_sen);
+		}
+	} else {
+		if (!vsen->kthread_sen)
+			return -EINVAL;
+
+		/* Stop image generator */
+		ret = kthread_stop(vsen->kthread_sen);
+		vsen->kthread_sen = NULL;
+
+		vfree(vsen->frame);
+		vsen->frame = NULL;
+		return ret;
+	}
+
+	return 0;
+}
+
+struct v4l2_subdev_video_ops vimc_sen_video_ops = {
+	.s_stream = vimc_sen_s_stream,
+};
+
+static const struct v4l2_subdev_ops vimc_sen_ops = {
+	.pad = &vimc_sen_pad_ops,
+	.video = &vimc_sen_video_ops,
+};
+
+static void vimc_sen_destroy(struct vimc_ent_device *ved)
+{
+	struct vimc_sen_device *vsen = container_of(ved,
+						struct vimc_sen_device, ved);
+
+	media_entity_cleanup(ved->ent);
+	v4l2_device_unregister_subdev(&vsen->sd);
+	kfree(vsen);
+}
+
+struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag)
+{
+	int ret;
+	struct vimc_sen_device *vsen;
+
+	if (!v4l2_dev || !v4l2_dev->dev || !name || (num_pads && !pads_flag))
+		return ERR_PTR(-EINVAL);
+
+	/* Allocate the vsen struct */
+	vsen = kzalloc(sizeof(*vsen), GFP_KERNEL);
+	if (!vsen)
+		return ERR_PTR(-ENOMEM);
+
+	/* Link the vimc_sen_device struct with the v4l2 parent */
+	vsen->v4l2_dev = v4l2_dev;
+	/* Link the vimc_sen_device struct with the dev parent */
+	vsen->dev = v4l2_dev->dev;
+
+	/* Allocate the pads */
+	vsen->ved.pads = vimc_pads_init(num_pads, pads_flag);
+	if (IS_ERR(vsen->ved.pads)) {
+		ret = PTR_ERR(vsen->ved.pads);
+		goto err_free_vsen;
+	}
+
+	/* Initialize the media entity */
+	vsen->sd.entity.name = name;
+	vsen->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+	ret = media_entity_pads_init(&vsen->sd.entity,
+				     num_pads, vsen->ved.pads);
+	if (ret)
+		goto err_clean_pads;
+
+	/* Set the active frame format (this is hardcoded for now) */
+	vsen->mbus_format.width = 640;
+	vsen->mbus_format.height = 480;
+	vsen->mbus_format.code = MEDIA_BUS_FMT_RGB888_1X24;
+	vsen->mbus_format.field = V4L2_FIELD_NONE;
+	vsen->mbus_format.colorspace = V4L2_COLORSPACE_SRGB;
+	vsen->mbus_format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
+	vsen->mbus_format.xfer_func = V4L2_XFER_FUNC_SRGB;
+
+	/* Fill the vimc_ent_device struct */
+	vsen->ved.destroy = vimc_sen_destroy;
+	vsen->ved.ent = &vsen->sd.entity;
+
+	/* Initialize the subdev */
+	v4l2_subdev_init(&vsen->sd, &vimc_sen_ops);
+	vsen->sd.entity.ops = &vimc_sen_mops;
+	vsen->sd.owner = THIS_MODULE;
+	strlcpy(vsen->sd.name, name, sizeof(vsen->sd.name));
+	v4l2_set_subdevdata(&vsen->sd, vsen);
+
+	/* Expose this subdev to user space */
+	vsen->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	/* Register the subdev with the v4l2 and the media framework */
+	ret = v4l2_device_register_subdev(vsen->v4l2_dev, &vsen->sd);
+	if (ret) {
+		dev_err(vsen->dev,
+			"subdev register failed (err=%d)\n", ret);
+		goto err_clean_m_ent;
+	}
+
+	return &vsen->ved;
+
+err_clean_m_ent:
+	media_entity_cleanup(&vsen->sd.entity);
+err_clean_pads:
+	vimc_pads_cleanup(vsen->ved.pads);
+err_free_vsen:
+	kfree(vsen);
+
+	return ERR_PTR(ret);
+}
diff --git a/drivers/media/platform/vimc/vimc-sensor.h b/drivers/media/platform/vimc/vimc-sensor.h
new file mode 100644
index 0000000..67b7e07
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-sensor.h
@@ -0,0 +1,28 @@
+/*
+ * vimc-sensor.h Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015 Helen Fornazier <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIMC_SENSOR_H_
+#define _VIMC_SENSOR_H_
+
+#include "vimc-core.h"
+
+struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag);
+
+#endif
-- 
1.9.1


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

* Re: [PATCH v4] [media] vimc: Virtual Media Controller core, capture and sensor
  2016-05-31 15:02 [PATCH v4] [media] vimc: Virtual Media Controller core, capture and sensor Helen Koike
@ 2016-06-12  1:59 ` kbuild test robot
  2016-06-12  2:11 ` kbuild test robot
  2016-07-01 12:39 ` Hans Verkuil
  2 siblings, 0 replies; 41+ messages in thread
From: kbuild test robot @ 2016-06-12  1:59 UTC (permalink / raw)
  To: Helen Koike
  Cc: kbuild-all, linux-media, laurent.pinchart, hverkuil, jgebben,
	mchehab, Helen Fornazier

[-- Attachment #1: Type: text/plain, Size: 4784 bytes --]

Hi,

[auto build test ERROR on linuxtv-media/master]
[also build test ERROR on v4.7-rc2 next-20160609]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Helen-Koike/vimc-Virtual-Media-Controller-core-capture-and-sensor/20160531-230840
base:   git://linuxtv.org/media_tree.git master
config: i386-randconfig-x010-06120748 (attached as .config)
compiler: gcc-6 (Debian 6.1.1-1) 6.1.1 20160430
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All error/warnings (new ones prefixed by >>):

warning: (VIDEO_VIMC) selects VIDEO_V4L2_SUBDEV_API which has unmet direct dependencies (MEDIA_SUPPORT && VIDEO_DEV && MEDIA_CONTROLLER)
   In file included from include/linux/init.h:4:0,
                    from drivers/media/pci/cx88/cx88-cards.c:23:
   include/media/v4l2-subdev.h: In function 'v4l2_subdev_get_try_format':
>> include/media/v4l2-subdev.h:770:19: error: 'struct v4l2_subdev' has no member named 'entity'
      BUG_ON(pad >= sd->entity.num_pads);   \
                      ^
   include/linux/compiler.h:170:42: note: in definition of macro 'unlikely'
    # define unlikely(x) __builtin_expect(!!(x), 0)
                                             ^
>> include/media/v4l2-subdev.h:770:3: note: in expansion of macro 'BUG_ON'
      BUG_ON(pad >= sd->entity.num_pads);   \
      ^~~~~~
>> include/media/v4l2-subdev.h:774:1: note: in expansion of macro '__V4L2_SUBDEV_MK_GET_TRY'
    __V4L2_SUBDEV_MK_GET_TRY(v4l2_mbus_framefmt, v4l2_subdev_get_try_format, try_fmt)
    ^~~~~~~~~~~~~~~~~~~~~~~~
   include/media/v4l2-subdev.h: In function 'v4l2_subdev_get_try_crop':
>> include/media/v4l2-subdev.h:770:19: error: 'struct v4l2_subdev' has no member named 'entity'
      BUG_ON(pad >= sd->entity.num_pads);   \
                      ^
   include/linux/compiler.h:170:42: note: in definition of macro 'unlikely'
    # define unlikely(x) __builtin_expect(!!(x), 0)
                                             ^
>> include/media/v4l2-subdev.h:770:3: note: in expansion of macro 'BUG_ON'
      BUG_ON(pad >= sd->entity.num_pads);   \
      ^~~~~~
   include/media/v4l2-subdev.h:775:1: note: in expansion of macro '__V4L2_SUBDEV_MK_GET_TRY'
    __V4L2_SUBDEV_MK_GET_TRY(v4l2_rect, v4l2_subdev_get_try_crop, try_crop)
    ^~~~~~~~~~~~~~~~~~~~~~~~
   include/media/v4l2-subdev.h: In function 'v4l2_subdev_get_try_compose':
>> include/media/v4l2-subdev.h:770:19: error: 'struct v4l2_subdev' has no member named 'entity'
      BUG_ON(pad >= sd->entity.num_pads);   \
                      ^
   include/linux/compiler.h:170:42: note: in definition of macro 'unlikely'
    # define unlikely(x) __builtin_expect(!!(x), 0)
                                             ^
>> include/media/v4l2-subdev.h:770:3: note: in expansion of macro 'BUG_ON'
      BUG_ON(pad >= sd->entity.num_pads);   \
      ^~~~~~
   include/media/v4l2-subdev.h:776:1: note: in expansion of macro '__V4L2_SUBDEV_MK_GET_TRY'
    __V4L2_SUBDEV_MK_GET_TRY(v4l2_rect, v4l2_subdev_get_try_compose, try_compose)
    ^~~~~~~~~~~~~~~~~~~~~~~~

vim +770 include/media/v4l2-subdev.h

5e6ff7c1 Sakari Ailus      2012-02-15  764  #define __V4L2_SUBDEV_MK_GET_TRY(rtype, fun_name, field_name)		\
5e6ff7c1 Sakari Ailus      2012-02-15  765  	static inline struct rtype *					\
f7234138 Hans Verkuil      2015-03-04  766  	fun_name(struct v4l2_subdev *sd,				\
f7234138 Hans Verkuil      2015-03-04  767  		 struct v4l2_subdev_pad_config *cfg,			\
5e6ff7c1 Sakari Ailus      2012-02-15  768  		 unsigned int pad)					\
5e6ff7c1 Sakari Ailus      2012-02-15  769  	{								\
f7234138 Hans Verkuil      2015-03-04 @770  		BUG_ON(pad >= sd->entity.num_pads);			\
f7234138 Hans Verkuil      2015-03-04  771  		return &cfg[pad].field_name;				\
7cd5a16b Stanimir Varbanov 2010-05-21  772  	}
7cd5a16b Stanimir Varbanov 2010-05-21  773  
f7234138 Hans Verkuil      2015-03-04 @774  __V4L2_SUBDEV_MK_GET_TRY(v4l2_mbus_framefmt, v4l2_subdev_get_try_format, try_fmt)
f7234138 Hans Verkuil      2015-03-04  775  __V4L2_SUBDEV_MK_GET_TRY(v4l2_rect, v4l2_subdev_get_try_crop, try_crop)
f7234138 Hans Verkuil      2015-03-04  776  __V4L2_SUBDEV_MK_GET_TRY(v4l2_rect, v4l2_subdev_get_try_compose, try_compose)
7cd5a16b Stanimir Varbanov 2010-05-21  777  #endif

:::::: The code at line 770 was first introduced by commit
:::::: f7234138f14c2296c5eb6b8224abe00b507faf3f [media] v4l2-subdev: replace v4l2_subdev_fh by v4l2_subdev_pad_config

:::::: TO: Hans Verkuil <hans.verkuil@cisco.com>
:::::: CC: Mauro Carvalho Chehab <mchehab@osg.samsung.com>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 29755 bytes --]

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

* Re: [PATCH v4] [media] vimc: Virtual Media Controller core, capture and sensor
  2016-05-31 15:02 [PATCH v4] [media] vimc: Virtual Media Controller core, capture and sensor Helen Koike
  2016-06-12  1:59 ` kbuild test robot
@ 2016-06-12  2:11 ` kbuild test robot
  2016-07-01 12:39 ` Hans Verkuil
  2 siblings, 0 replies; 41+ messages in thread
From: kbuild test robot @ 2016-06-12  2:11 UTC (permalink / raw)
  To: Helen Koike
  Cc: kbuild-all, linux-media, laurent.pinchart, hverkuil, jgebben,
	mchehab, Helen Fornazier

[-- Attachment #1: Type: text/plain, Size: 25719 bytes --]

Hi,

[auto build test ERROR on linuxtv-media/master]
[also build test ERROR on v4.7-rc2 next-20160609]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Helen-Koike/vimc-Virtual-Media-Controller-core-capture-and-sensor/20160531-230840
base:   git://linuxtv.org/media_tree.git master
config: i386-randconfig-x010-06120748 (attached as .config)
compiler: gcc-6 (Debian 6.1.1-1) 6.1.1 20160430
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All error/warnings (new ones prefixed by >>):

   In file included from include/linux/init.h:4:0,
                    from drivers/media/platform/vimc/vimc-core.c:18:
   include/media/v4l2-subdev.h: In function 'v4l2_subdev_get_try_format':
   include/media/v4l2-subdev.h:770:19: error: 'struct v4l2_subdev' has no member named 'entity'
      BUG_ON(pad >= sd->entity.num_pads);   \
                      ^
   include/linux/compiler.h:170:42: note: in definition of macro 'unlikely'
    # define unlikely(x) __builtin_expect(!!(x), 0)
                                             ^
   include/media/v4l2-subdev.h:770:3: note: in expansion of macro 'BUG_ON'
      BUG_ON(pad >= sd->entity.num_pads);   \
      ^~~~~~
   include/media/v4l2-subdev.h:774:1: note: in expansion of macro '__V4L2_SUBDEV_MK_GET_TRY'
    __V4L2_SUBDEV_MK_GET_TRY(v4l2_mbus_framefmt, v4l2_subdev_get_try_format, try_fmt)
    ^~~~~~~~~~~~~~~~~~~~~~~~
   include/media/v4l2-subdev.h: In function 'v4l2_subdev_get_try_crop':
   include/media/v4l2-subdev.h:770:19: error: 'struct v4l2_subdev' has no member named 'entity'
      BUG_ON(pad >= sd->entity.num_pads);   \
                      ^
   include/linux/compiler.h:170:42: note: in definition of macro 'unlikely'
    # define unlikely(x) __builtin_expect(!!(x), 0)
                                             ^
   include/media/v4l2-subdev.h:770:3: note: in expansion of macro 'BUG_ON'
      BUG_ON(pad >= sd->entity.num_pads);   \
      ^~~~~~
   include/media/v4l2-subdev.h:775:1: note: in expansion of macro '__V4L2_SUBDEV_MK_GET_TRY'
    __V4L2_SUBDEV_MK_GET_TRY(v4l2_rect, v4l2_subdev_get_try_crop, try_crop)
    ^~~~~~~~~~~~~~~~~~~~~~~~
   include/media/v4l2-subdev.h: In function 'v4l2_subdev_get_try_compose':
   include/media/v4l2-subdev.h:770:19: error: 'struct v4l2_subdev' has no member named 'entity'
      BUG_ON(pad >= sd->entity.num_pads);   \
                      ^
   include/linux/compiler.h:170:42: note: in definition of macro 'unlikely'
    # define unlikely(x) __builtin_expect(!!(x), 0)
                                             ^
   include/media/v4l2-subdev.h:770:3: note: in expansion of macro 'BUG_ON'
      BUG_ON(pad >= sd->entity.num_pads);   \
      ^~~~~~
   include/media/v4l2-subdev.h:776:1: note: in expansion of macro '__V4L2_SUBDEV_MK_GET_TRY'
    __V4L2_SUBDEV_MK_GET_TRY(v4l2_rect, v4l2_subdev_get_try_compose, try_compose)
    ^~~~~~~~~~~~~~~~~~~~~~~~
   drivers/media/platform/vimc/vimc-core.c: In function 'vimc_device_unregister':
>> drivers/media/platform/vimc/vimc-core.c:308:2: error: implicit declaration of function 'media_device_cleanup' [-Werror=implicit-function-declaration]
     media_device_cleanup(&vimc->mdev);
     ^~~~~~~~~~~~~~~~~~~~
   drivers/media/platform/vimc/vimc-core.c: In function 'vimc_raw_create':
>> drivers/media/platform/vimc/vimc-core.c:383:45: error: 'struct v4l2_device' has no member named 'mdev'; did you mean 'dev'?
     ret = media_device_register_entity(v4l2_dev->mdev, ved->ent);
                                                ^~
   drivers/media/platform/vimc/vimc-core.c: In function 'vimc_device_register':
   drivers/media/platform/vimc/vimc-core.c:423:16: error: 'struct v4l2_device' has no member named 'mdev'; did you mean 'dev'?
     vimc->v4l2_dev.mdev = &vimc->mdev;
                   ^
   drivers/media/platform/vimc/vimc-core.c: In function 'vimc_probe':
>> drivers/media/platform/vimc/vimc-core.c:524:2: error: implicit declaration of function 'media_device_init' [-Werror=implicit-function-declaration]
     media_device_init(&vimc->mdev);
     ^~~~~~~~~~~~~~~~~
   cc1: some warnings being treated as errors
--
   In file included from include/uapi/linux/stddef.h:1:0,
                    from include/linux/stddef.h:4,
                    from include/uapi/linux/posix_types.h:4,
                    from include/uapi/linux/types.h:13,
                    from include/linux/types.h:5,
                    from include/linux/mm_types.h:5,
                    from include/media/videobuf2-core.h:15,
                    from drivers/media/platform/vimc/vimc-capture.c:18:
   include/media/v4l2-subdev.h: In function 'v4l2_subdev_get_try_format':
   include/media/v4l2-subdev.h:770:19: error: 'struct v4l2_subdev' has no member named 'entity'
      BUG_ON(pad >= sd->entity.num_pads);   \
                      ^
   include/linux/compiler.h:170:42: note: in definition of macro 'unlikely'
    # define unlikely(x) __builtin_expect(!!(x), 0)
                                             ^
   include/media/v4l2-subdev.h:770:3: note: in expansion of macro 'BUG_ON'
      BUG_ON(pad >= sd->entity.num_pads);   \
      ^~~~~~
   include/media/v4l2-subdev.h:774:1: note: in expansion of macro '__V4L2_SUBDEV_MK_GET_TRY'
    __V4L2_SUBDEV_MK_GET_TRY(v4l2_mbus_framefmt, v4l2_subdev_get_try_format, try_fmt)
    ^~~~~~~~~~~~~~~~~~~~~~~~
   include/media/v4l2-subdev.h: In function 'v4l2_subdev_get_try_crop':
   include/media/v4l2-subdev.h:770:19: error: 'struct v4l2_subdev' has no member named 'entity'
      BUG_ON(pad >= sd->entity.num_pads);   \
                      ^
   include/linux/compiler.h:170:42: note: in definition of macro 'unlikely'
    # define unlikely(x) __builtin_expect(!!(x), 0)
                                             ^
   include/media/v4l2-subdev.h:770:3: note: in expansion of macro 'BUG_ON'
      BUG_ON(pad >= sd->entity.num_pads);   \
      ^~~~~~
   include/media/v4l2-subdev.h:775:1: note: in expansion of macro '__V4L2_SUBDEV_MK_GET_TRY'
    __V4L2_SUBDEV_MK_GET_TRY(v4l2_rect, v4l2_subdev_get_try_crop, try_crop)
    ^~~~~~~~~~~~~~~~~~~~~~~~
   include/media/v4l2-subdev.h: In function 'v4l2_subdev_get_try_compose':
   include/media/v4l2-subdev.h:770:19: error: 'struct v4l2_subdev' has no member named 'entity'
      BUG_ON(pad >= sd->entity.num_pads);   \
                      ^
   include/linux/compiler.h:170:42: note: in definition of macro 'unlikely'
    # define unlikely(x) __builtin_expect(!!(x), 0)
                                             ^
   include/media/v4l2-subdev.h:770:3: note: in expansion of macro 'BUG_ON'
      BUG_ON(pad >= sd->entity.num_pads);   \
      ^~~~~~
   include/media/v4l2-subdev.h:776:1: note: in expansion of macro '__V4L2_SUBDEV_MK_GET_TRY'
    __V4L2_SUBDEV_MK_GET_TRY(v4l2_rect, v4l2_subdev_get_try_compose, try_compose)
    ^~~~~~~~~~~~~~~~~~~~~~~~
   drivers/media/platform/vimc/vimc-capture.c: In function 'vimc_cap_pipeline_s_stream':
>> drivers/media/platform/vimc/vimc-capture.c:175:22: error: 'struct video_device' has no member named 'entity'
     entity = &vcap->vdev.entity;
                         ^
   In file included from include/linux/list.h:8:0,
                    from include/linux/mm_types.h:7,
                    from include/media/videobuf2-core.h:15,
                    from drivers/media/platform/vimc/vimc-capture.c:18:
>> include/linux/kernel.h:831:27: error: 'struct v4l2_subdev' has no member named 'entity'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                              ^
>> include/media/v4l2-subdev.h:746:2: note: in expansion of macro 'container_of'
     container_of(ent, struct v4l2_subdev, entity)
     ^~~~~~~~~~~~
>> drivers/media/platform/vimc/vimc-capture.c:186:7: note: in expansion of macro 'media_entity_to_v4l2_subdev'
     sd = media_entity_to_v4l2_subdev(entity);
          ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/kernel.h:831:48: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
>> include/media/v4l2-subdev.h:746:2: note: in expansion of macro 'container_of'
     container_of(ent, struct v4l2_subdev, entity)
     ^~~~~~~~~~~~
>> drivers/media/platform/vimc/vimc-capture.c:186:7: note: in expansion of macro 'media_entity_to_v4l2_subdev'
     sd = media_entity_to_v4l2_subdev(entity);
          ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   In file included from include/linux/compiler.h:60:0,
                    from include/uapi/linux/stddef.h:1,
                    from include/linux/stddef.h:4,
                    from include/uapi/linux/posix_types.h:4,
                    from include/uapi/linux/types.h:13,
                    from include/linux/types.h:5,
                    from include/linux/mm_types.h:5,
                    from include/media/videobuf2-core.h:15,
                    from drivers/media/platform/vimc/vimc-capture.c:18:
>> include/linux/compiler-gcc.h:159:2: error: 'struct v4l2_subdev' has no member named 'entity'
     __builtin_offsetof(a, b)
     ^
   include/linux/stddef.h:16:32: note: in expansion of macro '__compiler_offsetof'
    #define offsetof(TYPE, MEMBER) __compiler_offsetof(TYPE, MEMBER)
                                   ^~~~~~~~~~~~~~~~~~~
   include/linux/kernel.h:832:29: note: in expansion of macro 'offsetof'
     (type *)( (char *)__mptr - offsetof(type,member) );})
                                ^~~~~~~~
>> include/media/v4l2-subdev.h:746:2: note: in expansion of macro 'container_of'
     container_of(ent, struct v4l2_subdev, entity)
     ^~~~~~~~~~~~
>> drivers/media/platform/vimc/vimc-capture.c:186:7: note: in expansion of macro 'media_entity_to_v4l2_subdev'
     sd = media_entity_to_v4l2_subdev(entity);
          ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/media/platform/vimc/vimc-capture.c: In function 'vimc_cap_start_streaming':
   drivers/media/platform/vimc/vimc-capture.c:204:22: error: 'struct video_device' has no member named 'entity'
     entity = &vcap->vdev.entity;
                         ^
   drivers/media/platform/vimc/vimc-capture.c: In function 'vimc_cap_stop_streaming':
   drivers/media/platform/vimc/vimc-capture.c:233:40: error: 'struct video_device' has no member named 'entity'
     media_entity_pipeline_stop(&vcap->vdev.entity);
                                           ^
   In file included from include/linux/list.h:8:0,
                    from include/linux/mm_types.h:7,
                    from include/media/videobuf2-core.h:15,
                    from drivers/media/platform/vimc/vimc-capture.c:18:
   drivers/media/platform/vimc/vimc-capture.c: In function 'vimc_cap_v4l2_subdev_link_validate_get_format':
>> include/linux/kernel.h:831:27: error: 'struct v4l2_subdev' has no member named 'entity'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                              ^
>> include/media/v4l2-subdev.h:746:2: note: in expansion of macro 'container_of'
     container_of(ent, struct v4l2_subdev, entity)
     ^~~~~~~~~~~~
   drivers/media/platform/vimc/vimc-capture.c:291:27: note: in expansion of macro 'media_entity_to_v4l2_subdev'
     struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity);
                              ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/kernel.h:831:48: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
>> include/media/v4l2-subdev.h:746:2: note: in expansion of macro 'container_of'
     container_of(ent, struct v4l2_subdev, entity)
     ^~~~~~~~~~~~
   drivers/media/platform/vimc/vimc-capture.c:291:27: note: in expansion of macro 'media_entity_to_v4l2_subdev'
     struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity);
                              ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/kernel.h:831:48: note: (near initialization for 'sd')
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
>> include/media/v4l2-subdev.h:746:2: note: in expansion of macro 'container_of'
     container_of(ent, struct v4l2_subdev, entity)
     ^~~~~~~~~~~~
   drivers/media/platform/vimc/vimc-capture.c:291:27: note: in expansion of macro 'media_entity_to_v4l2_subdev'
     struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity);
                              ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   In file included from include/linux/compiler.h:60:0,
                    from include/uapi/linux/stddef.h:1,
                    from include/linux/stddef.h:4,
                    from include/uapi/linux/posix_types.h:4,
                    from include/uapi/linux/types.h:13,
                    from include/linux/types.h:5,
                    from include/linux/mm_types.h:5,
                    from include/media/videobuf2-core.h:15,
                    from drivers/media/platform/vimc/vimc-capture.c:18:
>> include/linux/compiler-gcc.h:159:2: error: 'struct v4l2_subdev' has no member named 'entity'
     __builtin_offsetof(a, b)
     ^
   include/linux/stddef.h:16:32: note: in expansion of macro '__compiler_offsetof'
    #define offsetof(TYPE, MEMBER) __compiler_offsetof(TYPE, MEMBER)
                                   ^~~~~~~~~~~~~~~~~~~
   include/linux/kernel.h:832:29: note: in expansion of macro 'offsetof'
     (type *)( (char *)__mptr - offsetof(type,member) );})
                                ^~~~~~~~
>> include/media/v4l2-subdev.h:746:2: note: in expansion of macro 'container_of'
     container_of(ent, struct v4l2_subdev, entity)
     ^~~~~~~~~~~~
   drivers/media/platform/vimc/vimc-capture.c:291:27: note: in expansion of macro 'media_entity_to_v4l2_subdev'
     struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity);
                              ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   In file included from include/linux/list.h:8:0,
                    from include/linux/mm_types.h:7,
                    from include/media/videobuf2-core.h:15,
                    from drivers/media/platform/vimc/vimc-capture.c:18:
   drivers/media/platform/vimc/vimc-capture.c: In function 'vimc_cap_link_validate':
   drivers/media/platform/vimc/vimc-capture.c:308:36: error: 'struct video_device' has no member named 'entity'
           struct vimc_cap_device, vdev.entity);
                                       ^
   include/linux/kernel.h:831:29: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                ^~~~~~
   include/linux/kernel.h:831:48: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
>> drivers/media/platform/vimc/vimc-capture.c:307:9: note: in expansion of macro 'container_of'
     vcap = container_of(link->sink->entity,
            ^~~~~~~~~~~~
   In file included from include/linux/compiler.h:60:0,
                    from include/uapi/linux/stddef.h:1,
                    from include/linux/stddef.h:4,
                    from include/uapi/linux/posix_types.h:4,
                    from include/uapi/linux/types.h:13,
                    from include/linux/types.h:5,
                    from include/linux/mm_types.h:5,
                    from include/media/videobuf2-core.h:15,
                    from drivers/media/platform/vimc/vimc-capture.c:18:
>> include/linux/compiler-gcc.h:159:2: error: 'struct video_device' has no member named 'entity'
     __builtin_offsetof(a, b)
     ^
   include/linux/stddef.h:16:32: note: in expansion of macro '__compiler_offsetof'
    #define offsetof(TYPE, MEMBER) __compiler_offsetof(TYPE, MEMBER)
                                   ^~~~~~~~~~~~~~~~~~~
   include/linux/kernel.h:832:29: note: in expansion of macro 'offsetof'
     (type *)( (char *)__mptr - offsetof(type,member) );})
                                ^~~~~~~~
>> drivers/media/platform/vimc/vimc-capture.c:307:9: note: in expansion of macro 'container_of'
     vcap = container_of(link->sink->entity,
            ^~~~~~~~~~~~
   drivers/media/platform/vimc/vimc-capture.c: In function 'vimc_cap_create':
   drivers/media/platform/vimc/vimc-capture.c:450:12: error: 'struct video_device' has no member named 'entity'
     vcap->vdev.entity.name = name;
               ^
   drivers/media/platform/vimc/vimc-capture.c:451:12: error: 'struct video_device' has no member named 'entity'
     vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L;
               ^
   drivers/media/platform/vimc/vimc-capture.c:452:42: error: 'struct video_device' has no member named 'entity'
     ret = media_entity_pads_init(&vcap->vdev.entity,
                                             ^
   drivers/media/platform/vimc/vimc-capture.c:500:29: error: 'struct video_device' has no member named 'entity'
     vcap->ved.ent = &vcap->vdev.entity;
                                ^
   drivers/media/platform/vimc/vimc-capture.c:505:6: error: 'struct video_device' has no member named 'entity'
     vdev->entity.ops = &vimc_cap_mops;
         ^~
   drivers/media/platform/vimc/vimc-capture.c:529:34: error: 'struct video_device' has no member named 'entity'
     media_entity_cleanup(&vcap->vdev.entity);
                                     ^
   cc1: some warnings being treated as errors
--
   In file included from include/linux/linkage.h:4:0,
                    from include/linux/kernel.h:6,
                    from include/linux/debug_locks.h:4,
                    from include/linux/freezer.h:6,
                    from drivers/media/platform/vimc/vimc-sensor.c:18:
   include/media/v4l2-subdev.h: In function 'v4l2_subdev_get_try_format':
   include/media/v4l2-subdev.h:770:19: error: 'struct v4l2_subdev' has no member named 'entity'
      BUG_ON(pad >= sd->entity.num_pads);   \
                      ^
   include/linux/compiler.h:170:42: note: in definition of macro 'unlikely'
    # define unlikely(x) __builtin_expect(!!(x), 0)
                                             ^
   include/media/v4l2-subdev.h:770:3: note: in expansion of macro 'BUG_ON'
      BUG_ON(pad >= sd->entity.num_pads);   \
      ^~~~~~
   include/media/v4l2-subdev.h:774:1: note: in expansion of macro '__V4L2_SUBDEV_MK_GET_TRY'
    __V4L2_SUBDEV_MK_GET_TRY(v4l2_mbus_framefmt, v4l2_subdev_get_try_format, try_fmt)
    ^~~~~~~~~~~~~~~~~~~~~~~~
   include/media/v4l2-subdev.h: In function 'v4l2_subdev_get_try_crop':
   include/media/v4l2-subdev.h:770:19: error: 'struct v4l2_subdev' has no member named 'entity'
      BUG_ON(pad >= sd->entity.num_pads);   \
                      ^
   include/linux/compiler.h:170:42: note: in definition of macro 'unlikely'
    # define unlikely(x) __builtin_expect(!!(x), 0)
                                             ^
   include/media/v4l2-subdev.h:770:3: note: in expansion of macro 'BUG_ON'
      BUG_ON(pad >= sd->entity.num_pads);   \
      ^~~~~~
   include/media/v4l2-subdev.h:775:1: note: in expansion of macro '__V4L2_SUBDEV_MK_GET_TRY'
    __V4L2_SUBDEV_MK_GET_TRY(v4l2_rect, v4l2_subdev_get_try_crop, try_crop)
    ^~~~~~~~~~~~~~~~~~~~~~~~
   include/media/v4l2-subdev.h: In function 'v4l2_subdev_get_try_compose':
   include/media/v4l2-subdev.h:770:19: error: 'struct v4l2_subdev' has no member named 'entity'
      BUG_ON(pad >= sd->entity.num_pads);   \
                      ^
   include/linux/compiler.h:170:42: note: in definition of macro 'unlikely'
    # define unlikely(x) __builtin_expect(!!(x), 0)
                                             ^
   include/media/v4l2-subdev.h:770:3: note: in expansion of macro 'BUG_ON'
      BUG_ON(pad >= sd->entity.num_pads);   \
      ^~~~~~
   include/media/v4l2-subdev.h:776:1: note: in expansion of macro '__V4L2_SUBDEV_MK_GET_TRY'
    __V4L2_SUBDEV_MK_GET_TRY(v4l2_rect, v4l2_subdev_get_try_compose, try_compose)
    ^~~~~~~~~~~~~~~~~~~~~~~~
   drivers/media/platform/vimc/vimc-sensor.c: In function 'vimc_sen_enum_mbus_code':
>> drivers/media/platform/vimc/vimc-sensor.c:45:27: error: 'struct v4l2_subdev' has no member named 'entity'
     if (code->pad >= vsen->sd.entity.num_pads)
                              ^
   drivers/media/platform/vimc/vimc-sensor.c: In function 'vimc_sen_enum_frame_size':
   drivers/media/platform/vimc/vimc-sensor.c:60:26: error: 'struct v4l2_subdev' has no member named 'entity'
     if (fse->pad >= vsen->sd.entity.num_pads ||
                             ^
   drivers/media/platform/vimc/vimc-sensor.c:61:16: error: 'struct v4l2_subdev' has no member named 'entity'
         !(vsen->sd.entity.pads[fse->pad].flags & MEDIA_PAD_FL_SOURCE))
                   ^
   drivers/media/platform/vimc/vimc-sensor.c: At top level:
>> drivers/media/platform/vimc/vimc-sensor.c:101:19: error: 'v4l2_subdev_link_validate' undeclared here (not in a function)
     .link_validate = v4l2_subdev_link_validate,
                      ^~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/media/platform/vimc/vimc-sensor.c: In function 'vimc_thread_sen':
   drivers/media/platform/vimc/vimc-sensor.c:119:27: error: 'struct v4l2_subdev' has no member named 'entity'
      for (i = 0; i < vsen->sd.entity.num_pads; i++)
                              ^
   drivers/media/platform/vimc/vimc-sensor.c:120:16: error: 'struct v4l2_subdev' has no member named 'entity'
       if (vsen->sd.entity.pads[i].flags & MEDIA_PAD_FL_SOURCE)
                   ^
   drivers/media/platform/vimc/vimc-sensor.c:122:21: error: 'struct v4l2_subdev' has no member named 'entity'
               &vsen->sd.entity.pads[i],
                        ^
   drivers/media/platform/vimc/vimc-sensor.c: In function 'vimc_sen_create':
   drivers/media/platform/vimc/vimc-sensor.c:230:10: error: 'struct v4l2_subdev' has no member named 'entity'
     vsen->sd.entity.name = name;
             ^
   drivers/media/platform/vimc/vimc-sensor.c:231:10: error: 'struct v4l2_subdev' has no member named 'entity'
     vsen->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
             ^
   drivers/media/platform/vimc/vimc-sensor.c:232:40: error: 'struct v4l2_subdev' has no member named 'entity'
     ret = media_entity_pads_init(&vsen->sd.entity,
                                           ^
   drivers/media/platform/vimc/vimc-sensor.c:248:27: error: 'struct v4l2_subdev' has no member named 'entity'
     vsen->ved.ent = &vsen->sd.entity;
                              ^
   drivers/media/platform/vimc/vimc-sensor.c:252:10: error: 'struct v4l2_subdev' has no member named 'entity'
     vsen->sd.entity.ops = &vimc_sen_mops;
             ^
   drivers/media/platform/vimc/vimc-sensor.c:271:32: error: 'struct v4l2_subdev' has no member named 'entity'
     media_entity_cleanup(&vsen->sd.entity);
                                   ^
..

vim +/media_device_cleanup +308 drivers/media/platform/vimc/vimc-core.c

   302	
   303			vimc->ved[i] = NULL;
   304		}
   305		v4l2_device_unregister(&vimc->v4l2_dev);
   306	
   307		media_device_unregister(&vimc->mdev);
 > 308		media_device_cleanup(&vimc->mdev);
   309	}
   310	
   311	/* Helper function to allocate and initialize pads */
   312	struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag)
   313	{
   314		unsigned int i;
   315		struct media_pad *pads;
   316	
   317		/* Allocate memory for the pads */
   318		pads = kcalloc(num_pads, sizeof(*pads), GFP_KERNEL);
   319		if (!pads)
   320			return ERR_PTR(-ENOMEM);
   321	
   322		/* Initialize the pads */
   323		for (i = 0; i < num_pads; i++) {
   324			pads[i].index = i;
   325			pads[i].flags = pads_flag[i];
   326		}
   327	
   328		return pads;
   329	}
   330	
   331	/* TODO: remove this function when all the
   332	 * entities specific code are implemented */
   333	static void vimc_raw_destroy(struct vimc_ent_device *ved)
   334	{
   335		media_device_unregister_entity(ved->ent);
   336	
   337		media_entity_cleanup(ved->ent);
   338	
   339		vimc_pads_cleanup(ved->pads);
   340	
   341		kfree(ved->ent);
   342	
   343		kfree(ved);
   344	}
   345	
   346	/* TODO: remove this function when all the
   347	 * entities specific code are implemented */
   348	static struct vimc_ent_device *vimc_raw_create(struct v4l2_device *v4l2_dev,
   349						       const char *const name,
   350						       u16 num_pads,
   351						       const unsigned long *pads_flag)
   352	{
   353		int ret;
   354		struct vimc_ent_device *ved;
   355	
   356		/* Allocate the main ved struct */
   357		ved = kzalloc(sizeof(*ved), GFP_KERNEL);
   358		if (!ved)
   359			return ERR_PTR(-ENOMEM);
   360	
   361		/* Allocate the media entity */
   362		ved->ent = kzalloc(sizeof(*ved->ent), GFP_KERNEL);
   363		if (!ved->ent) {
   364			ret = -ENOMEM;
   365			goto err_free_ved;
   366		}
   367	
   368		/* Allocate the pads */
   369		ved->pads = vimc_pads_init(num_pads, pads_flag);
   370		if (IS_ERR(ved->pads)) {
   371			ret = PTR_ERR(ved->pads);
   372			goto err_free_ent;
   373		}
   374	
   375		/* Initialize the media entity */
   376		ved->ent->name = name;
   377		ved->ent->function = MEDIA_ENT_F_IO_V4L;
   378		ret = media_entity_pads_init(ved->ent, num_pads, ved->pads);
   379		if (ret)
   380			goto err_cleanup_pads;
   381	
   382		/* Register the media entity */
 > 383		ret = media_device_register_entity(v4l2_dev->mdev, ved->ent);
   384		if (ret)
   385			goto err_cleanup_entity;
   386	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 29755 bytes --]

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

* Re: [PATCH v4] [media] vimc: Virtual Media Controller core, capture and sensor
  2016-05-31 15:02 [PATCH v4] [media] vimc: Virtual Media Controller core, capture and sensor Helen Koike
  2016-06-12  1:59 ` kbuild test robot
  2016-06-12  2:11 ` kbuild test robot
@ 2016-07-01 12:39 ` Hans Verkuil
  2016-08-17 22:08   ` Helen Koike
  2016-08-17 22:09   ` [PATCH v5] " Helen Koike
  2 siblings, 2 replies; 41+ messages in thread
From: Hans Verkuil @ 2016-07-01 12:39 UTC (permalink / raw)
  To: Helen Koike, linux-media, laurent.pinchart, jgebben, mchehab
  Cc: Helen Fornazier

Hi Helen,

Better late than never, but I finally have time for a review, mostly with a eye for V4L2 issues.

Please note the 'kbuild test robot' mails you received: those issues should be resolved
in v5. It looks like it misses the media controller dependency in the Kconfig.

On 05/31/2016 05:02 PM, Helen Koike wrote:
> From: Helen Fornazier <helen.fornazier@gmail.com>
> 
> First version of the Virtual Media Controller.
> Add a simple version of the core of the driver, the capture and
> sensor nodes in the topology, generating a grey image in a hardcoded
> format.
> 
> Signed-off-by: Helen Fornazier <helen.fornazier@gmail.com>
> 
> ---
> Changes since v3: fix rmmod crash and built-in compile
> - Re-order unregister calls in vimc_device_unregister function (remove
> rmmod issue)
> - Call media_device_unregister_entity in vimc_raw_destroy
> - Add depends on VIDEO_DEV && VIDEO_V4L2 and select VIDEOBUF2_VMALLOC
> - Check *nplanes in queue_setup (this remove v4l2-compliance fail)
> - Include <linux/kthread.h> in vimc-sensor.c
> - Move include of <linux/slab.h> from vimc-core.c to vimc-core.h
> - Generate 60 frames per sec instead of 1 in the sensor
> 
> Changes since v2: update with current media master tree
> - Add struct media_pipeline in vimc_cap_device
> - Use vb2_v4l2_buffer instead of vb2_buffer
> - Typos
> - Remove usage of entity->type and use entity->function instead
> - Remove fmt argument from queue setup
> - Use ktime_get_ns instead of v4l2_get_timestamp
> - Iterate over link's list using list_for_each_entry
> - Use media_device_{init, cleanup}
> - Use entity->use_count to keep track of entities instead of the old
> entity->id
> - Replace media_entity_init by media_entity_pads_init
> 
>  drivers/media/platform/Kconfig             |   2 +
>  drivers/media/platform/Makefile            |   1 +
>  drivers/media/platform/vimc/Kconfig        |   8 +
>  drivers/media/platform/vimc/Makefile       |   3 +
>  drivers/media/platform/vimc/vimc-capture.c | 536 ++++++++++++++++++++++++++
>  drivers/media/platform/vimc/vimc-capture.h |  28 ++
>  drivers/media/platform/vimc/vimc-core.c    | 595 +++++++++++++++++++++++++++++
>  drivers/media/platform/vimc/vimc-core.h    |  56 +++
>  drivers/media/platform/vimc/vimc-sensor.c  | 278 ++++++++++++++
>  drivers/media/platform/vimc/vimc-sensor.h  |  28 ++
>  10 files changed, 1535 insertions(+)
>  create mode 100644 drivers/media/platform/vimc/Kconfig
>  create mode 100644 drivers/media/platform/vimc/Makefile
>  create mode 100644 drivers/media/platform/vimc/vimc-capture.c
>  create mode 100644 drivers/media/platform/vimc/vimc-capture.h
>  create mode 100644 drivers/media/platform/vimc/vimc-core.c
>  create mode 100644 drivers/media/platform/vimc/vimc-core.h
>  create mode 100644 drivers/media/platform/vimc/vimc-sensor.c
>  create mode 100644 drivers/media/platform/vimc/vimc-sensor.h
> 
> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> index 84e041c..9e10d49 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -284,6 +284,8 @@ menuconfig V4L_TEST_DRIVERS
>  
>  if V4L_TEST_DRIVERS
>  
> +source "drivers/media/platform/vimc/Kconfig"
> +
>  source "drivers/media/platform/vivid/Kconfig"
>  
>  config VIDEO_VIM2M
> diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> index bbb7bd1..e4508fe 100644
> --- a/drivers/media/platform/Makefile
> +++ b/drivers/media/platform/Makefile
> @@ -12,6 +12,7 @@ obj-$(CONFIG_VIDEO_OMAP3)	+= omap3isp/
>  
>  obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o
>  
> +obj-$(CONFIG_VIDEO_VIMC)		+= vimc/
>  obj-$(CONFIG_VIDEO_VIVID)		+= vivid/
>  obj-$(CONFIG_VIDEO_VIM2M)		+= vim2m.o
>  
> diff --git a/drivers/media/platform/vimc/Kconfig b/drivers/media/platform/vimc/Kconfig
> new file mode 100644
> index 0000000..c99666a
> --- /dev/null
> +++ b/drivers/media/platform/vimc/Kconfig
> @@ -0,0 +1,8 @@
> +config VIDEO_VIMC
> +	tristate "Virtual Media Controller Driver (VIMC)"
> +	depends on VIDEO_DEV && VIDEO_V4L2
> +	select VIDEO_V4L2_SUBDEV_API

This should be depends on.

> +	select VIDEOBUF2_VMALLOC
> +	default n
> +	---help---
> +	  Skeleton driver for Virtual Media Controller
> diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile
> new file mode 100644
> index 0000000..c45195e
> --- /dev/null
> +++ b/drivers/media/platform/vimc/Makefile
> @@ -0,0 +1,3 @@
> +vimc-objs := vimc-core.o vimc-capture.o vimc-sensor.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
> new file mode 100644
> index 0000000..82de8b89
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-capture.c
> @@ -0,0 +1,536 @@
> +/*
> + * vimc-capture.c Virtual Media Controller Driver
> + *
> + * Copyright (C) 2015 Helen Fornazier <helen.fornazier@gmail.com>

Copyright year can be updated to 2016.

> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <media/videobuf2-core.h>
> +#include <media/videobuf2-vmalloc.h>
> +#include <media/v4l2-ioctl.h>
> +
> +#include "vimc-capture.h"
> +
> +struct vimc_cap_device {
> +	struct vimc_ent_device ved;
> +	struct video_device vdev;
> +	struct v4l2_device *v4l2_dev;
> +	struct device *dev;
> +	struct v4l2_pix_format format;
> +	struct vb2_queue queue;
> +	struct list_head buf_list;
> +	/* NOTE: in a real driver, a spin lock must be used to access the
> +	 * queue because the frames are generated from a hardware interruption
> +	 * and the isr is not allowed to sleep.
> +	 * Even if it is not necessary a spinlock in the vimc driver, we
> +	 * use it here as a code reference */

I suspect checkpatch will warn about the comment format. Did you run checkpatch?

> +	spinlock_t qlock;
> +	struct mutex lock;
> +	u32 sequence;
> +	struct media_pipeline pipe;
> +};
> +
> +struct vimc_cap_buffer {
> +	/*
> +	 * vb2_buffer must be the first element
> +	 * the videobf2 framework will allocate this struct based on

s/videobf2/videobuf2/

> +	 * buf_struct_size and use the first sizeof(struct vb2_buffer) bytes of
> +	 * memory as a vb2_buffer
> +	 */
> +	struct vb2_v4l2_buffer vb2;
> +	struct list_head list;
> +};
> +
> +static int vimc_cap_querycap(struct file *file, void *priv,
> +			     struct v4l2_capability *cap)
> +{
> +	struct vimc_cap_device *vcap = video_drvdata(file);
> +
> +	strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
> +	strlcpy(cap->card, KBUILD_MODNAME, sizeof(cap->card));
> +	snprintf(cap->bus_info, sizeof(cap->bus_info),
> +		 "platform:%s", vcap->v4l2_dev->name);
> +
> +	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
> +	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;

This line can be dropped, the v4l2 core will fill this in (a recent change).

> +
> +	return 0;
> +}
> +
> +static int vimc_cap_enum_input(struct file *file, void *priv,
> +			       struct v4l2_input *i)
> +{
> +	/* We only have one input */
> +	if (i->index > 0)
> +		return -EINVAL;
> +
> +	i->type = V4L2_INPUT_TYPE_CAMERA;
> +	strlcpy(i->name, "VIMC capture", sizeof(i->name));
> +
> +	return 0;
> +}
> +
> +static int vimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
> +{
> +	/* We only have one input */
> +	*i = 0;
> +	return 0;
> +}
> +
> +static int vimc_cap_s_input(struct file *file, void *priv, unsigned int i)
> +{
> +	/* We only have one input */
> +	return i ? -EINVAL : 0;
> +}
> +
> +static int vimc_cap_g_fmt_vid_cap(struct file *file, void *priv,
> +				  struct v4l2_format *f)
> +{
> +	struct vimc_cap_device *vcap = video_drvdata(file);
> +
> +	f->fmt.pix = vcap->format;
> +
> +	return 0;
> +}
> +
> +static int vimc_cap_enum_fmt_vid_cap(struct file *file, void *priv,
> +				     struct v4l2_fmtdesc *f)
> +{
> +	struct vimc_cap_device *vcap = video_drvdata(file);
> +
> +	if (f->index > 0)
> +		return -EINVAL;
> +
> +	/* We only support one format for now */
> +	f->pixelformat = vcap->format.pixelformat;
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_file_operations vimc_cap_fops = {
> +	.owner		= THIS_MODULE,
> +	.open		= v4l2_fh_open,
> +	.release	= vb2_fop_release,
> +	.read           = vb2_fop_read,
> +	.poll		= vb2_fop_poll,
> +	.unlocked_ioctl = video_ioctl2,
> +	.mmap           = vb2_fop_mmap,
> +};
> +
> +static const struct v4l2_ioctl_ops vimc_cap_ioctl_ops = {
> +	.vidioc_querycap = vimc_cap_querycap,
> +
> +	.vidioc_enum_input = vimc_cap_enum_input,
> +	.vidioc_g_input = vimc_cap_g_input,
> +	.vidioc_s_input = vimc_cap_s_input,
> +
> +	.vidioc_g_fmt_vid_cap = vimc_cap_g_fmt_vid_cap,
> +	.vidioc_s_fmt_vid_cap = vimc_cap_g_fmt_vid_cap,
> +	.vidioc_try_fmt_vid_cap = vimc_cap_g_fmt_vid_cap,

Rename vimc_cap_g_fmt_vid_cap to vimc_cap_fmt_vid_cap to make it clear it is used by all three ioctls.

> +	.vidioc_enum_fmt_vid_cap = vimc_cap_enum_fmt_vid_cap,
> +
> +	.vidioc_reqbufs = vb2_ioctl_reqbufs,
> +	.vidioc_create_bufs = vb2_ioctl_create_bufs,
> +	.vidioc_querybuf = vb2_ioctl_querybuf,
> +	.vidioc_qbuf = vb2_ioctl_qbuf,
> +	.vidioc_dqbuf = vb2_ioctl_dqbuf,
> +	.vidioc_expbuf = vb2_ioctl_expbuf,
> +	.vidioc_streamon = vb2_ioctl_streamon,
> +	.vidioc_streamoff = vb2_ioctl_streamoff,
> +};
> +
> +static void vimc_cap_return_all_buffers(struct vimc_cap_device *vcap,
> +					enum vb2_buffer_state state)
> +{
> +	struct vimc_cap_buffer *vbuf, *node;
> +
> +	spin_lock(&vcap->qlock);
> +
> +	list_for_each_entry_safe(vbuf, node, &vcap->buf_list, list) {
> +		vb2_buffer_done(&vbuf->vb2.vb2_buf, state);
> +		list_del(&vbuf->list);
> +	}
> +
> +	spin_unlock(&vcap->qlock);
> +}
> +
> +static int vimc_cap_pipeline_s_stream(struct vimc_cap_device *vcap, int enable)
> +{
> +	int ret;
> +	struct media_pad *pad;
> +	struct media_entity *entity;
> +	struct v4l2_subdev *sd;
> +
> +	/* Start the stream in the subdevice direct connected */
> +	entity = &vcap->vdev.entity;
> +	pad = media_entity_remote_pad(&entity->pads[0]);
> +
> +	/* If we are not connected to any subdev node, it means there is nothing
> +	 * to activate on the pipe (e.g. we can be connected with an input
> +	 * device or we are not connected at all)
> +	 */
> +	if (pad == NULL || !is_media_entity_v4l2_subdev(pad->entity))
> +		return 0;
> +
> +	entity = pad->entity;
> +	sd = media_entity_to_v4l2_subdev(entity);
> +
> +	ret = v4l2_subdev_call(sd, video, s_stream, enable);
> +	if (ret && ret != -ENOIOCTLCMD)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count)
> +{
> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
> +	struct media_entity *entity;
> +	int ret;
> +
> +	vcap->sequence = 0;
> +
> +	/* Start the media pipeline */
> +	entity = &vcap->vdev.entity;
> +	ret = media_entity_pipeline_start(entity, &vcap->pipe);
> +	if (ret) {
> +		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
> +		return ret;
> +	}
> +
> +	/* Enable streaming from the pipe */
> +	ret = vimc_cap_pipeline_s_stream(vcap, 1);
> +	if (ret) {
> +		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * Stop the stream engine. Any remaining buffers in the stream queue are
> + * dequeued and passed on to the vb2 framework marked as STATE_ERROR.
> + */
> +static void vimc_cap_stop_streaming(struct vb2_queue *vq)
> +{
> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
> +
> +	/* Disable streaming from the pipe */
> +	vimc_cap_pipeline_s_stream(vcap, 0);
> +
> +	/* Stop the media pipeline */
> +	media_entity_pipeline_stop(&vcap->vdev.entity);
> +
> +	/* Release all active buffers */
> +	vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_ERROR);
> +}
> +
> +static void vimc_cap_buf_queue(struct vb2_buffer *vb2_buf)
> +{
> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb2_buf->vb2_queue);
> +	struct vimc_cap_buffer *buf = container_of(vb2_buf,
> +					struct vimc_cap_buffer, vb2.vb2_buf);
> +
> +	/* If the buffer doesn't have enough size, mark it as error */
> +	if (vb2_buf->planes[0].length < vcap->format.sizeimage) {
> +		vb2_buffer_done(vb2_buf, VB2_BUF_STATE_ERROR);
> +		return;
> +	}

That's wrong. This check should happen in the vb2_ops buf_prepare callback.
See how other drivers do that.

> +
> +	spin_lock(&vcap->qlock);
> +
> +	list_add_tail(&buf->list, &vcap->buf_list);
> +
> +	spin_unlock(&vcap->qlock);
> +}
> +
> +static int vimc_cap_queue_setup(struct vb2_queue *vq,
> +				unsigned int *nbuffers, unsigned int *nplanes,
> +				unsigned int sizes[], void *alloc_ctxs[])
> +{
> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
> +
> +	if (*nplanes)
> +		return sizes[0] < vcap->format.sizeimage ? -EINVAL : 0;
> +	/* We don't support multiplanes for now */
> +	*nplanes = 1;
> +	sizes[0] = vcap->format.sizeimage;
> +
> +	return 0;
> +}
> +
> +static const struct vb2_ops vimc_cap_qops = {
> +	.start_streaming	= vimc_cap_start_streaming,
> +	.stop_streaming		= vimc_cap_stop_streaming,
> +	.buf_queue		= vimc_cap_buf_queue,
> +	.queue_setup		= vimc_cap_queue_setup,
> +	/*
> +	 * Since q->lock is set we can use the standard
> +	 * vb2_ops_wait_prepare/finish helper functions.
> +	 */
> +	.wait_prepare		= vb2_ops_wait_prepare,
> +	.wait_finish		= vb2_ops_wait_finish,
> +};
> +
> +/* NOTE: this function is a copy of v4l2_subdev_link_validate_get_format
> + * maybe the v4l2 function should be public */
> +static int vimc_cap_v4l2_subdev_link_validate_get_format(struct media_pad *pad,
> +						struct v4l2_subdev_format *fmt)
> +{
> +	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity);
> +
> +	fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +	fmt->pad = pad->index;
> +	return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt);
> +}
> +
> +static int vimc_cap_link_validate(struct media_link *link)
> +{
> +	struct v4l2_subdev_format source_fmt;
> +	struct v4l2_pix_format *sink_fmt;
> +	struct vimc_cap_device *vcap;
> +	const struct vimc_pix_map *vpix;
> +	int ret;
> +
> +	/* Retrieve the video capture device */
> +	vcap = container_of(link->sink->entity,
> +			    struct vimc_cap_device, vdev.entity);
> +
> +	/* If the connected node is not a subdevice type
> +	 * then it's a raw node from vimc-core, ignore the link for now
> +	 * TODO: remove this when there are no more raw nodes in the
> +	 * core and return error instead */
> +	if (!is_media_entity_v4l2_subdev(link->source->entity))
> +		return 0;
> +
> +	/* Get the the format of the video device */
> +	sink_fmt = &vcap->format;
> +
> +	/* Get the the format of the subdev */
> +	ret = vimc_cap_v4l2_subdev_link_validate_get_format(link->source,
> +							    &source_fmt);
> +	if (ret)
> +		return ret;
> +
> +	dev_dbg(vcap->dev,
> +		"cap: %s: link validate formats src:%dx%d %d sink:%dx%d %d\n",
> +		vcap->vdev.name,
> +		source_fmt.format.width, source_fmt.format.height,
> +		source_fmt.format.code,
> +		sink_fmt->width, sink_fmt->height,
> +		sink_fmt->pixelformat);
> +
> +	/* Validate the format */
> +
> +	vpix = vimc_pix_map_by_pixelformat(sink_fmt->pixelformat);
> +	if (!vpix)
> +		return -EINVAL;
> +
> +	/* The width, height and code must match. */
> +	if (source_fmt.format.width != sink_fmt->width
> +	    || source_fmt.format.height != sink_fmt->height
> +	    || vpix->code != source_fmt.format.code)
> +		return -EINVAL;
> +
> +	/* The field order must match, or the sink field order must be NONE
> +	 * to support interlaced hardware connected to bridges that support
> +	 * progressive formats only.
> +	 */
> +	if (source_fmt.format.field != sink_fmt->field &&
> +	    sink_fmt->field != V4L2_FIELD_NONE)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static const struct media_entity_operations vimc_cap_mops = {
> +	.link_validate		= vimc_cap_link_validate,
> +};
> +
> +static void vimc_cap_destroy(struct vimc_ent_device *ved)
> +{
> +	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
> +						    ved);
> +
> +	vb2_queue_release(&vcap->queue);
> +	media_entity_cleanup(ved->ent);
> +	video_unregister_device(&vcap->vdev);
> +	vimc_pads_cleanup(vcap->ved.pads);
> +	kfree(vcap);
> +}
> +
> +static void vimc_cap_process_frame(struct vimc_ent_device *ved,
> +				   struct media_pad *sink, const void *frame)
> +{
> +	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
> +						    ved);
> +	struct vimc_cap_buffer *vimc_buf;
> +	void *vbuf;
> +
> +	/* If the stream in this node is not active, just return */
> +	mutex_lock(&vcap->lock);
> +	if (!vb2_is_busy(&vcap->queue)) {
> +		mutex_unlock(&vcap->lock);
> +		return;
> +	}
> +	mutex_unlock(&vcap->lock);
> +
> +	spin_lock(&vcap->qlock);
> +
> +	/* Get the first entry of the list */
> +	vimc_buf = list_first_entry_or_null(&vcap->buf_list,
> +					    typeof(*vimc_buf), list);
> +	if (!vimc_buf) {
> +		spin_unlock(&vcap->qlock);
> +		return;
> +	}
> +
> +	/* Remove this entry from the list */
> +	list_del(&vimc_buf->list);
> +
> +	spin_unlock(&vcap->qlock);
> +
> +	/* Fill the buffer */
> +	vimc_buf->vb2.vb2_buf.timestamp = ktime_get_ns();
> +	vimc_buf->vb2.sequence = vcap->sequence++;
> +	vimc_buf->vb2.field = vcap->format.field;
> +
> +	vbuf = vb2_plane_vaddr(&vimc_buf->vb2.vb2_buf, 0);
> +
> +	memcpy(vbuf, frame, vcap->format.sizeimage);
> +
> +	/* Set it as ready */
> +	vb2_set_plane_payload(&vimc_buf->vb2.vb2_buf, 0,
> +			      vcap->format.sizeimage);
> +	vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE);
> +}
> +
> +struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
> +					const char *const name,
> +					u16 num_pads,
> +					const unsigned long *pads_flag)
> +{
> +	int ret;
> +	struct vb2_queue *q;
> +	struct video_device *vdev;
> +	struct vimc_cap_device *vcap;
> +	const struct vimc_pix_map *vpix;
> +
> +	if (!v4l2_dev || !v4l2_dev->dev || !name || (num_pads && !pads_flag))
> +		return ERR_PTR(-EINVAL);
> +
> +	/* Allocate the vimc_cap_device struct */
> +	vcap = kzalloc(sizeof(*vcap), GFP_KERNEL);
> +	if (!vcap)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/* Link the vimc_cap_device struct with v4l2 and dev parent */
> +	vcap->v4l2_dev = v4l2_dev;
> +	vcap->dev = v4l2_dev->dev;
> +
> +	/* Allocate the pads */
> +	vcap->ved.pads = vimc_pads_init(num_pads, pads_flag);
> +	if (IS_ERR(vcap->ved.pads)) {
> +		ret = PTR_ERR(vcap->ved.pads);
> +		goto err_free_vcap;
> +	}
> +
> +	/* Initialize the media entity */
> +	vcap->vdev.entity.name = name;
> +	vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L;
> +	ret = media_entity_pads_init(&vcap->vdev.entity,
> +				     num_pads, vcap->ved.pads);
> +	if (ret)
> +		goto err_clean_pads;
> +
> +	/* Initialize the lock */
> +	mutex_init(&vcap->lock);
> +
> +	/* Initialize the vb2 queue */
> +	q = &vcap->queue;
> +	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	q->io_modes = VB2_MMAP | VB2_DMABUF;

Please add VB2_READ and VB2_USERPTR as well since all are supported.

> +	q->drv_priv = vcap;
> +	q->buf_struct_size = sizeof(struct vimc_cap_buffer);
> +	q->ops = &vimc_cap_qops;
> +	q->mem_ops = &vb2_vmalloc_memops;
> +	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> +	q->min_buffers_needed = 2;
> +	q->lock = &vcap->lock;
> +
> +	ret = vb2_queue_init(q);
> +	if (ret) {
> +		dev_err(vcap->dev,
> +			"vb2 queue init failed (err=%d)\n", ret);
> +		goto err_clean_m_ent;
> +	}
> +
> +	/* Initialize buffer list and its lock */
> +	INIT_LIST_HEAD(&vcap->buf_list);
> +	spin_lock_init(&vcap->qlock);
> +
> +	/* Set the frame format (this is hardcoded for now) */
> +	vcap->format.width = 640;
> +	vcap->format.height = 480;
> +	vcap->format.pixelformat = V4L2_PIX_FMT_RGB24;
> +	vcap->format.field = V4L2_FIELD_NONE;
> +	vcap->format.colorspace = V4L2_COLORSPACE_SRGB;
> +
> +	vpix = vimc_pix_map_by_pixelformat(vcap->format.pixelformat);
> +	/* This should never be NULL, as we won't allow any format
> +	 * other then the ones in the vimc_pix_map_list table */
> +	BUG_ON(!vpix);
> +
> +	vcap->format.bytesperline = vcap->format.width * vpix->bpp;
> +	vcap->format.sizeimage = vcap->format.bytesperline * vcap->format.height;
> +
> +	/* Fill the vimc_ent_device struct */
> +	vcap->ved.destroy = vimc_cap_destroy;
> +	vcap->ved.ent = &vcap->vdev.entity;
> +	vcap->ved.process_frame = vimc_cap_process_frame;
> +
> +	/* Initialize the video_device struct */
> +	vdev = &vcap->vdev;
> +	vdev->entity.ops = &vimc_cap_mops;
> +	vdev->release = video_device_release_empty;
> +	vdev->fops = &vimc_cap_fops;
> +	vdev->ioctl_ops = &vimc_cap_ioctl_ops;
> +	vdev->lock = &vcap->lock;
> +	vdev->queue = q;
> +	vdev->v4l2_dev = vcap->v4l2_dev;
> +	vdev->vfl_dir = VFL_DIR_RX;
> +	strlcpy(vdev->name, name, sizeof(vdev->name));
> +	video_set_drvdata(vdev, vcap);
> +
> +	/* Register the video_device with the v4l2 and the media framework */
> +	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
> +	if (ret) {
> +		dev_err(vcap->dev,
> +			"video register failed (err=%d)\n", ret);
> +		goto err_release_queue;
> +	}
> +
> +	return &vcap->ved;
> +
> +err_release_queue:
> +	vb2_queue_release(q);
> +err_clean_m_ent:
> +	media_entity_cleanup(&vcap->vdev.entity);
> +err_clean_pads:
> +	vimc_pads_cleanup(vcap->ved.pads);
> +err_free_vcap:
> +	kfree(vcap);
> +
> +	return ERR_PTR(ret);
> +}

Regards,

	Hans

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

* Re: [PATCH v4] [media] vimc: Virtual Media Controller core, capture and sensor
  2016-07-01 12:39 ` Hans Verkuil
@ 2016-08-17 22:08   ` Helen Koike
  2016-08-17 22:09   ` [PATCH v5] " Helen Koike
  1 sibling, 0 replies; 41+ messages in thread
From: Helen Koike @ 2016-08-17 22:08 UTC (permalink / raw)
  To: Hans Verkuil, linux-media, laurent.pinchart, jgebben, mchehab
  Cc: Helen Fornazier

Hi Hans

On 01-07-2016 09:39, Hans Verkuil wrote:
> Hi Helen,
>
> Better late than never, but I finally have time for a review, mostly with a eye for V4L2 issues.

Thank you for your review, I'll incorporate your suggestions in v5.
I am also preparing a patch series that integrates the tpg and have much 
more functionality, I want to add the optimization where I analyze the 
pipe and generate the final image directly from the video capture node 
instead of processing all the images in each processing unit. I hope 
that it will be useful.

Helen

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

* [PATCH v5] [media] vimc: Virtual Media Controller core, capture and sensor
  2016-07-01 12:39 ` Hans Verkuil
  2016-08-17 22:08   ` Helen Koike
@ 2016-08-17 22:09   ` Helen Koike
  2016-08-22 10:57     ` Hans Verkuil
  1 sibling, 1 reply; 41+ messages in thread
From: Helen Koike @ 2016-08-17 22:09 UTC (permalink / raw)
  To: Hans Verkuil, linux-media, laurent.pinchart, jgebben, mchehab
  Cc: Helen Fornazier, Helen Koike

From: Helen Fornazier <helen.fornazier@gmail.com>

First version of the Virtual Media Controller.
Add a simple version of the core of the driver, the capture and
sensor nodes in the topology, generating a grey image in a hardcoded
format.

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

---

Patch based in media/master tree, and available here:
https://github.com/helen-fornazier/opw-staging/tree/vimc/devel/vpu

Changes since v4:
- coding style fixes
- remove BUG_ON
- change copyright to 2016
- depens on VIDEO_V4L2_SUBDEV_API instead of select
- remove assignement of V4L2_CAP_DEVICE_CAPS
- s/vimc_cap_g_fmt_vid_cap/vimc_cap_fmt_vid_cap
- fix vimc_cap_queue_setup declaration type
- remove wrong buffer size check and add it in vimc_cap_buffer_prepare
- vimc_cap_create: remove unecessary check if v4l2_dev or v4l2_dev->dev is null
- vimc_cap_create: only allow a single pad
- vimc_sen_create: only allow source pads, remove unecessary source pads
checks in vimc_thread_sen

Changes since v3: fix rmmod crash and built-in compile
- Re-order unregister calls in vimc_device_unregister function (remove
rmmod issue)
- Call media_device_unregister_entity in vimc_raw_destroy
- Add depends on VIDEO_DEV && VIDEO_V4L2 and select VIDEOBUF2_VMALLOC
- Check *nplanes in queue_setup (this remove v4l2-compliance fail)
- Include <linux/kthread.h> in vimc-sensor.c
- Move include of <linux/slab.h> from vimc-core.c to vimc-core.h
- Generate 60 frames per sec instead of 1 in the sensor

Changes since v2: update with current media master tree
- Add struct media_pipeline in vimc_cap_device
- Use vb2_v4l2_buffer instead of vb2_buffer
- Typos
- Remove usage of entity->type and use entity->function instead
- Remove fmt argument from queue setup
- Use ktime_get_ns instead of v4l2_get_timestamp
- Iterate over link's list using list_for_each_entry
- Use media_device_{init, cleanup}
- Use entity->use_count to keep track of entities instead of the old
entity->id
- Replace media_entity_init by media_entity_pads_init
---
 drivers/media/platform/Kconfig             |   2 +
 drivers/media/platform/Makefile            |   1 +
 drivers/media/platform/vimc/Kconfig        |   7 +
 drivers/media/platform/vimc/Makefile       |   3 +
 drivers/media/platform/vimc/vimc-capture.c | 554 ++++++++++++++++++++++++++
 drivers/media/platform/vimc/vimc-capture.h |  28 ++
 drivers/media/platform/vimc/vimc-core.c    | 600 +++++++++++++++++++++++++++++
 drivers/media/platform/vimc/vimc-core.h    |  57 +++
 drivers/media/platform/vimc/vimc-sensor.c  | 279 ++++++++++++++
 drivers/media/platform/vimc/vimc-sensor.h  |  28 ++
 10 files changed, 1559 insertions(+)
 create mode 100644 drivers/media/platform/vimc/Kconfig
 create mode 100644 drivers/media/platform/vimc/Makefile
 create mode 100644 drivers/media/platform/vimc/vimc-capture.c
 create mode 100644 drivers/media/platform/vimc/vimc-capture.h
 create mode 100644 drivers/media/platform/vimc/vimc-core.c
 create mode 100644 drivers/media/platform/vimc/vimc-core.h
 create mode 100644 drivers/media/platform/vimc/vimc-sensor.c
 create mode 100644 drivers/media/platform/vimc/vimc-sensor.h

diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index f25344b..ead66b2 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -329,6 +329,8 @@ menuconfig V4L_TEST_DRIVERS
 
 if V4L_TEST_DRIVERS
 
+source "drivers/media/platform/vimc/Kconfig"
+
 source "drivers/media/platform/vivid/Kconfig"
 
 config VIDEO_VIM2M
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 21771c1..fd0cd83 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_VIDEO_OMAP3)	+= omap3isp/
 
 obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o
 
+obj-$(CONFIG_VIDEO_VIMC)		+= vimc/
 obj-$(CONFIG_VIDEO_VIVID)		+= vivid/
 obj-$(CONFIG_VIDEO_VIM2M)		+= vim2m.o
 
diff --git a/drivers/media/platform/vimc/Kconfig b/drivers/media/platform/vimc/Kconfig
new file mode 100644
index 0000000..b48819c
--- /dev/null
+++ b/drivers/media/platform/vimc/Kconfig
@@ -0,0 +1,7 @@
+config VIDEO_VIMC
+	tristate "Virtual Media Controller Driver (VIMC)"
+	depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+	select VIDEOBUF2_VMALLOC
+	default n
+	---help---
+	  Skeleton driver for Virtual Media Controller
diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile
new file mode 100644
index 0000000..c45195e
--- /dev/null
+++ b/drivers/media/platform/vimc/Makefile
@@ -0,0 +1,3 @@
+vimc-objs := vimc-core.o vimc-capture.o vimc-sensor.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
new file mode 100644
index 0000000..dfc031d
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-capture.c
@@ -0,0 +1,554 @@
+/*
+ * vimc-capture.c Virtual Media Controller Driver
+ *
+ * Copyright (C) 2016 Helen Koike F. <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-vmalloc.h>
+#include <media/v4l2-ioctl.h>
+
+#include "vimc-capture.h"
+
+struct vimc_cap_device {
+	struct vimc_ent_device ved;
+	struct video_device vdev;
+	struct v4l2_device *v4l2_dev;
+	struct device *dev;
+	struct v4l2_pix_format format;
+	struct vb2_queue queue;
+	struct list_head buf_list;
+	/*
+	 * NOTE: in a real driver, a spin lock must be used to access the
+	 * queue because the frames are generated from a hardware interruption
+	 * and the isr is not allowed to sleep.
+	 * Even if it is not necessary a spinlock in the vimc driver, we
+	 * use it here as a code reference
+	 */
+	spinlock_t qlock;
+	struct mutex lock;
+	u32 sequence;
+	struct media_pipeline pipe;
+};
+
+struct vimc_cap_buffer {
+	/*
+	 * vb2_buffer must be the first element
+	 * the videobuf2 framework will allocate this struct based on
+	 * buf_struct_size and use the first sizeof(struct vb2_buffer) bytes of
+	 * memory as a vb2_buffer
+	 */
+	struct vb2_v4l2_buffer vb2;
+	struct list_head list;
+};
+
+static int vimc_cap_querycap(struct file *file, void *priv,
+			     struct v4l2_capability *cap)
+{
+	struct vimc_cap_device *vcap = video_drvdata(file);
+
+	strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
+	strlcpy(cap->card, KBUILD_MODNAME, sizeof(cap->card));
+	snprintf(cap->bus_info, sizeof(cap->bus_info),
+		 "platform:%s", vcap->v4l2_dev->name);
+
+	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+
+	return 0;
+}
+
+static int vimc_cap_enum_input(struct file *file, void *priv,
+			       struct v4l2_input *i)
+{
+	/* We only have one input */
+	if (i->index > 0)
+		return -EINVAL;
+
+	i->type = V4L2_INPUT_TYPE_CAMERA;
+	strlcpy(i->name, "VIMC capture", sizeof(i->name));
+
+	return 0;
+}
+
+static int vimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
+{
+	/* We only have one input */
+	*i = 0;
+	return 0;
+}
+
+static int vimc_cap_s_input(struct file *file, void *priv, unsigned int i)
+{
+	/* We only have one input */
+	return i ? -EINVAL : 0;
+}
+
+static int vimc_cap_fmt_vid_cap(struct file *file, void *priv,
+				  struct v4l2_format *f)
+{
+	struct vimc_cap_device *vcap = video_drvdata(file);
+
+	f->fmt.pix = vcap->format;
+
+	return 0;
+}
+
+static int vimc_cap_enum_fmt_vid_cap(struct file *file, void *priv,
+				     struct v4l2_fmtdesc *f)
+{
+	struct vimc_cap_device *vcap = video_drvdata(file);
+
+	if (f->index > 0)
+		return -EINVAL;
+
+	/* We only support one format for now */
+	f->pixelformat = vcap->format.pixelformat;
+
+	return 0;
+}
+
+static const struct v4l2_file_operations vimc_cap_fops = {
+	.owner		= THIS_MODULE,
+	.open		= v4l2_fh_open,
+	.release	= vb2_fop_release,
+	.read           = vb2_fop_read,
+	.poll		= vb2_fop_poll,
+	.unlocked_ioctl = video_ioctl2,
+	.mmap           = vb2_fop_mmap,
+};
+
+static const struct v4l2_ioctl_ops vimc_cap_ioctl_ops = {
+	.vidioc_querycap = vimc_cap_querycap,
+
+	.vidioc_enum_input = vimc_cap_enum_input,
+	.vidioc_g_input = vimc_cap_g_input,
+	.vidioc_s_input = vimc_cap_s_input,
+
+	.vidioc_g_fmt_vid_cap = vimc_cap_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap = vimc_cap_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap = vimc_cap_fmt_vid_cap,
+	.vidioc_enum_fmt_vid_cap = vimc_cap_enum_fmt_vid_cap,
+
+	.vidioc_reqbufs = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs = vb2_ioctl_create_bufs,
+	.vidioc_querybuf = vb2_ioctl_querybuf,
+	.vidioc_qbuf = vb2_ioctl_qbuf,
+	.vidioc_dqbuf = vb2_ioctl_dqbuf,
+	.vidioc_expbuf = vb2_ioctl_expbuf,
+	.vidioc_streamon = vb2_ioctl_streamon,
+	.vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+static void vimc_cap_return_all_buffers(struct vimc_cap_device *vcap,
+					enum vb2_buffer_state state)
+{
+	struct vimc_cap_buffer *vbuf, *node;
+
+	spin_lock(&vcap->qlock);
+
+	list_for_each_entry_safe(vbuf, node, &vcap->buf_list, list) {
+		vb2_buffer_done(&vbuf->vb2.vb2_buf, state);
+		list_del(&vbuf->list);
+	}
+
+	spin_unlock(&vcap->qlock);
+}
+
+static int vimc_cap_pipeline_s_stream(struct vimc_cap_device *vcap, int enable)
+{
+	int ret;
+	struct media_pad *pad;
+	struct media_entity *entity;
+	struct v4l2_subdev *sd;
+
+	/* Start the stream in the subdevice direct connected */
+	entity = &vcap->vdev.entity;
+	pad = media_entity_remote_pad(&entity->pads[0]);
+
+	/* If we are not connected to any subdev node, it means there is nothing
+	 * to activate on the pipe (e.g. we can be connected with an input
+	 * device or we are not connected at all)
+	 */
+	if (pad == NULL || !is_media_entity_v4l2_subdev(pad->entity))
+		return 0;
+
+	entity = pad->entity;
+	sd = media_entity_to_v4l2_subdev(entity);
+
+	ret = v4l2_subdev_call(sd, video, s_stream, enable);
+	if (ret && ret != -ENOIOCTLCMD)
+		return ret;
+
+	return 0;
+}
+
+static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
+	struct media_entity *entity;
+	int ret;
+
+	vcap->sequence = 0;
+
+	/* Start the media pipeline */
+	entity = &vcap->vdev.entity;
+	ret = media_entity_pipeline_start(entity, &vcap->pipe);
+	if (ret) {
+		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
+		return ret;
+	}
+
+	/* Enable streaming from the pipe */
+	ret = vimc_cap_pipeline_s_stream(vcap, 1);
+	if (ret) {
+		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Stop the stream engine. Any remaining buffers in the stream queue are
+ * dequeued and passed on to the vb2 framework marked as STATE_ERROR.
+ */
+static void vimc_cap_stop_streaming(struct vb2_queue *vq)
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
+
+	/* Disable streaming from the pipe */
+	vimc_cap_pipeline_s_stream(vcap, 0);
+
+	/* Stop the media pipeline */
+	media_entity_pipeline_stop(&vcap->vdev.entity);
+
+	/* Release all active buffers */
+	vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_ERROR);
+}
+
+static void vimc_cap_buf_queue(struct vb2_buffer *vb2_buf)
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb2_buf->vb2_queue);
+	struct vimc_cap_buffer *buf = container_of(vb2_buf,
+						   struct vimc_cap_buffer,
+						   vb2.vb2_buf);
+
+	spin_lock(&vcap->qlock);
+	list_add_tail(&buf->list, &vcap->buf_list);
+	spin_unlock(&vcap->qlock);
+}
+
+static int vimc_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
+				unsigned int *nplanes, unsigned int sizes[],
+				struct device *alloc_devs[])
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
+
+	if (*nplanes)
+		return sizes[0] < vcap->format.sizeimage ? -EINVAL : 0;
+	/* We don't support multiplanes for now */
+	*nplanes = 1;
+	sizes[0] = vcap->format.sizeimage;
+
+	return 0;
+}
+
+/*
+ * Prepare the buffer for queueing to the DMA engine: check and set the
+ * payload size.
+ */
+static int vimc_cap_buffer_prepare(struct vb2_buffer *vb)
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb->vb2_queue);
+	unsigned long size = vcap->format.sizeimage;
+
+	if (vb2_plane_size(vb, 0) < size) {
+		dev_err(vcap->dev, "buffer too small (%lu < %lu)\n",
+			vb2_plane_size(vb, 0), size);
+		return -EINVAL;
+	}
+
+	vb2_set_plane_payload(vb, 0, size);
+	return 0;
+}
+
+static const struct vb2_ops vimc_cap_qops = {
+	.start_streaming	= vimc_cap_start_streaming,
+	.stop_streaming		= vimc_cap_stop_streaming,
+	.buf_queue		= vimc_cap_buf_queue,
+	.queue_setup		= vimc_cap_queue_setup,
+	.buf_prepare		= vimc_cap_buffer_prepare,
+	/*
+	 * Since q->lock is set we can use the standard
+	 * vb2_ops_wait_prepare/finish helper functions.
+	 */
+	.wait_prepare		= vb2_ops_wait_prepare,
+	.wait_finish		= vb2_ops_wait_finish,
+};
+
+/* NOTE: this function is a copy of v4l2_subdev_link_validate_get_format
+ * maybe the v4l2 function should be public
+ */
+static int vimc_cap_v4l2_subdev_link_validate_get_format(struct media_pad *pad,
+						struct v4l2_subdev_format *fmt)
+{
+	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity);
+
+	fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	fmt->pad = pad->index;
+	return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt);
+}
+
+static int vimc_cap_link_validate(struct media_link *link)
+{
+	struct v4l2_subdev_format source_fmt;
+	struct v4l2_pix_format *sink_fmt;
+	struct vimc_cap_device *vcap;
+	const struct vimc_pix_map *vpix;
+	int ret;
+
+	/* Retrieve the video capture device */
+	vcap = container_of(link->sink->entity,
+			    struct vimc_cap_device, vdev.entity);
+
+	/* If the connected node is not a subdevice type
+	 * then it's a raw node from vimc-core, ignore the link for now
+	 * TODO: remove this when there are no more raw nodes in the
+	 * core and return error instead
+	 */
+	if (!is_media_entity_v4l2_subdev(link->source->entity))
+		return 0;
+
+	/* Get the the format of the video device */
+	sink_fmt = &vcap->format;
+
+	/* Get the the format of the subdev */
+	ret = vimc_cap_v4l2_subdev_link_validate_get_format(link->source,
+							    &source_fmt);
+	if (ret)
+		return ret;
+
+	dev_dbg(vcap->dev,
+		"cap: %s: link validate formats src:%dx%d %d sink:%dx%d %d\n",
+		vcap->vdev.name,
+		source_fmt.format.width, source_fmt.format.height,
+		source_fmt.format.code,
+		sink_fmt->width, sink_fmt->height,
+		sink_fmt->pixelformat);
+
+	/* Validate the format */
+
+	vpix = vimc_pix_map_by_pixelformat(sink_fmt->pixelformat);
+	if (!vpix)
+		return -EINVAL;
+
+	/* The width, height and code must match. */
+	if (source_fmt.format.width != sink_fmt->width
+	    || source_fmt.format.height != sink_fmt->height
+	    || vpix->code != source_fmt.format.code)
+		return -EINVAL;
+
+	/* The field order must match, or the sink field order must be NONE
+	 * to support interlaced hardware connected to bridges that support
+	 * progressive formats only.
+	 */
+	if (source_fmt.format.field != sink_fmt->field &&
+	    sink_fmt->field != V4L2_FIELD_NONE)
+		return -EINVAL;
+
+	return 0;
+}
+
+static const struct media_entity_operations vimc_cap_mops = {
+	.link_validate		= vimc_cap_link_validate,
+};
+
+static void vimc_cap_destroy(struct vimc_ent_device *ved)
+{
+	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
+						    ved);
+
+	vb2_queue_release(&vcap->queue);
+	media_entity_cleanup(ved->ent);
+	video_unregister_device(&vcap->vdev);
+	vimc_pads_cleanup(vcap->ved.pads);
+	kfree(vcap);
+}
+
+static void vimc_cap_process_frame(struct vimc_ent_device *ved,
+				   struct media_pad *sink, const void *frame)
+{
+	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
+						    ved);
+	struct vimc_cap_buffer *vimc_buf;
+	void *vbuf;
+
+	/* If the stream in this node is not active, just return */
+	mutex_lock(&vcap->lock);
+	if (!vb2_is_busy(&vcap->queue)) {
+		mutex_unlock(&vcap->lock);
+		return;
+	}
+	mutex_unlock(&vcap->lock);
+
+	spin_lock(&vcap->qlock);
+
+	/* Get the first entry of the list */
+	vimc_buf = list_first_entry_or_null(&vcap->buf_list,
+					    typeof(*vimc_buf), list);
+	if (!vimc_buf) {
+		spin_unlock(&vcap->qlock);
+		return;
+	}
+
+	/* Remove this entry from the list */
+	list_del(&vimc_buf->list);
+
+	spin_unlock(&vcap->qlock);
+
+	/* Fill the buffer */
+	vimc_buf->vb2.vb2_buf.timestamp = ktime_get_ns();
+	vimc_buf->vb2.sequence = vcap->sequence++;
+	vimc_buf->vb2.field = vcap->format.field;
+
+	vbuf = vb2_plane_vaddr(&vimc_buf->vb2.vb2_buf, 0);
+
+	memcpy(vbuf, frame, vcap->format.sizeimage);
+
+	/* Set it as ready */
+	vb2_set_plane_payload(&vimc_buf->vb2.vb2_buf, 0,
+			      vcap->format.sizeimage);
+	vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag)
+{
+	int ret;
+	struct vb2_queue *q;
+	struct video_device *vdev;
+	struct vimc_cap_device *vcap;
+	const struct vimc_pix_map *vpix;
+
+	/* Check entity configuration params
+	 * NOTE: we only support a single sink pad
+	 */
+	if (!name || num_pads != 1 || !pads_flag ||
+	    !(pads_flag[0] & MEDIA_PAD_FL_SINK))
+		return ERR_PTR(-EINVAL);
+
+	/* Allocate the vimc_cap_device struct */
+	vcap = kzalloc(sizeof(*vcap), GFP_KERNEL);
+	if (!vcap)
+		return ERR_PTR(-ENOMEM);
+
+	/* Link the vimc_cap_device struct with v4l2 and dev parent */
+	vcap->v4l2_dev = v4l2_dev;
+	vcap->dev = v4l2_dev->dev;
+
+	/* Allocate the pads */
+	vcap->ved.pads = vimc_pads_init(num_pads, pads_flag);
+	if (IS_ERR(vcap->ved.pads)) {
+		ret = PTR_ERR(vcap->ved.pads);
+		goto err_free_vcap;
+	}
+
+	/* Initialize the media entity */
+	vcap->vdev.entity.name = name;
+	vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L;
+	ret = media_entity_pads_init(&vcap->vdev.entity,
+				     num_pads, vcap->ved.pads);
+	if (ret)
+		goto err_clean_pads;
+
+	/* Initialize the lock */
+	mutex_init(&vcap->lock);
+
+	/* Initialize the vb2 queue */
+	q = &vcap->queue;
+	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	q->io_modes = VB2_MMAP | VB2_DMABUF;
+	q->drv_priv = vcap;
+	q->buf_struct_size = sizeof(struct vimc_cap_buffer);
+	q->ops = &vimc_cap_qops;
+	q->mem_ops = &vb2_vmalloc_memops;
+	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	q->min_buffers_needed = 2;
+	q->lock = &vcap->lock;
+
+	ret = vb2_queue_init(q);
+	if (ret) {
+		dev_err(vcap->dev,
+			"vb2 queue init failed (err=%d)\n", ret);
+		goto err_clean_m_ent;
+	}
+
+	/* Initialize buffer list and its lock */
+	INIT_LIST_HEAD(&vcap->buf_list);
+	spin_lock_init(&vcap->qlock);
+
+	/* Set the frame format (this is hardcoded for now) */
+	vcap->format.width = 640;
+	vcap->format.height = 480;
+	vcap->format.pixelformat = V4L2_PIX_FMT_RGB24;
+	vcap->format.field = V4L2_FIELD_NONE;
+	vcap->format.colorspace = V4L2_COLORSPACE_SRGB;
+
+	vpix = vimc_pix_map_by_pixelformat(vcap->format.pixelformat);
+
+	vcap->format.bytesperline = vcap->format.width * vpix->bpp;
+	vcap->format.sizeimage = vcap->format.bytesperline *
+				 vcap->format.height;
+
+	/* Fill the vimc_ent_device struct */
+	vcap->ved.destroy = vimc_cap_destroy;
+	vcap->ved.ent = &vcap->vdev.entity;
+	vcap->ved.process_frame = vimc_cap_process_frame;
+
+	/* Initialize the video_device struct */
+	vdev = &vcap->vdev;
+	vdev->entity.ops = &vimc_cap_mops;
+	vdev->release = video_device_release_empty;
+	vdev->fops = &vimc_cap_fops;
+	vdev->ioctl_ops = &vimc_cap_ioctl_ops;
+	vdev->lock = &vcap->lock;
+	vdev->queue = q;
+	vdev->v4l2_dev = vcap->v4l2_dev;
+	vdev->vfl_dir = VFL_DIR_RX;
+	strlcpy(vdev->name, name, sizeof(vdev->name));
+	video_set_drvdata(vdev, vcap);
+
+	/* Register the video_device with the v4l2 and the media framework */
+	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+	if (ret) {
+		dev_err(vcap->dev,
+			"video register failed (err=%d)\n", ret);
+		goto err_release_queue;
+	}
+
+	return &vcap->ved;
+
+err_release_queue:
+	vb2_queue_release(q);
+err_clean_m_ent:
+	media_entity_cleanup(&vcap->vdev.entity);
+err_clean_pads:
+	vimc_pads_cleanup(vcap->ved.pads);
+err_free_vcap:
+	kfree(vcap);
+
+	return ERR_PTR(ret);
+}
diff --git a/drivers/media/platform/vimc/vimc-capture.h b/drivers/media/platform/vimc/vimc-capture.h
new file mode 100644
index 0000000..bcf9fc37
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-capture.h
@@ -0,0 +1,28 @@
+/*
+ * vimc-capture.h Virtual Media Controller Driver
+ *
+ * Copyright (C) 2016 Helen Koike F. <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIMC_CAPTURE_H_
+#define _VIMC_CAPTURE_H_
+
+#include "vimc-core.h"
+
+struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag);
+
+#endif
diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c
new file mode 100644
index 0000000..273b386
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-core.c
@@ -0,0 +1,600 @@
+/*
+ * vimc-core.c Virtual Media Controller Driver
+ *
+ * Copyright (C) 2016 Helen Koike F. <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <media/media-device.h>
+#include <media/v4l2-device.h>
+
+#include "vimc-capture.h"
+#include "vimc-core.h"
+#include "vimc-sensor.h"
+
+#define VIMC_PDEV_NAME "vimc"
+#define VIMC_MDEV_MODEL_NAME "VIMC MDEV"
+
+#define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) {	\
+	.src_ent = src,						\
+	.src_pad = srcpad,					\
+	.sink_ent = sink,					\
+	.sink_pad = sinkpad,					\
+	.flags = link_flags,					\
+}
+
+#define VIMC_PIX_MAP(_code, _bpp, _pixelformat) {	\
+	.code = _code,					\
+	.pixelformat = _pixelformat,			\
+	.bpp = _bpp,					\
+}
+
+struct vimc_device {
+	/* The pipeline configuration
+	 * (filled before calling vimc_device_register)
+	 */
+	const struct vimc_pipeline_config *pipe_cfg;
+
+	/* The Associated media_device parent */
+	struct media_device mdev;
+
+	/* Internal v4l2 parent device*/
+	struct v4l2_device v4l2_dev;
+
+	/* Internal topology */
+	struct vimc_ent_device **ved;
+};
+
+/**
+ * enum vimc_ent_node - Select the functionality of a node in the topology
+ * @VIMC_ENT_NODE_SENSOR:	A node of type SENSOR simulates a camera sensor
+ *				generating internal images in bayer format and
+ *				propagating those images through the pipeline
+ * @VIMC_ENT_NODE_CAPTURE:	A node of type CAPTURE is a v4l2 video_device
+ *				that exposes the received image from the
+ *				pipeline to the user space
+ * @VIMC_ENT_NODE_INPUT:	A node of type INPUT is a v4l2 video_device that
+ *				receives images from the user space and
+ *				propagates them through the pipeline
+ * @VIMC_ENT_NODE_DEBAYER:	A node type DEBAYER expects to receive a frame
+ *				in bayer format converts it to RGB
+ * @VIMC_ENT_NODE_SCALER:	A node of type SCALER scales the received image
+ *				by a given multiplier
+ *
+ * This enum is used in the entity configuration struct to allow the definition
+ * of a custom topology specifying the role of each node on it.
+ */
+enum vimc_ent_node {
+	VIMC_ENT_NODE_SENSOR,
+	VIMC_ENT_NODE_CAPTURE,
+	VIMC_ENT_NODE_INPUT,
+	VIMC_ENT_NODE_DEBAYER,
+	VIMC_ENT_NODE_SCALER,
+};
+
+/* Structure which describes individual configuration for each entity */
+struct vimc_ent_config {
+	const char *name;
+	size_t pads_qty;
+	const unsigned long *pads_flag;
+	enum vimc_ent_node node;
+};
+
+/* Structure which describes links between entities */
+struct vimc_ent_link {
+	unsigned int src_ent;
+	u16 src_pad;
+	unsigned int sink_ent;
+	u16 sink_pad;
+	u32 flags;
+};
+
+/* Structure which describes the whole topology */
+struct vimc_pipeline_config {
+	const struct vimc_ent_config *ents;
+	size_t num_ents;
+	const struct vimc_ent_link *links;
+	size_t num_links;
+};
+
+/* --------------------------------------------------------------------------
+ * Topology Configuration
+ */
+
+static const struct vimc_ent_config ent_config[] = {
+	{
+		.name = "Sensor A",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_SENSOR,
+	},
+	{
+		.name = "Sensor B",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_SENSOR,
+	},
+	{
+		.name = "Debayer A",
+		.pads_qty = 2,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
+						     MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_DEBAYER,
+	},
+	{
+		.name = "Debayer B",
+		.pads_qty = 2,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
+						     MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_DEBAYER,
+	},
+	{
+		.name = "Raw Capture 0",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
+		.node = VIMC_ENT_NODE_CAPTURE,
+	},
+	{
+		.name = "Raw Capture 1",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
+		.node = VIMC_ENT_NODE_CAPTURE,
+	},
+	{
+		.name = "RGB/YUV Input",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_INPUT,
+	},
+	{
+		.name = "Scaler",
+		.pads_qty = 2,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
+						     MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_SCALER,
+	},
+	{
+		.name = "RGB/YUV Capture",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
+		.node = VIMC_ENT_NODE_CAPTURE,
+	},
+};
+
+static const struct vimc_ent_link ent_links[] = {
+	/* Link: Sensor A (Pad 0)->(Pad 0) Debayer A */
+	VIMC_ENT_LINK(0, 0, 2, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Sensor A (Pad 0)->(Pad 0) Raw Capture 0 */
+	VIMC_ENT_LINK(0, 0, 4, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Sensor B (Pad 0)->(Pad 0) Debayer B */
+	VIMC_ENT_LINK(1, 0, 3, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Sensor B (Pad 0)->(Pad 0) Raw Capture 1 */
+	VIMC_ENT_LINK(1, 0, 5, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Debayer A (Pad 1)->(Pad 0) Scaler */
+	VIMC_ENT_LINK(2, 1, 7, 0, MEDIA_LNK_FL_ENABLED),
+	/* Link: Debayer B (Pad 1)->(Pad 0) Scaler */
+	VIMC_ENT_LINK(3, 1, 7, 0, 0),
+	/* Link: RGB/YUV Input (Pad 0)->(Pad 0) Scaler */
+	VIMC_ENT_LINK(6, 0, 7, 0, 0),
+	/* Link: Scaler (Pad 1)->(Pad 0) RGB/YUV Capture */
+	VIMC_ENT_LINK(7, 1, 8, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+};
+
+static const struct vimc_pipeline_config pipe_cfg = {
+	.ents		= ent_config,
+	.num_ents	= ARRAY_SIZE(ent_config),
+	.links		= ent_links,
+	.num_links	= ARRAY_SIZE(ent_links)
+};
+
+/* -------------------------------------------------------------------------- */
+
+static void vimc_dev_release(struct device *dev)
+{}
+
+static struct platform_device vimc_pdev = {
+	.name		= VIMC_PDEV_NAME,
+	.dev.release	= vimc_dev_release,
+};
+
+const struct vimc_pix_map vimc_pix_map_list[] = {
+	/* TODO: add all missing formats */
+
+	/* RGB formats */
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_BGR888_1X24, 3, V4L2_PIX_FMT_BGR24),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_RGB888_1X24, 3, V4L2_PIX_FMT_RGB24),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_ARGB8888_1X32, 4, V4L2_PIX_FMT_ARGB32),
+
+	/* Bayer formats */
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR8_1X8, 1, V4L2_PIX_FMT_SBGGR8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG8_1X8, 1, V4L2_PIX_FMT_SGBRG8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG8_1X8, 1, V4L2_PIX_FMT_SGRBG8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB8_1X8, 1, V4L2_PIX_FMT_SRGGB8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR10_1X10, 2, V4L2_PIX_FMT_SBGGR10),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG10_1X10, 2, V4L2_PIX_FMT_SGBRG10),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG10_1X10, 2, V4L2_PIX_FMT_SGRBG10),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB10_1X10, 2, V4L2_PIX_FMT_SRGGB10),
+	/* 10bit raw bayer a-law compressed to 8 bits */
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8, 1, V4L2_PIX_FMT_SBGGR10ALAW8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8, 1, V4L2_PIX_FMT_SGBRG10ALAW8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8, 1, V4L2_PIX_FMT_SGRBG10ALAW8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8, 1, V4L2_PIX_FMT_SRGGB10ALAW8),
+	/* 10bit raw bayer DPCM compressed to 8 bits */
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, 1, V4L2_PIX_FMT_SBGGR10DPCM8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, 1, V4L2_PIX_FMT_SGBRG10DPCM8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, 1, V4L2_PIX_FMT_SGRBG10DPCM8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, 1, V4L2_PIX_FMT_SRGGB10DPCM8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR12_1X12, 2, V4L2_PIX_FMT_SBGGR12),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG12_1X12, 2, V4L2_PIX_FMT_SGBRG12),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG12_1X12, 2, V4L2_PIX_FMT_SGRBG12),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB12_1X12, 2, V4L2_PIX_FMT_SRGGB12),
+
+	/* End */
+	{0, 0, 0}
+};
+
+const struct vimc_pix_map *vimc_pix_map_by_code(u32 code)
+{
+	unsigned int i;
+
+	for (i = 0; vimc_pix_map_list[i].bpp; i++) {
+		if (vimc_pix_map_list[i].code == code)
+			return &vimc_pix_map_list[i];
+	}
+	return NULL;
+}
+
+const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat)
+{
+	unsigned int i;
+
+	for (i = 0; vimc_pix_map_list[i].bpp; i++) {
+		if (vimc_pix_map_list[i].pixelformat == pixelformat)
+			return &vimc_pix_map_list[i];
+	}
+	return NULL;
+}
+
+int vimc_propagate_frame(struct device *dev,
+			 struct media_pad *src, const void *frame)
+{
+	struct media_link *link;
+	struct vimc_device *vimc = dev_get_drvdata(dev);
+
+	if (!(src->flags & MEDIA_PAD_FL_SOURCE))
+		return -EINVAL;
+
+	/* Send this frame to all sink pads that are direct linked */
+	list_for_each_entry(link, &src->entity->links, list) {
+		if (link->source == src &&
+		    (link->flags & MEDIA_LNK_FL_ENABLED)) {
+			struct vimc_ent_device *ved;
+
+			ved = vimc->ved[link->sink->entity->use_count];
+			if (ved && ved->process_frame)
+				ved->process_frame(ved, link->sink, frame);
+		}
+	}
+
+	return 0;
+}
+
+static void vimc_device_unregister(struct vimc_device *vimc)
+{
+	unsigned int i;
+
+	/* Cleanup (only initialized) entities */
+	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
+		if (vimc->ved[i] && vimc->ved[i]->destroy)
+			vimc->ved[i]->destroy(vimc->ved[i]);
+
+		vimc->ved[i] = NULL;
+	}
+	v4l2_device_unregister(&vimc->v4l2_dev);
+
+	media_device_unregister(&vimc->mdev);
+	media_device_cleanup(&vimc->mdev);
+}
+
+/* Helper function to allocate and initialize pads */
+struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag)
+{
+	unsigned int i;
+	struct media_pad *pads;
+
+	/* Allocate memory for the pads */
+	pads = kcalloc(num_pads, sizeof(*pads), GFP_KERNEL);
+	if (!pads)
+		return ERR_PTR(-ENOMEM);
+
+	/* Initialize the pads */
+	for (i = 0; i < num_pads; i++) {
+		pads[i].index = i;
+		pads[i].flags = pads_flag[i];
+	}
+
+	return pads;
+}
+
+/* TODO: remove this function when all the
+ * entities specific code are implemented
+ */
+static void vimc_raw_destroy(struct vimc_ent_device *ved)
+{
+	media_device_unregister_entity(ved->ent);
+
+	media_entity_cleanup(ved->ent);
+
+	vimc_pads_cleanup(ved->pads);
+
+	kfree(ved->ent);
+
+	kfree(ved);
+}
+
+/* TODO: remove this function when all the
+ * entities specific code are implemented
+ */
+static struct vimc_ent_device *vimc_raw_create(struct v4l2_device *v4l2_dev,
+					       const char *const name,
+					       u16 num_pads,
+					       const unsigned long *pads_flag)
+{
+	int ret;
+	struct vimc_ent_device *ved;
+
+	/* Allocate the main ved struct */
+	ved = kzalloc(sizeof(*ved), GFP_KERNEL);
+	if (!ved)
+		return ERR_PTR(-ENOMEM);
+
+	/* Allocate the media entity */
+	ved->ent = kzalloc(sizeof(*ved->ent), GFP_KERNEL);
+	if (!ved->ent) {
+		ret = -ENOMEM;
+		goto err_free_ved;
+	}
+
+	/* Allocate the pads */
+	ved->pads = vimc_pads_init(num_pads, pads_flag);
+	if (IS_ERR(ved->pads)) {
+		ret = PTR_ERR(ved->pads);
+		goto err_free_ent;
+	}
+
+	/* Initialize the media entity */
+	ved->ent->name = name;
+	ved->ent->function = MEDIA_ENT_F_IO_V4L;
+	ret = media_entity_pads_init(ved->ent, num_pads, ved->pads);
+	if (ret)
+		goto err_cleanup_pads;
+
+	/* Register the media entity */
+	ret = media_device_register_entity(v4l2_dev->mdev, ved->ent);
+	if (ret)
+		goto err_cleanup_entity;
+
+	/* Fill out the destroy function and return */
+	ved->destroy = vimc_raw_destroy;
+	return ved;
+
+err_cleanup_entity:
+	media_entity_cleanup(ved->ent);
+err_cleanup_pads:
+	vimc_pads_cleanup(ved->pads);
+err_free_ent:
+	kfree(ved->ent);
+err_free_ved:
+	kfree(ved);
+
+	return ERR_PTR(ret);
+}
+
+static int vimc_device_register(struct vimc_device *vimc)
+{
+	unsigned int i;
+	int ret = 0;
+
+	/* Allocate memory for the vimc_ent_devices pointers */
+	vimc->ved = devm_kcalloc(vimc->mdev.dev, vimc->pipe_cfg->num_ents,
+				 sizeof(*vimc->ved), GFP_KERNEL);
+	if (!vimc->ved)
+		return -ENOMEM;
+
+	/* Register the media device */
+	ret = media_device_register(&vimc->mdev);
+	if (ret) {
+		dev_err(vimc->mdev.dev,
+			"media device register failed (err=%d)\n", ret);
+		return ret;
+	}
+
+	/* Link the media device within the v4l2_device */
+	vimc->v4l2_dev.mdev = &vimc->mdev;
+
+	/* Register the v4l2 struct */
+	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);
+		return ret;
+	}
+
+	/* Initialize entities */
+	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
+		struct vimc_ent_device *(*create_func)(struct v4l2_device *,
+						       const char *const,
+						       u16,
+						       const unsigned long *);
+
+		/* Register the specific node */
+		switch (vimc->pipe_cfg->ents[i].node) {
+		case VIMC_ENT_NODE_SENSOR:
+			create_func = vimc_sen_create;
+			break;
+
+		case VIMC_ENT_NODE_CAPTURE:
+			create_func = vimc_cap_create;
+			break;
+
+		/* TODO: Instantiate the specific topology node */
+		case VIMC_ENT_NODE_INPUT:
+		case VIMC_ENT_NODE_DEBAYER:
+		case VIMC_ENT_NODE_SCALER:
+		default:
+			/* TODO: remove this when all the entities specific
+			 * code are implemented
+			 */
+			create_func = vimc_raw_create;
+			break;
+		}
+
+		vimc->ved[i] = create_func(&vimc->v4l2_dev,
+					   vimc->pipe_cfg->ents[i].name,
+					   vimc->pipe_cfg->ents[i].pads_qty,
+					   vimc->pipe_cfg->ents[i].pads_flag);
+		if (IS_ERR(vimc->ved[i])) {
+			ret = PTR_ERR(vimc->ved[i]);
+			vimc->ved[i] = NULL;
+			goto err;
+		}
+
+		/* Set use_count to keep track of the ved structure */
+		vimc->ved[i]->ent->use_count = i;
+	}
+
+	/* 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];
+
+		ret = media_create_pad_link(vimc->ved[link->src_ent]->ent,
+					    link->src_pad,
+					    vimc->ved[link->sink_ent]->ent,
+					    link->sink_pad,
+					    link->flags);
+		if (ret)
+			goto err;
+	}
+
+	/* 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);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	/* Destroy de so far created topology */
+	vimc_device_unregister(vimc);
+
+	return ret;
+}
+
+static int vimc_probe(struct platform_device *pdev)
+{
+	struct vimc_device *vimc;
+	int ret;
+
+	/* Prepare the vimc topology structure */
+
+	/* Allocate memory for the vimc structure */
+	vimc = devm_kzalloc(&pdev->dev, sizeof(*vimc), GFP_KERNEL);
+	if (!vimc)
+		return -ENOMEM;
+
+	/* Set the pipeline configuration struct */
+	vimc->pipe_cfg = &pipe_cfg;
+
+	/* Initialize media device */
+	strlcpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME,
+		sizeof(vimc->mdev.model));
+	vimc->mdev.dev = &pdev->dev;
+	media_device_init(&vimc->mdev);
+
+	/* Create vimc topology */
+	ret = vimc_device_register(vimc);
+	if (ret) {
+		dev_err(vimc->mdev.dev,
+			"vimc device registration failed (err=%d)\n", ret);
+		return ret;
+	}
+
+	/* Link the topology object with the platform device object */
+	platform_set_drvdata(pdev, vimc);
+
+	return 0;
+}
+
+static int vimc_remove(struct platform_device *pdev)
+{
+	struct vimc_device *vimc;
+
+	/* Get the topology object linked with the platform device object */
+	vimc = platform_get_drvdata(pdev);
+
+	/* Destroy all the topology */
+	vimc_device_unregister(vimc);
+
+	return 0;
+}
+
+static struct platform_driver vimc_pdrv = {
+	.probe		= vimc_probe,
+	.remove		= vimc_remove,
+	.driver		= {
+		.name	= VIMC_PDEV_NAME,
+	},
+};
+
+static int __init vimc_init(void)
+{
+	int ret;
+
+	ret = platform_device_register(&vimc_pdev);
+	if (ret) {
+		dev_err(&vimc_pdev.dev,
+			"platform device registration failed (err=%d)\n", ret);
+		return ret;
+	}
+
+	ret = platform_driver_register(&vimc_pdrv);
+	if (ret) {
+		dev_err(&vimc_pdev.dev,
+			"platform driver registration failed (err=%d)\n", ret);
+
+		platform_device_unregister(&vimc_pdev);
+	}
+
+	return ret;
+}
+
+static void __exit vimc_exit(void)
+{
+	platform_driver_unregister(&vimc_pdrv);
+
+	platform_device_unregister(&vimc_pdev);
+}
+
+module_init(vimc_init);
+module_exit(vimc_exit);
+
+MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC)");
+MODULE_AUTHOR("Helen Fornazier <helen.fornazier@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/vimc/vimc-core.h b/drivers/media/platform/vimc/vimc-core.h
new file mode 100644
index 0000000..bd7abff
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-core.h
@@ -0,0 +1,57 @@
+/*
+ * vimc-core.h Virtual Media Controller Driver
+ *
+ * Copyright (C) 2016 Helen Koike F. <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIMC_CORE_H_
+#define _VIMC_CORE_H_
+
+#include <linux/slab.h>
+#include <media/v4l2-device.h>
+
+/* Struct which matches the MEDIA_BUS_FMT_ codes with the corresponding
+ * V4L2_PIX_FMT_ fourcc pixelformat and its bytes per pixel (bpp)
+ */
+struct vimc_pix_map {
+	unsigned int code;
+	unsigned int bpp;
+	u32 pixelformat;
+};
+extern const struct vimc_pix_map vimc_pix_map_list[];
+
+struct vimc_ent_device {
+	struct media_entity *ent;
+	struct media_pad *pads;
+	void (*destroy)(struct vimc_ent_device *);
+	void (*process_frame)(struct vimc_ent_device *ved,
+			      struct media_pad *sink, const void *frame);
+};
+
+int vimc_propagate_frame(struct device *dev,
+			 struct media_pad *src, const void *frame);
+
+/* Helper functions to allocate/initialize pads and free them */
+struct media_pad *vimc_pads_init(u16 num_pads,
+				 const unsigned long *pads_flag);
+static inline void vimc_pads_cleanup(struct media_pad *pads)
+{
+	kfree(pads);
+}
+
+const struct vimc_pix_map *vimc_pix_map_by_code(u32 code);
+
+const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat);
+
+#endif
diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c
new file mode 100644
index 0000000..a97ef92
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-sensor.c
@@ -0,0 +1,279 @@
+/*
+ * vimc-sensor.c Virtual Media Controller Driver
+ *
+ * Copyright (C) 2016 Helen Koike F. <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/freezer.h>
+#include <linux/kthread.h>
+#include <linux/vmalloc.h>
+#include <linux/v4l2-mediabus.h>
+#include <media/v4l2-subdev.h>
+
+#include "vimc-sensor.h"
+
+struct vimc_sen_device {
+	struct vimc_ent_device ved;
+	struct v4l2_subdev sd;
+	struct v4l2_device *v4l2_dev;
+	struct device *dev;
+	struct task_struct *kthread_sen;
+	u8 *frame;
+	/* The active format */
+	struct v4l2_mbus_framefmt mbus_format;
+	int frame_size;
+};
+
+static int vimc_sen_enum_mbus_code(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_pad_config *cfg,
+				   struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd);
+
+	/* Check if it is a valid pad */
+	if (code->pad >= vsen->sd.entity.num_pads)
+		return -EINVAL;
+
+	code->code = vsen->mbus_format.code;
+
+	return 0;
+}
+
+static int vimc_sen_enum_frame_size(struct v4l2_subdev *sd,
+				    struct v4l2_subdev_pad_config *cfg,
+				    struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd);
+
+	/* Check if it is a valid pad */
+	if (fse->pad >= vsen->sd.entity.num_pads)
+		return -EINVAL;
+
+	/* TODO: Add support to other formats */
+	if (fse->index)
+		return -EINVAL;
+
+	/* TODO: Add support for other codes */
+	if (fse->code != vsen->mbus_format.code)
+		return -EINVAL;
+
+	fse->min_width = vsen->mbus_format.width;
+	fse->max_width = vsen->mbus_format.width;
+	fse->min_height = vsen->mbus_format.height;
+	fse->max_height = vsen->mbus_format.height;
+
+	return 0;
+}
+
+static int vimc_sen_get_fmt(struct v4l2_subdev *sd,
+			    struct v4l2_subdev_pad_config *cfg,
+			    struct v4l2_subdev_format *format)
+{
+	struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd);
+
+	format->format = vsen->mbus_format;
+
+	return 0;
+}
+
+static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = {
+	.enum_mbus_code		= vimc_sen_enum_mbus_code,
+	.enum_frame_size	= vimc_sen_enum_frame_size,
+	.get_fmt		= vimc_sen_get_fmt,
+	/* TODO: Add support to other formats */
+	.set_fmt		= vimc_sen_get_fmt,
+};
+
+/* media operations */
+static const struct media_entity_operations vimc_sen_mops = {
+	.link_validate = v4l2_subdev_link_validate,
+};
+
+static int vimc_thread_sen(void *data)
+{
+	unsigned int i;
+	struct vimc_sen_device *vsen = data;
+
+	set_freezable();
+
+	for (;;) {
+		try_to_freeze();
+		if (kthread_should_stop())
+			break;
+
+		memset(vsen->frame, 100, vsen->frame_size);
+
+		/* Send the frame to all source pads */
+		for (i = 0; i < vsen->sd.entity.num_pads; i++)
+			vimc_propagate_frame(vsen->dev,
+					     &vsen->sd.entity.pads[i],
+					     vsen->frame);
+
+		/* 60 frames per second */
+		schedule_timeout_interruptible(HZ/60);
+	}
+
+	return 0;
+}
+
+static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	int ret;
+	struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd);
+
+	if (enable) {
+		const struct vimc_pix_map *vpix;
+
+		if (vsen->kthread_sen)
+			return -EINVAL;
+
+		/* Calculate the frame size */
+		vpix = vimc_pix_map_by_code(vsen->mbus_format.code);
+		vsen->frame_size = vsen->mbus_format.width * vpix->bpp *
+				   vsen->mbus_format.height;
+
+		/* Allocate the frame buffer. Use vmalloc to be able to
+		 * allocate a large amount of memory
+		 */
+		vsen->frame = vmalloc(vsen->frame_size);
+		if (!vsen->frame)
+			return -ENOMEM;
+
+		/* Initialize the image generator thread */
+		vsen->kthread_sen = kthread_run(vimc_thread_sen, vsen,
+						"%s-sen", vsen->v4l2_dev->name);
+		if (IS_ERR(vsen->kthread_sen)) {
+			v4l2_err(vsen->v4l2_dev, "kernel_thread() failed\n");
+			vfree(vsen->frame);
+			vsen->frame = NULL;
+			return PTR_ERR(vsen->kthread_sen);
+		}
+	} else {
+		if (!vsen->kthread_sen)
+			return -EINVAL;
+
+		/* Stop image generator */
+		ret = kthread_stop(vsen->kthread_sen);
+		vsen->kthread_sen = NULL;
+
+		vfree(vsen->frame);
+		vsen->frame = NULL;
+		return ret;
+	}
+
+	return 0;
+}
+
+struct v4l2_subdev_video_ops vimc_sen_video_ops = {
+	.s_stream = vimc_sen_s_stream,
+};
+
+static const struct v4l2_subdev_ops vimc_sen_ops = {
+	.pad = &vimc_sen_pad_ops,
+	.video = &vimc_sen_video_ops,
+};
+
+static void vimc_sen_destroy(struct vimc_ent_device *ved)
+{
+	struct vimc_sen_device *vsen = container_of(ved,
+						struct vimc_sen_device, ved);
+
+	media_entity_cleanup(ved->ent);
+	v4l2_device_unregister_subdev(&vsen->sd);
+	kfree(vsen);
+}
+
+struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag)
+{
+	int ret;
+	unsigned int i;
+	struct vimc_sen_device *vsen;
+
+	if (!name || (num_pads && !pads_flag))
+		return ERR_PTR(-EINVAL);
+	/* check if all pads are sources */
+	for (i = 0; i < num_pads; i++)
+		if (!(pads_flag[i] & MEDIA_PAD_FL_SOURCE))
+			return ERR_PTR(-EINVAL);
+
+	/* Allocate the vsen struct */
+	vsen = kzalloc(sizeof(*vsen), GFP_KERNEL);
+	if (!vsen)
+		return ERR_PTR(-ENOMEM);
+
+	/* Link the vimc_sen_device struct with the v4l2 parent */
+	vsen->v4l2_dev = v4l2_dev;
+	/* Link the vimc_sen_device struct with the dev parent */
+	vsen->dev = v4l2_dev->dev;
+
+	/* Allocate the pads */
+	vsen->ved.pads = vimc_pads_init(num_pads, pads_flag);
+	if (IS_ERR(vsen->ved.pads)) {
+		ret = PTR_ERR(vsen->ved.pads);
+		goto err_free_vsen;
+	}
+
+	/* Initialize the media entity */
+	vsen->sd.entity.name = name;
+	vsen->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+	ret = media_entity_pads_init(&vsen->sd.entity,
+				     num_pads, vsen->ved.pads);
+	if (ret)
+		goto err_clean_pads;
+
+	/* Set the active frame format (this is hardcoded for now) */
+	vsen->mbus_format.width = 640;
+	vsen->mbus_format.height = 480;
+	vsen->mbus_format.code = MEDIA_BUS_FMT_RGB888_1X24;
+	vsen->mbus_format.field = V4L2_FIELD_NONE;
+	vsen->mbus_format.colorspace = V4L2_COLORSPACE_SRGB;
+	vsen->mbus_format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
+	vsen->mbus_format.xfer_func = V4L2_XFER_FUNC_SRGB;
+
+	/* Fill the vimc_ent_device struct */
+	vsen->ved.destroy = vimc_sen_destroy;
+	vsen->ved.ent = &vsen->sd.entity;
+
+	/* Initialize the subdev */
+	v4l2_subdev_init(&vsen->sd, &vimc_sen_ops);
+	vsen->sd.entity.ops = &vimc_sen_mops;
+	vsen->sd.owner = THIS_MODULE;
+	strlcpy(vsen->sd.name, name, sizeof(vsen->sd.name));
+	v4l2_set_subdevdata(&vsen->sd, vsen);
+
+	/* Expose this subdev to user space */
+	vsen->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	/* Register the subdev with the v4l2 and the media framework */
+	ret = v4l2_device_register_subdev(vsen->v4l2_dev, &vsen->sd);
+	if (ret) {
+		dev_err(vsen->dev,
+			"subdev register failed (err=%d)\n", ret);
+		goto err_clean_m_ent;
+	}
+
+	return &vsen->ved;
+
+err_clean_m_ent:
+	media_entity_cleanup(&vsen->sd.entity);
+err_clean_pads:
+	vimc_pads_cleanup(vsen->ved.pads);
+err_free_vsen:
+	kfree(vsen);
+
+	return ERR_PTR(ret);
+}
diff --git a/drivers/media/platform/vimc/vimc-sensor.h b/drivers/media/platform/vimc/vimc-sensor.h
new file mode 100644
index 0000000..ffeaf6c
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-sensor.h
@@ -0,0 +1,28 @@
+/*
+ * vimc-sensor.h Virtual Media Controller Driver
+ *
+ * Copyright (C) 2016 Helen Koike F. <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIMC_SENSOR_H_
+#define _VIMC_SENSOR_H_
+
+#include "vimc-core.h"
+
+struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag);
+
+#endif
-- 
1.9.1


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

* Re: [PATCH v5] [media] vimc: Virtual Media Controller core, capture and sensor
  2016-08-17 22:09   ` [PATCH v5] " Helen Koike
@ 2016-08-22 10:57     ` Hans Verkuil
  2016-09-04 20:02       ` [PATCH v6] " Helen Koike
  2016-09-04 20:05       ` [PATCH v5] " Helen Koike
  0 siblings, 2 replies; 41+ messages in thread
From: Hans Verkuil @ 2016-08-22 10:57 UTC (permalink / raw)
  To: Helen Koike, linux-media, laurent.pinchart, jgebben, mchehab
  Cc: Helen Fornazier, Helen Koike

Hi Helen,

A few small code comments are below.

Note that if I try to capture I see these two messages in the kernel log:

[588197.368145] vimc vimc.0: Entity type for entity Sensor A was not initialized!
[588197.368169] vimc vimc.0: Entity type for entity Sensor B was not initialized!

I also can't capture anything: v4l2-ctl --stream-mmap just sits there, waiting for
frames, I guess.

I'm not sure if that has to do with the two warnings above.

I am assuming that the initial pipeline is correct and that you should be able
to start streaming. If not, then attempting to start streaming should return an
error.

On 08/18/2016 12:09 AM, Helen Koike wrote:
> From: Helen Fornazier <helen.fornazier@gmail.com>
> 
> First version of the Virtual Media Controller.
> Add a simple version of the core of the driver, the capture and
> sensor nodes in the topology, generating a grey image in a hardcoded
> format.
> 
> Signed-off-by: Helen Koike <helen.koike@collabora.com>

<snip>

> +static int vimc_cap_querycap(struct file *file, void *priv,
> +			     struct v4l2_capability *cap)
> +{
> +	struct vimc_cap_device *vcap = video_drvdata(file);
> +
> +	strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
> +	strlcpy(cap->card, KBUILD_MODNAME, sizeof(cap->card));
> +	snprintf(cap->bus_info, sizeof(cap->bus_info),
> +		 "platform:%s", vcap->v4l2_dev->name);
> +
> +	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;

This line should be moved to vimc_cap_create:

	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;

This is new. The v4l2 core will fill in the querycap capabilities for you
based on vdev->device_caps.

> +
> +	return 0;
> +}

<snip>

> +static int vimc_device_register(struct vimc_device *vimc)
> +{
> +	unsigned int i;
> +	int ret = 0;
> +
> +	/* Allocate memory for the vimc_ent_devices pointers */
> +	vimc->ved = devm_kcalloc(vimc->mdev.dev, vimc->pipe_cfg->num_ents,
> +				 sizeof(*vimc->ved), GFP_KERNEL);
> +	if (!vimc->ved)
> +		return -ENOMEM;
> +
> +	/* Register the media device */
> +	ret = media_device_register(&vimc->mdev);
> +	if (ret) {
> +		dev_err(vimc->mdev.dev,
> +			"media device register failed (err=%d)\n", ret);
> +		return ret;
> +	}
> +
> +	/* Link the media device within the v4l2_device */
> +	vimc->v4l2_dev.mdev = &vimc->mdev;
> +
> +	/* Register the v4l2 struct */
> +	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);
> +		return ret;
> +	}
> +
> +	/* Initialize entities */
> +	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
> +		struct vimc_ent_device *(*create_func)(struct v4l2_device *,
> +						       const char *const,
> +						       u16,
> +						       const unsigned long *);
> +
> +		/* Register the specific node */
> +		switch (vimc->pipe_cfg->ents[i].node) {
> +		case VIMC_ENT_NODE_SENSOR:
> +			create_func = vimc_sen_create;
> +			break;
> +
> +		case VIMC_ENT_NODE_CAPTURE:
> +			create_func = vimc_cap_create;
> +			break;
> +
> +		/* TODO: Instantiate the specific topology node */
> +		case VIMC_ENT_NODE_INPUT:
> +		case VIMC_ENT_NODE_DEBAYER:
> +		case VIMC_ENT_NODE_SCALER:
> +		default:
> +			/* TODO: remove this when all the entities specific
> +			 * code are implemented
> +			 */
> +			create_func = vimc_raw_create;
> +			break;
> +		}
> +
> +		vimc->ved[i] = create_func(&vimc->v4l2_dev,
> +					   vimc->pipe_cfg->ents[i].name,
> +					   vimc->pipe_cfg->ents[i].pads_qty,
> +					   vimc->pipe_cfg->ents[i].pads_flag);
> +		if (IS_ERR(vimc->ved[i])) {
> +			ret = PTR_ERR(vimc->ved[i]);
> +			vimc->ved[i] = NULL;
> +			goto err;
> +		}
> +
> +		/* Set use_count to keep track of the ved structure */
> +		vimc->ved[i]->ent->use_count = i;
> +	}
> +
> +	/* 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];
> +
> +		ret = media_create_pad_link(vimc->ved[link->src_ent]->ent,
> +					    link->src_pad,
> +					    vimc->ved[link->sink_ent]->ent,
> +					    link->sink_pad,
> +					    link->flags);
> +		if (ret)
> +			goto err;
> +	}
> +
> +	/* 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);
> +		goto err;
> +	}
> +
> +	return 0;
> +
> +err:
> +	/* Destroy de so far created topology */

s/de/the/

> +	vimc_device_unregister(vimc);
> +
> +	return ret;
> +}

Regards,

	Hans

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

* [PATCH v6] [media] vimc: Virtual Media Controller core, capture and sensor
  2016-08-22 10:57     ` Hans Verkuil
@ 2016-09-04 20:02       ` Helen Koike
  2016-09-06  7:33         ` Hans Verkuil
                           ` (2 more replies)
  2016-09-04 20:05       ` [PATCH v5] " Helen Koike
  1 sibling, 3 replies; 41+ messages in thread
From: Helen Koike @ 2016-09-04 20:02 UTC (permalink / raw)
  To: Hans Verkuil, linux-media, laurent.pinchart, jgebben, mchehab
  Cc: Helen Fornazier, Helen Koike

From: Helen Fornazier <helen.fornazier@gmail.com>

First version of the Virtual Media Controller.
Add a simple version of the core of the driver, the capture and
sensor nodes in the topology, generating a grey image in a hardcoded
format.

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

---

Patch based in media/master tree, and available here:
https://github.com/helen-fornazier/opw-staging/tree/vimc/devel/vpu

Changes since v5:
- Fix message "Entity type for entity Sensor A was not initialized!"
  by initializing the sensor entity.function after the calling
  v4l2_subded_init
- populate device_caps in vimc_cap_create instead of in
  vimc_cap_querycap
- Fix typo in vimc-core.c s/de/the

Changes since v4:
- coding style fixes
- remove BUG_ON
- change copyright to 2016
- depens on VIDEO_V4L2_SUBDEV_API instead of select
- remove assignement of V4L2_CAP_DEVICE_CAPS
- s/vimc_cap_g_fmt_vid_cap/vimc_cap_fmt_vid_cap
- fix vimc_cap_queue_setup declaration type
- remove wrong buffer size check and add it in vimc_cap_buffer_prepare
- vimc_cap_create: remove unecessary check if v4l2_dev or v4l2_dev->dev is null
- vimc_cap_create: only allow a single pad
- vimc_sen_create: only allow source pads, remove unecessary source pads
checks in vimc_thread_sen

Changes since v3: fix rmmod crash and built-in compile
- Re-order unregister calls in vimc_device_unregister function (remove
rmmod issue)
- Call media_device_unregister_entity in vimc_raw_destroy
- Add depends on VIDEO_DEV && VIDEO_V4L2 and select VIDEOBUF2_VMALLOC
- Check *nplanes in queue_setup (this remove v4l2-compliance fail)
- Include <linux/kthread.h> in vimc-sensor.c
- Move include of <linux/slab.h> from vimc-core.c to vimc-core.h
- Generate 60 frames per sec instead of 1 in the sensor

Changes since v2: update with current media master tree
- Add struct media_pipeline in vimc_cap_device
- Use vb2_v4l2_buffer instead of vb2_buffer
- Typos
- Remove usage of entity->type and use entity->function instead
- Remove fmt argument from queue setup
- Use ktime_get_ns instead of v4l2_get_timestamp
- Iterate over link's list using list_for_each_entry
- Use media_device_{init, cleanup}
- Use entity->use_count to keep track of entities instead of the old
entity->id
- Replace media_entity_init by media_entity_pads_init
---
 drivers/media/platform/Kconfig             |   2 +
 drivers/media/platform/Makefile            |   1 +
 drivers/media/platform/vimc/Kconfig        |   7 +
 drivers/media/platform/vimc/Makefile       |   3 +
 drivers/media/platform/vimc/vimc-capture.c | 553 ++++++++++++++++++++++++++
 drivers/media/platform/vimc/vimc-capture.h |  28 ++
 drivers/media/platform/vimc/vimc-core.c    | 600 +++++++++++++++++++++++++++++
 drivers/media/platform/vimc/vimc-core.h    |  57 +++
 drivers/media/platform/vimc/vimc-sensor.c  | 279 ++++++++++++++
 drivers/media/platform/vimc/vimc-sensor.h  |  28 ++
 10 files changed, 1558 insertions(+)
 create mode 100644 drivers/media/platform/vimc/Kconfig
 create mode 100644 drivers/media/platform/vimc/Makefile
 create mode 100644 drivers/media/platform/vimc/vimc-capture.c
 create mode 100644 drivers/media/platform/vimc/vimc-capture.h
 create mode 100644 drivers/media/platform/vimc/vimc-core.c
 create mode 100644 drivers/media/platform/vimc/vimc-core.h
 create mode 100644 drivers/media/platform/vimc/vimc-sensor.c
 create mode 100644 drivers/media/platform/vimc/vimc-sensor.h

diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 46f14dd..4a0577f 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -329,6 +329,8 @@ menuconfig V4L_TEST_DRIVERS
 
 if V4L_TEST_DRIVERS
 
+source "drivers/media/platform/vimc/Kconfig"
+
 source "drivers/media/platform/vivid/Kconfig"
 
 config VIDEO_VIM2M
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 536d1d8..dd4f658 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_VIDEO_OMAP3)	+= omap3isp/
 
 obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o
 
+obj-$(CONFIG_VIDEO_VIMC)		+= vimc/
 obj-$(CONFIG_VIDEO_VIVID)		+= vivid/
 obj-$(CONFIG_VIDEO_VIM2M)		+= vim2m.o
 
diff --git a/drivers/media/platform/vimc/Kconfig b/drivers/media/platform/vimc/Kconfig
new file mode 100644
index 0000000..b48819c
--- /dev/null
+++ b/drivers/media/platform/vimc/Kconfig
@@ -0,0 +1,7 @@
+config VIDEO_VIMC
+	tristate "Virtual Media Controller Driver (VIMC)"
+	depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+	select VIDEOBUF2_VMALLOC
+	default n
+	---help---
+	  Skeleton driver for Virtual Media Controller
diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile
new file mode 100644
index 0000000..c45195e
--- /dev/null
+++ b/drivers/media/platform/vimc/Makefile
@@ -0,0 +1,3 @@
+vimc-objs := vimc-core.o vimc-capture.o vimc-sensor.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
new file mode 100644
index 0000000..b7636cf
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-capture.c
@@ -0,0 +1,553 @@
+/*
+ * vimc-capture.c Virtual Media Controller Driver
+ *
+ * Copyright (C) 2016 Helen Koike F. <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-vmalloc.h>
+#include <media/v4l2-ioctl.h>
+
+#include "vimc-capture.h"
+
+struct vimc_cap_device {
+	struct vimc_ent_device ved;
+	struct video_device vdev;
+	struct v4l2_device *v4l2_dev;
+	struct device *dev;
+	struct v4l2_pix_format format;
+	struct vb2_queue queue;
+	struct list_head buf_list;
+	/*
+	 * NOTE: in a real driver, a spin lock must be used to access the
+	 * queue because the frames are generated from a hardware interruption
+	 * and the isr is not allowed to sleep.
+	 * Even if it is not necessary a spinlock in the vimc driver, we
+	 * use it here as a code reference
+	 */
+	spinlock_t qlock;
+	struct mutex lock;
+	u32 sequence;
+	struct media_pipeline pipe;
+};
+
+struct vimc_cap_buffer {
+	/*
+	 * vb2_buffer must be the first element
+	 * the videobuf2 framework will allocate this struct based on
+	 * buf_struct_size and use the first sizeof(struct vb2_buffer) bytes of
+	 * memory as a vb2_buffer
+	 */
+	struct vb2_v4l2_buffer vb2;
+	struct list_head list;
+};
+
+static int vimc_cap_querycap(struct file *file, void *priv,
+			     struct v4l2_capability *cap)
+{
+	struct vimc_cap_device *vcap = video_drvdata(file);
+
+	strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
+	strlcpy(cap->card, KBUILD_MODNAME, sizeof(cap->card));
+	snprintf(cap->bus_info, sizeof(cap->bus_info),
+		 "platform:%s", vcap->v4l2_dev->name);
+
+	return 0;
+}
+
+static int vimc_cap_enum_input(struct file *file, void *priv,
+			       struct v4l2_input *i)
+{
+	/* We only have one input */
+	if (i->index > 0)
+		return -EINVAL;
+
+	i->type = V4L2_INPUT_TYPE_CAMERA;
+	strlcpy(i->name, "VIMC capture", sizeof(i->name));
+
+	return 0;
+}
+
+static int vimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
+{
+	/* We only have one input */
+	*i = 0;
+	return 0;
+}
+
+static int vimc_cap_s_input(struct file *file, void *priv, unsigned int i)
+{
+	/* We only have one input */
+	return i ? -EINVAL : 0;
+}
+
+static int vimc_cap_fmt_vid_cap(struct file *file, void *priv,
+				  struct v4l2_format *f)
+{
+	struct vimc_cap_device *vcap = video_drvdata(file);
+
+	f->fmt.pix = vcap->format;
+
+	return 0;
+}
+
+static int vimc_cap_enum_fmt_vid_cap(struct file *file, void *priv,
+				     struct v4l2_fmtdesc *f)
+{
+	struct vimc_cap_device *vcap = video_drvdata(file);
+
+	if (f->index > 0)
+		return -EINVAL;
+
+	/* We only support one format for now */
+	f->pixelformat = vcap->format.pixelformat;
+
+	return 0;
+}
+
+static const struct v4l2_file_operations vimc_cap_fops = {
+	.owner		= THIS_MODULE,
+	.open		= v4l2_fh_open,
+	.release	= vb2_fop_release,
+	.read           = vb2_fop_read,
+	.poll		= vb2_fop_poll,
+	.unlocked_ioctl = video_ioctl2,
+	.mmap           = vb2_fop_mmap,
+};
+
+static const struct v4l2_ioctl_ops vimc_cap_ioctl_ops = {
+	.vidioc_querycap = vimc_cap_querycap,
+
+	.vidioc_enum_input = vimc_cap_enum_input,
+	.vidioc_g_input = vimc_cap_g_input,
+	.vidioc_s_input = vimc_cap_s_input,
+
+	.vidioc_g_fmt_vid_cap = vimc_cap_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap = vimc_cap_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap = vimc_cap_fmt_vid_cap,
+	.vidioc_enum_fmt_vid_cap = vimc_cap_enum_fmt_vid_cap,
+
+	.vidioc_reqbufs = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs = vb2_ioctl_create_bufs,
+	.vidioc_querybuf = vb2_ioctl_querybuf,
+	.vidioc_qbuf = vb2_ioctl_qbuf,
+	.vidioc_dqbuf = vb2_ioctl_dqbuf,
+	.vidioc_expbuf = vb2_ioctl_expbuf,
+	.vidioc_streamon = vb2_ioctl_streamon,
+	.vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+static void vimc_cap_return_all_buffers(struct vimc_cap_device *vcap,
+					enum vb2_buffer_state state)
+{
+	struct vimc_cap_buffer *vbuf, *node;
+
+	spin_lock(&vcap->qlock);
+
+	list_for_each_entry_safe(vbuf, node, &vcap->buf_list, list) {
+		vb2_buffer_done(&vbuf->vb2.vb2_buf, state);
+		list_del(&vbuf->list);
+	}
+
+	spin_unlock(&vcap->qlock);
+}
+
+static int vimc_cap_pipeline_s_stream(struct vimc_cap_device *vcap, int enable)
+{
+	int ret;
+	struct media_pad *pad;
+	struct media_entity *entity;
+	struct v4l2_subdev *sd;
+
+	/* Start the stream in the subdevice direct connected */
+	entity = &vcap->vdev.entity;
+	pad = media_entity_remote_pad(&entity->pads[0]);
+
+	/* If we are not connected to any subdev node, it means there is nothing
+	 * to activate on the pipe (e.g. we can be connected with an input
+	 * device or we are not connected at all)
+	 */
+	if (pad == NULL || !is_media_entity_v4l2_subdev(pad->entity))
+		return 0;
+
+	entity = pad->entity;
+	sd = media_entity_to_v4l2_subdev(entity);
+
+	ret = v4l2_subdev_call(sd, video, s_stream, enable);
+	if (ret && ret != -ENOIOCTLCMD)
+		return ret;
+
+	return 0;
+}
+
+static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
+	struct media_entity *entity;
+	int ret;
+
+	vcap->sequence = 0;
+
+	/* Start the media pipeline */
+	entity = &vcap->vdev.entity;
+	ret = media_entity_pipeline_start(entity, &vcap->pipe);
+	if (ret) {
+		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
+		return ret;
+	}
+
+	/* Enable streaming from the pipe */
+	ret = vimc_cap_pipeline_s_stream(vcap, 1);
+	if (ret) {
+		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Stop the stream engine. Any remaining buffers in the stream queue are
+ * dequeued and passed on to the vb2 framework marked as STATE_ERROR.
+ */
+static void vimc_cap_stop_streaming(struct vb2_queue *vq)
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
+
+	/* Disable streaming from the pipe */
+	vimc_cap_pipeline_s_stream(vcap, 0);
+
+	/* Stop the media pipeline */
+	media_entity_pipeline_stop(&vcap->vdev.entity);
+
+	/* Release all active buffers */
+	vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_ERROR);
+}
+
+static void vimc_cap_buf_queue(struct vb2_buffer *vb2_buf)
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb2_buf->vb2_queue);
+	struct vimc_cap_buffer *buf = container_of(vb2_buf,
+						   struct vimc_cap_buffer,
+						   vb2.vb2_buf);
+
+	spin_lock(&vcap->qlock);
+	list_add_tail(&buf->list, &vcap->buf_list);
+	spin_unlock(&vcap->qlock);
+}
+
+static int vimc_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
+				unsigned int *nplanes, unsigned int sizes[],
+				struct device *alloc_devs[])
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
+
+	if (*nplanes)
+		return sizes[0] < vcap->format.sizeimage ? -EINVAL : 0;
+	/* We don't support multiplanes for now */
+	*nplanes = 1;
+	sizes[0] = vcap->format.sizeimage;
+
+	return 0;
+}
+
+/*
+ * Prepare the buffer for queueing to the DMA engine: check and set the
+ * payload size.
+ */
+static int vimc_cap_buffer_prepare(struct vb2_buffer *vb)
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb->vb2_queue);
+	unsigned long size = vcap->format.sizeimage;
+
+	if (vb2_plane_size(vb, 0) < size) {
+		dev_err(vcap->dev, "buffer too small (%lu < %lu)\n",
+			vb2_plane_size(vb, 0), size);
+		return -EINVAL;
+	}
+
+	vb2_set_plane_payload(vb, 0, size);
+	return 0;
+}
+
+static const struct vb2_ops vimc_cap_qops = {
+	.start_streaming	= vimc_cap_start_streaming,
+	.stop_streaming		= vimc_cap_stop_streaming,
+	.buf_queue		= vimc_cap_buf_queue,
+	.queue_setup		= vimc_cap_queue_setup,
+	.buf_prepare		= vimc_cap_buffer_prepare,
+	/*
+	 * Since q->lock is set we can use the standard
+	 * vb2_ops_wait_prepare/finish helper functions.
+	 */
+	.wait_prepare		= vb2_ops_wait_prepare,
+	.wait_finish		= vb2_ops_wait_finish,
+};
+
+/* NOTE: this function is a copy of v4l2_subdev_link_validate_get_format
+ * maybe the v4l2 function should be public
+ */
+static int vimc_cap_v4l2_subdev_link_validate_get_format(struct media_pad *pad,
+						struct v4l2_subdev_format *fmt)
+{
+	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity);
+
+	fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	fmt->pad = pad->index;
+	return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt);
+}
+
+static int vimc_cap_link_validate(struct media_link *link)
+{
+	struct v4l2_subdev_format source_fmt;
+	struct v4l2_pix_format *sink_fmt;
+	struct vimc_cap_device *vcap;
+	const struct vimc_pix_map *vpix;
+	int ret;
+
+	/* Retrieve the video capture device */
+	vcap = container_of(link->sink->entity,
+			    struct vimc_cap_device, vdev.entity);
+
+	/* If the connected node is not a subdevice type
+	 * then it's a raw node from vimc-core, ignore the link for now
+	 * TODO: remove this when there are no more raw nodes in the
+	 * core and return error instead
+	 */
+	if (!is_media_entity_v4l2_subdev(link->source->entity))
+		return 0;
+
+	/* Get the the format of the video device */
+	sink_fmt = &vcap->format;
+
+	/* Get the the format of the subdev */
+	ret = vimc_cap_v4l2_subdev_link_validate_get_format(link->source,
+							    &source_fmt);
+	if (ret)
+		return ret;
+
+	dev_dbg(vcap->dev,
+		"cap: %s: link validate formats src:%dx%d %d sink:%dx%d %d\n",
+		vcap->vdev.name,
+		source_fmt.format.width, source_fmt.format.height,
+		source_fmt.format.code,
+		sink_fmt->width, sink_fmt->height,
+		sink_fmt->pixelformat);
+
+	/* Validate the format */
+
+	vpix = vimc_pix_map_by_pixelformat(sink_fmt->pixelformat);
+	if (!vpix)
+		return -EINVAL;
+
+	/* The width, height and code must match. */
+	if (source_fmt.format.width != sink_fmt->width
+	    || source_fmt.format.height != sink_fmt->height
+	    || vpix->code != source_fmt.format.code)
+		return -EINVAL;
+
+	/* The field order must match, or the sink field order must be NONE
+	 * to support interlaced hardware connected to bridges that support
+	 * progressive formats only.
+	 */
+	if (source_fmt.format.field != sink_fmt->field &&
+	    sink_fmt->field != V4L2_FIELD_NONE)
+		return -EINVAL;
+
+	return 0;
+}
+
+static const struct media_entity_operations vimc_cap_mops = {
+	.link_validate		= vimc_cap_link_validate,
+};
+
+static void vimc_cap_destroy(struct vimc_ent_device *ved)
+{
+	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
+						    ved);
+
+	vb2_queue_release(&vcap->queue);
+	media_entity_cleanup(ved->ent);
+	video_unregister_device(&vcap->vdev);
+	vimc_pads_cleanup(vcap->ved.pads);
+	kfree(vcap);
+}
+
+static void vimc_cap_process_frame(struct vimc_ent_device *ved,
+				   struct media_pad *sink, const void *frame)
+{
+	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
+						    ved);
+	struct vimc_cap_buffer *vimc_buf;
+	void *vbuf;
+
+	/* If the stream in this node is not active, just return */
+	mutex_lock(&vcap->lock);
+	if (!vb2_is_busy(&vcap->queue)) {
+		mutex_unlock(&vcap->lock);
+		return;
+	}
+	mutex_unlock(&vcap->lock);
+
+	spin_lock(&vcap->qlock);
+
+	/* Get the first entry of the list */
+	vimc_buf = list_first_entry_or_null(&vcap->buf_list,
+					    typeof(*vimc_buf), list);
+	if (!vimc_buf) {
+		spin_unlock(&vcap->qlock);
+		return;
+	}
+
+	/* Remove this entry from the list */
+	list_del(&vimc_buf->list);
+
+	spin_unlock(&vcap->qlock);
+
+	/* Fill the buffer */
+	vimc_buf->vb2.vb2_buf.timestamp = ktime_get_ns();
+	vimc_buf->vb2.sequence = vcap->sequence++;
+	vimc_buf->vb2.field = vcap->format.field;
+
+	vbuf = vb2_plane_vaddr(&vimc_buf->vb2.vb2_buf, 0);
+
+	memcpy(vbuf, frame, vcap->format.sizeimage);
+
+	/* Set it as ready */
+	vb2_set_plane_payload(&vimc_buf->vb2.vb2_buf, 0,
+			      vcap->format.sizeimage);
+	vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag)
+{
+	int ret;
+	struct vb2_queue *q;
+	struct video_device *vdev;
+	struct vimc_cap_device *vcap;
+	const struct vimc_pix_map *vpix;
+
+	/* Check entity configuration params
+	 * NOTE: we only support a single sink pad
+	 */
+	if (!name || num_pads != 1 || !pads_flag ||
+	    !(pads_flag[0] & MEDIA_PAD_FL_SINK))
+		return ERR_PTR(-EINVAL);
+
+	/* Allocate the vimc_cap_device struct */
+	vcap = kzalloc(sizeof(*vcap), GFP_KERNEL);
+	if (!vcap)
+		return ERR_PTR(-ENOMEM);
+
+	/* Link the vimc_cap_device struct with v4l2 and dev parent */
+	vcap->v4l2_dev = v4l2_dev;
+	vcap->dev = v4l2_dev->dev;
+
+	/* Allocate the pads */
+	vcap->ved.pads = vimc_pads_init(num_pads, pads_flag);
+	if (IS_ERR(vcap->ved.pads)) {
+		ret = PTR_ERR(vcap->ved.pads);
+		goto err_free_vcap;
+	}
+
+	/* Initialize the media entity */
+	vcap->vdev.entity.name = name;
+	vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L;
+	ret = media_entity_pads_init(&vcap->vdev.entity,
+				     num_pads, vcap->ved.pads);
+	if (ret)
+		goto err_clean_pads;
+
+	/* Initialize the lock */
+	mutex_init(&vcap->lock);
+
+	/* Initialize the vb2 queue */
+	q = &vcap->queue;
+	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	q->io_modes = VB2_MMAP | VB2_DMABUF;
+	q->drv_priv = vcap;
+	q->buf_struct_size = sizeof(struct vimc_cap_buffer);
+	q->ops = &vimc_cap_qops;
+	q->mem_ops = &vb2_vmalloc_memops;
+	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	q->min_buffers_needed = 2;
+	q->lock = &vcap->lock;
+
+	ret = vb2_queue_init(q);
+	if (ret) {
+		dev_err(vcap->dev,
+			"vb2 queue init failed (err=%d)\n", ret);
+		goto err_clean_m_ent;
+	}
+
+	/* Initialize buffer list and its lock */
+	INIT_LIST_HEAD(&vcap->buf_list);
+	spin_lock_init(&vcap->qlock);
+
+	/* Set the frame format (this is hardcoded for now) */
+	vcap->format.width = 640;
+	vcap->format.height = 480;
+	vcap->format.pixelformat = V4L2_PIX_FMT_RGB24;
+	vcap->format.field = V4L2_FIELD_NONE;
+	vcap->format.colorspace = V4L2_COLORSPACE_SRGB;
+
+	vpix = vimc_pix_map_by_pixelformat(vcap->format.pixelformat);
+
+	vcap->format.bytesperline = vcap->format.width * vpix->bpp;
+	vcap->format.sizeimage = vcap->format.bytesperline *
+				 vcap->format.height;
+
+	/* Fill the vimc_ent_device struct */
+	vcap->ved.destroy = vimc_cap_destroy;
+	vcap->ved.ent = &vcap->vdev.entity;
+	vcap->ved.process_frame = vimc_cap_process_frame;
+
+	/* Initialize the video_device struct */
+	vdev = &vcap->vdev;
+	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	vdev->entity.ops = &vimc_cap_mops;
+	vdev->release = video_device_release_empty;
+	vdev->fops = &vimc_cap_fops;
+	vdev->ioctl_ops = &vimc_cap_ioctl_ops;
+	vdev->lock = &vcap->lock;
+	vdev->queue = q;
+	vdev->v4l2_dev = vcap->v4l2_dev;
+	vdev->vfl_dir = VFL_DIR_RX;
+	strlcpy(vdev->name, name, sizeof(vdev->name));
+	video_set_drvdata(vdev, vcap);
+
+	/* Register the video_device with the v4l2 and the media framework */
+	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+	if (ret) {
+		dev_err(vcap->dev,
+			"video register failed (err=%d)\n", ret);
+		goto err_release_queue;
+	}
+
+	return &vcap->ved;
+
+err_release_queue:
+	vb2_queue_release(q);
+err_clean_m_ent:
+	media_entity_cleanup(&vcap->vdev.entity);
+err_clean_pads:
+	vimc_pads_cleanup(vcap->ved.pads);
+err_free_vcap:
+	kfree(vcap);
+
+	return ERR_PTR(ret);
+}
diff --git a/drivers/media/platform/vimc/vimc-capture.h b/drivers/media/platform/vimc/vimc-capture.h
new file mode 100644
index 0000000..bcf9fc37
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-capture.h
@@ -0,0 +1,28 @@
+/*
+ * vimc-capture.h Virtual Media Controller Driver
+ *
+ * Copyright (C) 2016 Helen Koike F. <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIMC_CAPTURE_H_
+#define _VIMC_CAPTURE_H_
+
+#include "vimc-core.h"
+
+struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag);
+
+#endif
diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c
new file mode 100644
index 0000000..0a2b91b
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-core.c
@@ -0,0 +1,600 @@
+/*
+ * vimc-core.c Virtual Media Controller Driver
+ *
+ * Copyright (C) 2016 Helen Koike F. <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <media/media-device.h>
+#include <media/v4l2-device.h>
+
+#include "vimc-capture.h"
+#include "vimc-core.h"
+#include "vimc-sensor.h"
+
+#define VIMC_PDEV_NAME "vimc"
+#define VIMC_MDEV_MODEL_NAME "VIMC MDEV"
+
+#define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) {	\
+	.src_ent = src,						\
+	.src_pad = srcpad,					\
+	.sink_ent = sink,					\
+	.sink_pad = sinkpad,					\
+	.flags = link_flags,					\
+}
+
+#define VIMC_PIX_MAP(_code, _bpp, _pixelformat) {	\
+	.code = _code,					\
+	.pixelformat = _pixelformat,			\
+	.bpp = _bpp,					\
+}
+
+struct vimc_device {
+	/* The pipeline configuration
+	 * (filled before calling vimc_device_register)
+	 */
+	const struct vimc_pipeline_config *pipe_cfg;
+
+	/* The Associated media_device parent */
+	struct media_device mdev;
+
+	/* Internal v4l2 parent device*/
+	struct v4l2_device v4l2_dev;
+
+	/* Internal topology */
+	struct vimc_ent_device **ved;
+};
+
+/**
+ * enum vimc_ent_node - Select the functionality of a node in the topology
+ * @VIMC_ENT_NODE_SENSOR:	A node of type SENSOR simulates a camera sensor
+ *				generating internal images in bayer format and
+ *				propagating those images through the pipeline
+ * @VIMC_ENT_NODE_CAPTURE:	A node of type CAPTURE is a v4l2 video_device
+ *				that exposes the received image from the
+ *				pipeline to the user space
+ * @VIMC_ENT_NODE_INPUT:	A node of type INPUT is a v4l2 video_device that
+ *				receives images from the user space and
+ *				propagates them through the pipeline
+ * @VIMC_ENT_NODE_DEBAYER:	A node type DEBAYER expects to receive a frame
+ *				in bayer format converts it to RGB
+ * @VIMC_ENT_NODE_SCALER:	A node of type SCALER scales the received image
+ *				by a given multiplier
+ *
+ * This enum is used in the entity configuration struct to allow the definition
+ * of a custom topology specifying the role of each node on it.
+ */
+enum vimc_ent_node {
+	VIMC_ENT_NODE_SENSOR,
+	VIMC_ENT_NODE_CAPTURE,
+	VIMC_ENT_NODE_INPUT,
+	VIMC_ENT_NODE_DEBAYER,
+	VIMC_ENT_NODE_SCALER,
+};
+
+/* Structure which describes individual configuration for each entity */
+struct vimc_ent_config {
+	const char *name;
+	size_t pads_qty;
+	const unsigned long *pads_flag;
+	enum vimc_ent_node node;
+};
+
+/* Structure which describes links between entities */
+struct vimc_ent_link {
+	unsigned int src_ent;
+	u16 src_pad;
+	unsigned int sink_ent;
+	u16 sink_pad;
+	u32 flags;
+};
+
+/* Structure which describes the whole topology */
+struct vimc_pipeline_config {
+	const struct vimc_ent_config *ents;
+	size_t num_ents;
+	const struct vimc_ent_link *links;
+	size_t num_links;
+};
+
+/* --------------------------------------------------------------------------
+ * Topology Configuration
+ */
+
+static const struct vimc_ent_config ent_config[] = {
+	{
+		.name = "Sensor A",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_SENSOR,
+	},
+	{
+		.name = "Sensor B",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_SENSOR,
+	},
+	{
+		.name = "Debayer A",
+		.pads_qty = 2,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
+						     MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_DEBAYER,
+	},
+	{
+		.name = "Debayer B",
+		.pads_qty = 2,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
+						     MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_DEBAYER,
+	},
+	{
+		.name = "Raw Capture 0",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
+		.node = VIMC_ENT_NODE_CAPTURE,
+	},
+	{
+		.name = "Raw Capture 1",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
+		.node = VIMC_ENT_NODE_CAPTURE,
+	},
+	{
+		.name = "RGB/YUV Input",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_INPUT,
+	},
+	{
+		.name = "Scaler",
+		.pads_qty = 2,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
+						     MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_SCALER,
+	},
+	{
+		.name = "RGB/YUV Capture",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
+		.node = VIMC_ENT_NODE_CAPTURE,
+	},
+};
+
+static const struct vimc_ent_link ent_links[] = {
+	/* Link: Sensor A (Pad 0)->(Pad 0) Debayer A */
+	VIMC_ENT_LINK(0, 0, 2, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Sensor A (Pad 0)->(Pad 0) Raw Capture 0 */
+	VIMC_ENT_LINK(0, 0, 4, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Sensor B (Pad 0)->(Pad 0) Debayer B */
+	VIMC_ENT_LINK(1, 0, 3, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Sensor B (Pad 0)->(Pad 0) Raw Capture 1 */
+	VIMC_ENT_LINK(1, 0, 5, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Debayer A (Pad 1)->(Pad 0) Scaler */
+	VIMC_ENT_LINK(2, 1, 7, 0, MEDIA_LNK_FL_ENABLED),
+	/* Link: Debayer B (Pad 1)->(Pad 0) Scaler */
+	VIMC_ENT_LINK(3, 1, 7, 0, 0),
+	/* Link: RGB/YUV Input (Pad 0)->(Pad 0) Scaler */
+	VIMC_ENT_LINK(6, 0, 7, 0, 0),
+	/* Link: Scaler (Pad 1)->(Pad 0) RGB/YUV Capture */
+	VIMC_ENT_LINK(7, 1, 8, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+};
+
+static const struct vimc_pipeline_config pipe_cfg = {
+	.ents		= ent_config,
+	.num_ents	= ARRAY_SIZE(ent_config),
+	.links		= ent_links,
+	.num_links	= ARRAY_SIZE(ent_links)
+};
+
+/* -------------------------------------------------------------------------- */
+
+static void vimc_dev_release(struct device *dev)
+{}
+
+static struct platform_device vimc_pdev = {
+	.name		= VIMC_PDEV_NAME,
+	.dev.release	= vimc_dev_release,
+};
+
+const struct vimc_pix_map vimc_pix_map_list[] = {
+	/* TODO: add all missing formats */
+
+	/* RGB formats */
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_BGR888_1X24, 3, V4L2_PIX_FMT_BGR24),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_RGB888_1X24, 3, V4L2_PIX_FMT_RGB24),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_ARGB8888_1X32, 4, V4L2_PIX_FMT_ARGB32),
+
+	/* Bayer formats */
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR8_1X8, 1, V4L2_PIX_FMT_SBGGR8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG8_1X8, 1, V4L2_PIX_FMT_SGBRG8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG8_1X8, 1, V4L2_PIX_FMT_SGRBG8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB8_1X8, 1, V4L2_PIX_FMT_SRGGB8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR10_1X10, 2, V4L2_PIX_FMT_SBGGR10),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG10_1X10, 2, V4L2_PIX_FMT_SGBRG10),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG10_1X10, 2, V4L2_PIX_FMT_SGRBG10),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB10_1X10, 2, V4L2_PIX_FMT_SRGGB10),
+	/* 10bit raw bayer a-law compressed to 8 bits */
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8, 1, V4L2_PIX_FMT_SBGGR10ALAW8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8, 1, V4L2_PIX_FMT_SGBRG10ALAW8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8, 1, V4L2_PIX_FMT_SGRBG10ALAW8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8, 1, V4L2_PIX_FMT_SRGGB10ALAW8),
+	/* 10bit raw bayer DPCM compressed to 8 bits */
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, 1, V4L2_PIX_FMT_SBGGR10DPCM8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, 1, V4L2_PIX_FMT_SGBRG10DPCM8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, 1, V4L2_PIX_FMT_SGRBG10DPCM8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, 1, V4L2_PIX_FMT_SRGGB10DPCM8),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR12_1X12, 2, V4L2_PIX_FMT_SBGGR12),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG12_1X12, 2, V4L2_PIX_FMT_SGBRG12),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG12_1X12, 2, V4L2_PIX_FMT_SGRBG12),
+	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB12_1X12, 2, V4L2_PIX_FMT_SRGGB12),
+
+	/* End */
+	{0, 0, 0}
+};
+
+const struct vimc_pix_map *vimc_pix_map_by_code(u32 code)
+{
+	unsigned int i;
+
+	for (i = 0; vimc_pix_map_list[i].bpp; i++) {
+		if (vimc_pix_map_list[i].code == code)
+			return &vimc_pix_map_list[i];
+	}
+	return NULL;
+}
+
+const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat)
+{
+	unsigned int i;
+
+	for (i = 0; vimc_pix_map_list[i].bpp; i++) {
+		if (vimc_pix_map_list[i].pixelformat == pixelformat)
+			return &vimc_pix_map_list[i];
+	}
+	return NULL;
+}
+
+int vimc_propagate_frame(struct device *dev,
+			 struct media_pad *src, const void *frame)
+{
+	struct media_link *link;
+	struct vimc_device *vimc = dev_get_drvdata(dev);
+
+	if (!(src->flags & MEDIA_PAD_FL_SOURCE))
+		return -EINVAL;
+
+	/* Send this frame to all sink pads that are direct linked */
+	list_for_each_entry(link, &src->entity->links, list) {
+		if (link->source == src &&
+		    (link->flags & MEDIA_LNK_FL_ENABLED)) {
+			struct vimc_ent_device *ved;
+
+			ved = vimc->ved[link->sink->entity->use_count];
+			if (ved && ved->process_frame)
+				ved->process_frame(ved, link->sink, frame);
+		}
+	}
+
+	return 0;
+}
+
+static void vimc_device_unregister(struct vimc_device *vimc)
+{
+	unsigned int i;
+
+	/* Cleanup (only initialized) entities */
+	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
+		if (vimc->ved[i] && vimc->ved[i]->destroy)
+			vimc->ved[i]->destroy(vimc->ved[i]);
+
+		vimc->ved[i] = NULL;
+	}
+	v4l2_device_unregister(&vimc->v4l2_dev);
+
+	media_device_unregister(&vimc->mdev);
+	media_device_cleanup(&vimc->mdev);
+}
+
+/* Helper function to allocate and initialize pads */
+struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag)
+{
+	unsigned int i;
+	struct media_pad *pads;
+
+	/* Allocate memory for the pads */
+	pads = kcalloc(num_pads, sizeof(*pads), GFP_KERNEL);
+	if (!pads)
+		return ERR_PTR(-ENOMEM);
+
+	/* Initialize the pads */
+	for (i = 0; i < num_pads; i++) {
+		pads[i].index = i;
+		pads[i].flags = pads_flag[i];
+	}
+
+	return pads;
+}
+
+/* TODO: remove this function when all the
+ * entities specific code are implemented
+ */
+static void vimc_raw_destroy(struct vimc_ent_device *ved)
+{
+	media_device_unregister_entity(ved->ent);
+
+	media_entity_cleanup(ved->ent);
+
+	vimc_pads_cleanup(ved->pads);
+
+	kfree(ved->ent);
+
+	kfree(ved);
+}
+
+/* TODO: remove this function when all the
+ * entities specific code are implemented
+ */
+static struct vimc_ent_device *vimc_raw_create(struct v4l2_device *v4l2_dev,
+					       const char *const name,
+					       u16 num_pads,
+					       const unsigned long *pads_flag)
+{
+	int ret;
+	struct vimc_ent_device *ved;
+
+	/* Allocate the main ved struct */
+	ved = kzalloc(sizeof(*ved), GFP_KERNEL);
+	if (!ved)
+		return ERR_PTR(-ENOMEM);
+
+	/* Allocate the media entity */
+	ved->ent = kzalloc(sizeof(*ved->ent), GFP_KERNEL);
+	if (!ved->ent) {
+		ret = -ENOMEM;
+		goto err_free_ved;
+	}
+
+	/* Allocate the pads */
+	ved->pads = vimc_pads_init(num_pads, pads_flag);
+	if (IS_ERR(ved->pads)) {
+		ret = PTR_ERR(ved->pads);
+		goto err_free_ent;
+	}
+
+	/* Initialize the media entity */
+	ved->ent->name = name;
+	ved->ent->function = MEDIA_ENT_F_IO_V4L;
+	ret = media_entity_pads_init(ved->ent, num_pads, ved->pads);
+	if (ret)
+		goto err_cleanup_pads;
+
+	/* Register the media entity */
+	ret = media_device_register_entity(v4l2_dev->mdev, ved->ent);
+	if (ret)
+		goto err_cleanup_entity;
+
+	/* Fill out the destroy function and return */
+	ved->destroy = vimc_raw_destroy;
+	return ved;
+
+err_cleanup_entity:
+	media_entity_cleanup(ved->ent);
+err_cleanup_pads:
+	vimc_pads_cleanup(ved->pads);
+err_free_ent:
+	kfree(ved->ent);
+err_free_ved:
+	kfree(ved);
+
+	return ERR_PTR(ret);
+}
+
+static int vimc_device_register(struct vimc_device *vimc)
+{
+	unsigned int i;
+	int ret = 0;
+
+	/* Allocate memory for the vimc_ent_devices pointers */
+	vimc->ved = devm_kcalloc(vimc->mdev.dev, vimc->pipe_cfg->num_ents,
+				 sizeof(*vimc->ved), GFP_KERNEL);
+	if (!vimc->ved)
+		return -ENOMEM;
+
+	/* Register the media device */
+	ret = media_device_register(&vimc->mdev);
+	if (ret) {
+		dev_err(vimc->mdev.dev,
+			"media device register failed (err=%d)\n", ret);
+		return ret;
+	}
+
+	/* Link the media device within the v4l2_device */
+	vimc->v4l2_dev.mdev = &vimc->mdev;
+
+	/* Register the v4l2 struct */
+	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);
+		return ret;
+	}
+
+	/* Initialize entities */
+	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
+		struct vimc_ent_device *(*create_func)(struct v4l2_device *,
+						       const char *const,
+						       u16,
+						       const unsigned long *);
+
+		/* Register the specific node */
+		switch (vimc->pipe_cfg->ents[i].node) {
+		case VIMC_ENT_NODE_SENSOR:
+			create_func = vimc_sen_create;
+			break;
+
+		case VIMC_ENT_NODE_CAPTURE:
+			create_func = vimc_cap_create;
+			break;
+
+		/* TODO: Instantiate the specific topology node */
+		case VIMC_ENT_NODE_INPUT:
+		case VIMC_ENT_NODE_DEBAYER:
+		case VIMC_ENT_NODE_SCALER:
+		default:
+			/* TODO: remove this when all the entities specific
+			 * code are implemented
+			 */
+			create_func = vimc_raw_create;
+			break;
+		}
+
+		vimc->ved[i] = create_func(&vimc->v4l2_dev,
+					   vimc->pipe_cfg->ents[i].name,
+					   vimc->pipe_cfg->ents[i].pads_qty,
+					   vimc->pipe_cfg->ents[i].pads_flag);
+		if (IS_ERR(vimc->ved[i])) {
+			ret = PTR_ERR(vimc->ved[i]);
+			vimc->ved[i] = NULL;
+			goto err;
+		}
+
+		/* Set use_count to keep track of the ved structure */
+		vimc->ved[i]->ent->use_count = i;
+	}
+
+	/* 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];
+
+		ret = media_create_pad_link(vimc->ved[link->src_ent]->ent,
+					    link->src_pad,
+					    vimc->ved[link->sink_ent]->ent,
+					    link->sink_pad,
+					    link->flags);
+		if (ret)
+			goto err;
+	}
+
+	/* 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);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	/* Destroy the so far created topology */
+	vimc_device_unregister(vimc);
+
+	return ret;
+}
+
+static int vimc_probe(struct platform_device *pdev)
+{
+	struct vimc_device *vimc;
+	int ret;
+
+	/* Prepare the vimc topology structure */
+
+	/* Allocate memory for the vimc structure */
+	vimc = devm_kzalloc(&pdev->dev, sizeof(*vimc), GFP_KERNEL);
+	if (!vimc)
+		return -ENOMEM;
+
+	/* Set the pipeline configuration struct */
+	vimc->pipe_cfg = &pipe_cfg;
+
+	/* Initialize media device */
+	strlcpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME,
+		sizeof(vimc->mdev.model));
+	vimc->mdev.dev = &pdev->dev;
+	media_device_init(&vimc->mdev);
+
+	/* Create vimc topology */
+	ret = vimc_device_register(vimc);
+	if (ret) {
+		dev_err(vimc->mdev.dev,
+			"vimc device registration failed (err=%d)\n", ret);
+		return ret;
+	}
+
+	/* Link the topology object with the platform device object */
+	platform_set_drvdata(pdev, vimc);
+
+	return 0;
+}
+
+static int vimc_remove(struct platform_device *pdev)
+{
+	struct vimc_device *vimc;
+
+	/* Get the topology object linked with the platform device object */
+	vimc = platform_get_drvdata(pdev);
+
+	/* Destroy all the topology */
+	vimc_device_unregister(vimc);
+
+	return 0;
+}
+
+static struct platform_driver vimc_pdrv = {
+	.probe		= vimc_probe,
+	.remove		= vimc_remove,
+	.driver		= {
+		.name	= VIMC_PDEV_NAME,
+	},
+};
+
+static int __init vimc_init(void)
+{
+	int ret;
+
+	ret = platform_device_register(&vimc_pdev);
+	if (ret) {
+		dev_err(&vimc_pdev.dev,
+			"platform device registration failed (err=%d)\n", ret);
+		return ret;
+	}
+
+	ret = platform_driver_register(&vimc_pdrv);
+	if (ret) {
+		dev_err(&vimc_pdev.dev,
+			"platform driver registration failed (err=%d)\n", ret);
+
+		platform_device_unregister(&vimc_pdev);
+	}
+
+	return ret;
+}
+
+static void __exit vimc_exit(void)
+{
+	platform_driver_unregister(&vimc_pdrv);
+
+	platform_device_unregister(&vimc_pdev);
+}
+
+module_init(vimc_init);
+module_exit(vimc_exit);
+
+MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC)");
+MODULE_AUTHOR("Helen Fornazier <helen.fornazier@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/vimc/vimc-core.h b/drivers/media/platform/vimc/vimc-core.h
new file mode 100644
index 0000000..bd7abff
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-core.h
@@ -0,0 +1,57 @@
+/*
+ * vimc-core.h Virtual Media Controller Driver
+ *
+ * Copyright (C) 2016 Helen Koike F. <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIMC_CORE_H_
+#define _VIMC_CORE_H_
+
+#include <linux/slab.h>
+#include <media/v4l2-device.h>
+
+/* Struct which matches the MEDIA_BUS_FMT_ codes with the corresponding
+ * V4L2_PIX_FMT_ fourcc pixelformat and its bytes per pixel (bpp)
+ */
+struct vimc_pix_map {
+	unsigned int code;
+	unsigned int bpp;
+	u32 pixelformat;
+};
+extern const struct vimc_pix_map vimc_pix_map_list[];
+
+struct vimc_ent_device {
+	struct media_entity *ent;
+	struct media_pad *pads;
+	void (*destroy)(struct vimc_ent_device *);
+	void (*process_frame)(struct vimc_ent_device *ved,
+			      struct media_pad *sink, const void *frame);
+};
+
+int vimc_propagate_frame(struct device *dev,
+			 struct media_pad *src, const void *frame);
+
+/* Helper functions to allocate/initialize pads and free them */
+struct media_pad *vimc_pads_init(u16 num_pads,
+				 const unsigned long *pads_flag);
+static inline void vimc_pads_cleanup(struct media_pad *pads)
+{
+	kfree(pads);
+}
+
+const struct vimc_pix_map *vimc_pix_map_by_code(u32 code);
+
+const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat);
+
+#endif
diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c
new file mode 100644
index 0000000..174e5dc
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-sensor.c
@@ -0,0 +1,279 @@
+/*
+ * vimc-sensor.c Virtual Media Controller Driver
+ *
+ * Copyright (C) 2016 Helen Koike F. <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/freezer.h>
+#include <linux/kthread.h>
+#include <linux/vmalloc.h>
+#include <linux/v4l2-mediabus.h>
+#include <media/v4l2-subdev.h>
+
+#include "vimc-sensor.h"
+
+struct vimc_sen_device {
+	struct vimc_ent_device ved;
+	struct v4l2_subdev sd;
+	struct v4l2_device *v4l2_dev;
+	struct device *dev;
+	struct task_struct *kthread_sen;
+	u8 *frame;
+	/* The active format */
+	struct v4l2_mbus_framefmt mbus_format;
+	int frame_size;
+};
+
+static int vimc_sen_enum_mbus_code(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_pad_config *cfg,
+				   struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd);
+
+	/* Check if it is a valid pad */
+	if (code->pad >= vsen->sd.entity.num_pads)
+		return -EINVAL;
+
+	code->code = vsen->mbus_format.code;
+
+	return 0;
+}
+
+static int vimc_sen_enum_frame_size(struct v4l2_subdev *sd,
+				    struct v4l2_subdev_pad_config *cfg,
+				    struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd);
+
+	/* Check if it is a valid pad */
+	if (fse->pad >= vsen->sd.entity.num_pads)
+		return -EINVAL;
+
+	/* TODO: Add support to other formats */
+	if (fse->index)
+		return -EINVAL;
+
+	/* TODO: Add support for other codes */
+	if (fse->code != vsen->mbus_format.code)
+		return -EINVAL;
+
+	fse->min_width = vsen->mbus_format.width;
+	fse->max_width = vsen->mbus_format.width;
+	fse->min_height = vsen->mbus_format.height;
+	fse->max_height = vsen->mbus_format.height;
+
+	return 0;
+}
+
+static int vimc_sen_get_fmt(struct v4l2_subdev *sd,
+			    struct v4l2_subdev_pad_config *cfg,
+			    struct v4l2_subdev_format *format)
+{
+	struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd);
+
+	format->format = vsen->mbus_format;
+
+	return 0;
+}
+
+static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = {
+	.enum_mbus_code		= vimc_sen_enum_mbus_code,
+	.enum_frame_size	= vimc_sen_enum_frame_size,
+	.get_fmt		= vimc_sen_get_fmt,
+	/* TODO: Add support to other formats */
+	.set_fmt		= vimc_sen_get_fmt,
+};
+
+/* media operations */
+static const struct media_entity_operations vimc_sen_mops = {
+	.link_validate = v4l2_subdev_link_validate,
+};
+
+static int vimc_thread_sen(void *data)
+{
+	unsigned int i;
+	struct vimc_sen_device *vsen = data;
+
+	set_freezable();
+
+	for (;;) {
+		try_to_freeze();
+		if (kthread_should_stop())
+			break;
+
+		memset(vsen->frame, 100, vsen->frame_size);
+
+		/* Send the frame to all source pads */
+		for (i = 0; i < vsen->sd.entity.num_pads; i++)
+			vimc_propagate_frame(vsen->dev,
+					     &vsen->sd.entity.pads[i],
+					     vsen->frame);
+
+		/* 60 frames per second */
+		schedule_timeout_interruptible(HZ/60);
+	}
+
+	return 0;
+}
+
+static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	int ret;
+	struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd);
+
+	if (enable) {
+		const struct vimc_pix_map *vpix;
+
+		if (vsen->kthread_sen)
+			return -EINVAL;
+
+		/* Calculate the frame size */
+		vpix = vimc_pix_map_by_code(vsen->mbus_format.code);
+		vsen->frame_size = vsen->mbus_format.width * vpix->bpp *
+				   vsen->mbus_format.height;
+
+		/* Allocate the frame buffer. Use vmalloc to be able to
+		 * allocate a large amount of memory
+		 */
+		vsen->frame = vmalloc(vsen->frame_size);
+		if (!vsen->frame)
+			return -ENOMEM;
+
+		/* Initialize the image generator thread */
+		vsen->kthread_sen = kthread_run(vimc_thread_sen, vsen,
+						"%s-sen", vsen->v4l2_dev->name);
+		if (IS_ERR(vsen->kthread_sen)) {
+			v4l2_err(vsen->v4l2_dev, "kernel_thread() failed\n");
+			vfree(vsen->frame);
+			vsen->frame = NULL;
+			return PTR_ERR(vsen->kthread_sen);
+		}
+	} else {
+		if (!vsen->kthread_sen)
+			return -EINVAL;
+
+		/* Stop image generator */
+		ret = kthread_stop(vsen->kthread_sen);
+		vsen->kthread_sen = NULL;
+
+		vfree(vsen->frame);
+		vsen->frame = NULL;
+		return ret;
+	}
+
+	return 0;
+}
+
+struct v4l2_subdev_video_ops vimc_sen_video_ops = {
+	.s_stream = vimc_sen_s_stream,
+};
+
+static const struct v4l2_subdev_ops vimc_sen_ops = {
+	.pad = &vimc_sen_pad_ops,
+	.video = &vimc_sen_video_ops,
+};
+
+static void vimc_sen_destroy(struct vimc_ent_device *ved)
+{
+	struct vimc_sen_device *vsen = container_of(ved,
+						struct vimc_sen_device, ved);
+
+	media_entity_cleanup(ved->ent);
+	v4l2_device_unregister_subdev(&vsen->sd);
+	kfree(vsen);
+}
+
+struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag)
+{
+	int ret;
+	unsigned int i;
+	struct vimc_sen_device *vsen;
+
+	if (!name || (num_pads && !pads_flag))
+		return ERR_PTR(-EINVAL);
+	/* check if all pads are sources */
+	for (i = 0; i < num_pads; i++)
+		if (!(pads_flag[i] & MEDIA_PAD_FL_SOURCE))
+			return ERR_PTR(-EINVAL);
+
+	/* Allocate the vsen struct */
+	vsen = kzalloc(sizeof(*vsen), GFP_KERNEL);
+	if (!vsen)
+		return ERR_PTR(-ENOMEM);
+
+	/* Link the vimc_sen_device struct with the v4l2 parent */
+	vsen->v4l2_dev = v4l2_dev;
+	/* Link the vimc_sen_device struct with the dev parent */
+	vsen->dev = v4l2_dev->dev;
+
+	/* Allocate the pads */
+	vsen->ved.pads = vimc_pads_init(num_pads, pads_flag);
+	if (IS_ERR(vsen->ved.pads)) {
+		ret = PTR_ERR(vsen->ved.pads);
+		goto err_free_vsen;
+	}
+
+	/* Initialize the media entity */
+	vsen->sd.entity.name = name;
+	ret = media_entity_pads_init(&vsen->sd.entity,
+				     num_pads, vsen->ved.pads);
+	if (ret)
+		goto err_clean_pads;
+
+	/* Set the active frame format (this is hardcoded for now) */
+	vsen->mbus_format.width = 640;
+	vsen->mbus_format.height = 480;
+	vsen->mbus_format.code = MEDIA_BUS_FMT_RGB888_1X24;
+	vsen->mbus_format.field = V4L2_FIELD_NONE;
+	vsen->mbus_format.colorspace = V4L2_COLORSPACE_SRGB;
+	vsen->mbus_format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
+	vsen->mbus_format.xfer_func = V4L2_XFER_FUNC_SRGB;
+
+	/* Fill the vimc_ent_device struct */
+	vsen->ved.destroy = vimc_sen_destroy;
+	vsen->ved.ent = &vsen->sd.entity;
+
+	/* Initialize the subdev */
+	v4l2_subdev_init(&vsen->sd, &vimc_sen_ops);
+	vsen->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+	vsen->sd.entity.ops = &vimc_sen_mops;
+	vsen->sd.owner = THIS_MODULE;
+	strlcpy(vsen->sd.name, name, sizeof(vsen->sd.name));
+	v4l2_set_subdevdata(&vsen->sd, vsen);
+
+	/* Expose this subdev to user space */
+	vsen->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	/* Register the subdev with the v4l2 and the media framework */
+	ret = v4l2_device_register_subdev(vsen->v4l2_dev, &vsen->sd);
+	if (ret) {
+		dev_err(vsen->dev,
+			"subdev register failed (err=%d)\n", ret);
+		goto err_clean_m_ent;
+	}
+
+	return &vsen->ved;
+
+err_clean_m_ent:
+	media_entity_cleanup(&vsen->sd.entity);
+err_clean_pads:
+	vimc_pads_cleanup(vsen->ved.pads);
+err_free_vsen:
+	kfree(vsen);
+
+	return ERR_PTR(ret);
+}
diff --git a/drivers/media/platform/vimc/vimc-sensor.h b/drivers/media/platform/vimc/vimc-sensor.h
new file mode 100644
index 0000000..ffeaf6c
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-sensor.h
@@ -0,0 +1,28 @@
+/*
+ * vimc-sensor.h Virtual Media Controller Driver
+ *
+ * Copyright (C) 2016 Helen Koike F. <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIMC_SENSOR_H_
+#define _VIMC_SENSOR_H_
+
+#include "vimc-core.h"
+
+struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag);
+
+#endif
-- 
2.7.4


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

* Re: [PATCH v5] [media] vimc: Virtual Media Controller core, capture and sensor
  2016-08-22 10:57     ` Hans Verkuil
  2016-09-04 20:02       ` [PATCH v6] " Helen Koike
@ 2016-09-04 20:05       ` Helen Koike
  2016-09-05  9:01         ` Hans Verkuil
  1 sibling, 1 reply; 41+ messages in thread
From: Helen Koike @ 2016-09-04 20:05 UTC (permalink / raw)
  To: Hans Verkuil, linux-media, laurent.pinchart, jgebben, mchehab
  Cc: Helen Fornazier, Helen Koike

Hi Hans,

Thank you for your review.

On 2016-08-22 07:57 AM, Hans Verkuil wrote:
> Hi Helen,
>
> A few small code comments are below.
>
> Note that if I try to capture I see these two messages in the kernel log:
>
> [588197.368145] vimc vimc.0: Entity type for entity Sensor A was not initialized!
> [588197.368169] vimc vimc.0: Entity type for entity Sensor B was not initialized!


I correct this, I am sending it in v6.


>
> I also can't capture anything: v4l2-ctl --stream-mmap just sits there, waiting for
> frames, I guess.
>
> I'm not sure if that has to do with the two warnings above.


This is weird, v4l2-ctl --stream-mmap works for me even with those 
messages above, could you try again with the v6 please?


>
> I am assuming that the initial pipeline is correct and that you should be able
> to start streaming. If not, then attempting to start streaming should return an
> error.
>
> On 08/18/2016 12:09 AM, Helen Koike wrote:
>> From: Helen Fornazier <helen.fornazier@gmail.com>
>>
>> First version of the Virtual Media Controller.
>> Add a simple version of the core of the driver, the capture and
>> sensor nodes in the topology, generating a grey image in a hardcoded
>> format.
>>
>> Signed-off-by: Helen Koike <helen.koike@collabora.com>
>
> <snip>
>
>> +static int vimc_cap_querycap(struct file *file, void *priv,
>> +			     struct v4l2_capability *cap)
>> +{
>> +	struct vimc_cap_device *vcap = video_drvdata(file);
>> +
>> +	strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
>> +	strlcpy(cap->card, KBUILD_MODNAME, sizeof(cap->card));
>> +	snprintf(cap->bus_info, sizeof(cap->bus_info),
>> +		 "platform:%s", vcap->v4l2_dev->name);
>> +
>> +	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
>
> This line should be moved to vimc_cap_create:
>
> 	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
>
> This is new. The v4l2 core will fill in the querycap capabilities for you
> based on vdev->device_caps.
>
>> +
>> +	return 0;
>> +}
>
> <snip>
>
>> +static int vimc_device_register(struct vimc_device *vimc)
>> +{
>> +	unsigned int i;
>> +	int ret = 0;
>> +
>> +	/* Allocate memory for the vimc_ent_devices pointers */
>> +	vimc->ved = devm_kcalloc(vimc->mdev.dev, vimc->pipe_cfg->num_ents,
>> +				 sizeof(*vimc->ved), GFP_KERNEL);
>> +	if (!vimc->ved)
>> +		return -ENOMEM;
>> +
>> +	/* Register the media device */
>> +	ret = media_device_register(&vimc->mdev);
>> +	if (ret) {
>> +		dev_err(vimc->mdev.dev,
>> +			"media device register failed (err=%d)\n", ret);
>> +		return ret;
>> +	}
>> +
>> +	/* Link the media device within the v4l2_device */
>> +	vimc->v4l2_dev.mdev = &vimc->mdev;
>> +
>> +	/* Register the v4l2 struct */
>> +	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);
>> +		return ret;
>> +	}
>> +
>> +	/* Initialize entities */
>> +	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
>> +		struct vimc_ent_device *(*create_func)(struct v4l2_device *,
>> +						       const char *const,
>> +						       u16,
>> +						       const unsigned long *);
>> +
>> +		/* Register the specific node */
>> +		switch (vimc->pipe_cfg->ents[i].node) {
>> +		case VIMC_ENT_NODE_SENSOR:
>> +			create_func = vimc_sen_create;
>> +			break;
>> +
>> +		case VIMC_ENT_NODE_CAPTURE:
>> +			create_func = vimc_cap_create;
>> +			break;
>> +
>> +		/* TODO: Instantiate the specific topology node */
>> +		case VIMC_ENT_NODE_INPUT:
>> +		case VIMC_ENT_NODE_DEBAYER:
>> +		case VIMC_ENT_NODE_SCALER:
>> +		default:
>> +			/* TODO: remove this when all the entities specific
>> +			 * code are implemented
>> +			 */
>> +			create_func = vimc_raw_create;
>> +			break;
>> +		}
>> +
>> +		vimc->ved[i] = create_func(&vimc->v4l2_dev,
>> +					   vimc->pipe_cfg->ents[i].name,
>> +					   vimc->pipe_cfg->ents[i].pads_qty,
>> +					   vimc->pipe_cfg->ents[i].pads_flag);
>> +		if (IS_ERR(vimc->ved[i])) {
>> +			ret = PTR_ERR(vimc->ved[i]);
>> +			vimc->ved[i] = NULL;
>> +			goto err;
>> +		}
>> +
>> +		/* Set use_count to keep track of the ved structure */
>> +		vimc->ved[i]->ent->use_count = i;
>> +	}
>> +
>> +	/* 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];
>> +
>> +		ret = media_create_pad_link(vimc->ved[link->src_ent]->ent,
>> +					    link->src_pad,
>> +					    vimc->ved[link->sink_ent]->ent,
>> +					    link->sink_pad,
>> +					    link->flags);
>> +		if (ret)
>> +			goto err;
>> +	}
>> +
>> +	/* 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);
>> +		goto err;
>> +	}
>> +
>> +	return 0;
>> +
>> +err:
>> +	/* Destroy de so far created topology */
>
> s/de/the/
>
>> +	vimc_device_unregister(vimc);
>> +
>> +	return ret;
>> +}
>
> Regards,
>
> 	Hans
>


Regards,
Helen

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

* Re: [PATCH v5] [media] vimc: Virtual Media Controller core, capture and sensor
  2016-09-04 20:05       ` [PATCH v5] " Helen Koike
@ 2016-09-05  9:01         ` Hans Verkuil
  0 siblings, 0 replies; 41+ messages in thread
From: Hans Verkuil @ 2016-09-05  9:01 UTC (permalink / raw)
  To: Helen Koike, linux-media, laurent.pinchart, jgebben, mchehab
  Cc: Helen Fornazier, Helen Koike

On 09/04/2016 10:05 PM, Helen Koike wrote:
> Hi Hans,
> 
> Thank you for your review.
> 
> On 2016-08-22 07:57 AM, Hans Verkuil wrote:
>> Hi Helen,
>>
>> A few small code comments are below.
>>
>> Note that if I try to capture I see these two messages in the kernel log:
>>
>> [588197.368145] vimc vimc.0: Entity type for entity Sensor A was not initialized!
>> [588197.368169] vimc vimc.0: Entity type for entity Sensor B was not initialized!
> 
> 
> I correct this, I am sending it in v6.
> 
> 
>>
>> I also can't capture anything: v4l2-ctl --stream-mmap just sits there, waiting for
>> frames, I guess.
>>
>> I'm not sure if that has to do with the two warnings above.
> 
> 
> This is weird, v4l2-ctl --stream-mmap works for me even with those 
> messages above, could you try again with the v6 please?

Yup, v6 fixed it for me. Not sure what was the cause, but it's now working fine.

Once I have Laurent's Ack I'll take it.

Thanks for all your hard work, I'm sure you expected this to get in sooner, but
better late than never!

	Hans

> 
> 
>>
>> I am assuming that the initial pipeline is correct and that you should be able
>> to start streaming. If not, then attempting to start streaming should return an
>> error.
>>
>> On 08/18/2016 12:09 AM, Helen Koike wrote:
>>> From: Helen Fornazier <helen.fornazier@gmail.com>
>>>
>>> First version of the Virtual Media Controller.
>>> Add a simple version of the core of the driver, the capture and
>>> sensor nodes in the topology, generating a grey image in a hardcoded
>>> format.
>>>
>>> Signed-off-by: Helen Koike <helen.koike@collabora.com>
>>
>> <snip>
>>
>>> +static int vimc_cap_querycap(struct file *file, void *priv,
>>> +			     struct v4l2_capability *cap)
>>> +{
>>> +	struct vimc_cap_device *vcap = video_drvdata(file);
>>> +
>>> +	strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
>>> +	strlcpy(cap->card, KBUILD_MODNAME, sizeof(cap->card));
>>> +	snprintf(cap->bus_info, sizeof(cap->bus_info),
>>> +		 "platform:%s", vcap->v4l2_dev->name);
>>> +
>>> +	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
>>
>> This line should be moved to vimc_cap_create:
>>
>> 	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
>>
>> This is new. The v4l2 core will fill in the querycap capabilities for you
>> based on vdev->device_caps.
>>
>>> +
>>> +	return 0;
>>> +}
>>
>> <snip>
>>
>>> +static int vimc_device_register(struct vimc_device *vimc)
>>> +{
>>> +	unsigned int i;
>>> +	int ret = 0;
>>> +
>>> +	/* Allocate memory for the vimc_ent_devices pointers */
>>> +	vimc->ved = devm_kcalloc(vimc->mdev.dev, vimc->pipe_cfg->num_ents,
>>> +				 sizeof(*vimc->ved), GFP_KERNEL);
>>> +	if (!vimc->ved)
>>> +		return -ENOMEM;
>>> +
>>> +	/* Register the media device */
>>> +	ret = media_device_register(&vimc->mdev);
>>> +	if (ret) {
>>> +		dev_err(vimc->mdev.dev,
>>> +			"media device register failed (err=%d)\n", ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	/* Link the media device within the v4l2_device */
>>> +	vimc->v4l2_dev.mdev = &vimc->mdev;
>>> +
>>> +	/* Register the v4l2 struct */
>>> +	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);
>>> +		return ret;
>>> +	}
>>> +
>>> +	/* Initialize entities */
>>> +	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
>>> +		struct vimc_ent_device *(*create_func)(struct v4l2_device *,
>>> +						       const char *const,
>>> +						       u16,
>>> +						       const unsigned long *);
>>> +
>>> +		/* Register the specific node */
>>> +		switch (vimc->pipe_cfg->ents[i].node) {
>>> +		case VIMC_ENT_NODE_SENSOR:
>>> +			create_func = vimc_sen_create;
>>> +			break;
>>> +
>>> +		case VIMC_ENT_NODE_CAPTURE:
>>> +			create_func = vimc_cap_create;
>>> +			break;
>>> +
>>> +		/* TODO: Instantiate the specific topology node */
>>> +		case VIMC_ENT_NODE_INPUT:
>>> +		case VIMC_ENT_NODE_DEBAYER:
>>> +		case VIMC_ENT_NODE_SCALER:
>>> +		default:
>>> +			/* TODO: remove this when all the entities specific
>>> +			 * code are implemented
>>> +			 */
>>> +			create_func = vimc_raw_create;
>>> +			break;
>>> +		}
>>> +
>>> +		vimc->ved[i] = create_func(&vimc->v4l2_dev,
>>> +					   vimc->pipe_cfg->ents[i].name,
>>> +					   vimc->pipe_cfg->ents[i].pads_qty,
>>> +					   vimc->pipe_cfg->ents[i].pads_flag);
>>> +		if (IS_ERR(vimc->ved[i])) {
>>> +			ret = PTR_ERR(vimc->ved[i]);
>>> +			vimc->ved[i] = NULL;
>>> +			goto err;
>>> +		}
>>> +
>>> +		/* Set use_count to keep track of the ved structure */
>>> +		vimc->ved[i]->ent->use_count = i;
>>> +	}
>>> +
>>> +	/* 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];
>>> +
>>> +		ret = media_create_pad_link(vimc->ved[link->src_ent]->ent,
>>> +					    link->src_pad,
>>> +					    vimc->ved[link->sink_ent]->ent,
>>> +					    link->sink_pad,
>>> +					    link->flags);
>>> +		if (ret)
>>> +			goto err;
>>> +	}
>>> +
>>> +	/* 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);
>>> +		goto err;
>>> +	}
>>> +
>>> +	return 0;
>>> +
>>> +err:
>>> +	/* Destroy de so far created topology */
>>
>> s/de/the/
>>
>>> +	vimc_device_unregister(vimc);
>>> +
>>> +	return ret;
>>> +}
>>
>> Regards,
>>
>> 	Hans
>>
> 
> 
> Regards,
> Helen
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [PATCH v6] [media] vimc: Virtual Media Controller core, capture and sensor
  2016-09-04 20:02       ` [PATCH v6] " Helen Koike
@ 2016-09-06  7:33         ` Hans Verkuil
  2016-09-12 12:53           ` [PATCH] [media] MAINTAINERS: add vimc entry Helen Koike
  2017-01-10 19:54         ` [PATCH v6] [media] vimc: Virtual Media Controller core, capture and sensor Laurent Pinchart
  2017-01-25 13:03         ` [PATCH v6] " Sakari Ailus
  2 siblings, 1 reply; 41+ messages in thread
From: Hans Verkuil @ 2016-09-06  7:33 UTC (permalink / raw)
  To: Helen Koike, linux-media, laurent.pinchart, jgebben, mchehab
  Cc: Helen Fornazier

On 09/04/16 22:02, Helen Koike wrote:
> From: Helen Fornazier <helen.fornazier@gmail.com>
>
> First version of the Virtual Media Controller.
> Add a simple version of the core of the driver, the capture and
> sensor nodes in the topology, generating a grey image in a hardcoded
> format.
>
> Signed-off-by: Helen Koike <helen.koike@collabora.com>
>

One thing is missing: a MAINTAINERS entry. Can you make a separate patch
updating the MAINTAINERS file?

Thanks!

	Hans


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

* [PATCH] [media] MAINTAINERS: add vimc entry
  2016-09-06  7:33         ` Hans Verkuil
@ 2016-09-12 12:53           ` Helen Koike
  0 siblings, 0 replies; 41+ messages in thread
From: Helen Koike @ 2016-09-12 12:53 UTC (permalink / raw)
  To: Hans Verkuil, Helen Koike, linux-media, laurent.pinchart,
	jgebben, mchehab
  Cc: Helen Fornazier

Signed-off-by: Helen Koike <helen.koike@collabora.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 0a16a82..43e0eb4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12540,6 +12540,14 @@ W:	https://linuxtv.org
 S:	Maintained
 F:	drivers/media/platform/vivid/*
 
+VIMC VIRTUAL MEDIA CONTROLLER DRIVER
+M:	Helen Koike <helen.koike@collabora.com>
+L:	linux-media@vger.kernel.org
+T:	git git://linuxtv.org/media_tree.git
+W:	https://linuxtv.org
+S:	Maintained
+F:	drivers/media/platform/vimc/*
+
 VLAN (802.1Q)
 M:	Patrick McHardy <kaber@trash.net>
 L:	netdev@vger.kernel.org
-- 
2.7.4


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

* Re: [PATCH v6] [media] vimc: Virtual Media Controller core, capture and sensor
  2016-09-04 20:02       ` [PATCH v6] " Helen Koike
  2016-09-06  7:33         ` Hans Verkuil
@ 2017-01-10 19:54         ` Laurent Pinchart
  2017-01-11  1:30           ` Helen Koike
  2017-01-25 13:03         ` [PATCH v6] " Sakari Ailus
  2 siblings, 1 reply; 41+ messages in thread
From: Laurent Pinchart @ 2017-01-10 19:54 UTC (permalink / raw)
  To: Helen Koike
  Cc: Hans Verkuil, linux-media, jgebben, mchehab, Helen Fornazier,
	Sakari Ailus

Hi Helen,

(CC'ing Sakari as there's a question specifically for him)

Thank you for the patch, and so sorry for the late review. 

On Sunday 04 Sep 2016 17:02:18 Helen Koike wrote:
> From: Helen Fornazier <helen.fornazier@gmail.com>
> 
> First version of the Virtual Media Controller.
> Add a simple version of the core of the driver, the capture and
> sensor nodes in the topology, generating a grey image in a hardcoded
> format.
> 
> Signed-off-by: Helen Koike <helen.koike@collabora.com>

I've reviewed the whole patch but haven't had time to test it. I've also 
skipped the items marked as TODO or FIXME as they're obviously not ready yet 
:-) Overall this looks good to me, all the issues are minor.

> ---
> 
> Patch based in media/master tree, and available here:
> https://github.com/helen-fornazier/opw-staging/tree/vimc/devel/vpu
> 
> Changes since v5:
> - Fix message "Entity type for entity Sensor A was not initialized!"
>   by initializing the sensor entity.function after the calling
>   v4l2_subded_init
> - populate device_caps in vimc_cap_create instead of in
>   vimc_cap_querycap
> - Fix typo in vimc-core.c s/de/the
> 
> Changes since v4:
> - coding style fixes
> - remove BUG_ON
> - change copyright to 2016
> - depens on VIDEO_V4L2_SUBDEV_API instead of select
> - remove assignement of V4L2_CAP_DEVICE_CAPS
> - s/vimc_cap_g_fmt_vid_cap/vimc_cap_fmt_vid_cap
> - fix vimc_cap_queue_setup declaration type
> - remove wrong buffer size check and add it in vimc_cap_buffer_prepare
> - vimc_cap_create: remove unecessary check if v4l2_dev or v4l2_dev->dev is
> null - vimc_cap_create: only allow a single pad
> - vimc_sen_create: only allow source pads, remove unecessary source pads
> checks in vimc_thread_sen
> 
> Changes since v3: fix rmmod crash and built-in compile
> - Re-order unregister calls in vimc_device_unregister function (remove
> rmmod issue)
> - Call media_device_unregister_entity in vimc_raw_destroy
> - Add depends on VIDEO_DEV && VIDEO_V4L2 and select VIDEOBUF2_VMALLOC
> - Check *nplanes in queue_setup (this remove v4l2-compliance fail)
> - Include <linux/kthread.h> in vimc-sensor.c
> - Move include of <linux/slab.h> from vimc-core.c to vimc-core.h
> - Generate 60 frames per sec instead of 1 in the sensor
> 
> Changes since v2: update with current media master tree
> - Add struct media_pipeline in vimc_cap_device
> - Use vb2_v4l2_buffer instead of vb2_buffer
> - Typos
> - Remove usage of entity->type and use entity->function instead
> - Remove fmt argument from queue setup
> - Use ktime_get_ns instead of v4l2_get_timestamp
> - Iterate over link's list using list_for_each_entry
> - Use media_device_{init, cleanup}
> - Use entity->use_count to keep track of entities instead of the old
> entity->id
> - Replace media_entity_init by media_entity_pads_init
> ---
>  drivers/media/platform/Kconfig             |   2 +
>  drivers/media/platform/Makefile            |   1 +
>  drivers/media/platform/vimc/Kconfig        |   7 +
>  drivers/media/platform/vimc/Makefile       |   3 +
>  drivers/media/platform/vimc/vimc-capture.c | 553 ++++++++++++++++++++++++++
>  drivers/media/platform/vimc/vimc-capture.h |  28 ++
>  drivers/media/platform/vimc/vimc-core.c    | 600 ++++++++++++++++++++++++++
>  drivers/media/platform/vimc/vimc-core.h    |  57 +++
>  drivers/media/platform/vimc/vimc-sensor.c  | 279 ++++++++++++++
>  drivers/media/platform/vimc/vimc-sensor.h  |  28 ++
>  10 files changed, 1558 insertions(+)
>  create mode 100644 drivers/media/platform/vimc/Kconfig
>  create mode 100644 drivers/media/platform/vimc/Makefile
>  create mode 100644 drivers/media/platform/vimc/vimc-capture.c
>  create mode 100644 drivers/media/platform/vimc/vimc-capture.h
>  create mode 100644 drivers/media/platform/vimc/vimc-core.c
>  create mode 100644 drivers/media/platform/vimc/vimc-core.h
>  create mode 100644 drivers/media/platform/vimc/vimc-sensor.c
>  create mode 100644 drivers/media/platform/vimc/vimc-sensor.h

[snip]

> diff --git a/drivers/media/platform/vimc/vimc-capture.c
> b/drivers/media/platform/vimc/vimc-capture.c new file mode 100644
> index 0000000..b7636cf
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-capture.c

[snip]

> +static void vimc_cap_return_all_buffers(struct vimc_cap_device *vcap,
> +					enum vb2_buffer_state state)
> +{
> +	struct vimc_cap_buffer *vbuf, *node;
> +
> +	spin_lock(&vcap->qlock);
> +
> +	list_for_each_entry_safe(vbuf, node, &vcap->buf_list, list) {
> +		vb2_buffer_done(&vbuf->vb2.vb2_buf, state);
> +		list_del(&vbuf->list);

It shouldn't matter given that you protect this with a spinlock, but moving 
the list_del() above makes the code flow follow a safer order.

> +	}
> +
> +	spin_unlock(&vcap->qlock);
> +}
> +
> +static int vimc_cap_pipeline_s_stream(struct vimc_cap_device *vcap, int
> enable)
> +{
> +	int ret;
> +	struct media_pad *pad;
> +	struct media_entity *entity;
> +	struct v4l2_subdev *sd;
> +
> +	/* Start the stream in the subdevice direct connected */
> +	entity = &vcap->vdev.entity;
> +	pad = media_entity_remote_pad(&entity->pads[0]);
> +
> +	/* If we are not connected to any subdev node, it means there is 
nothing
> +	 * to activate on the pipe (e.g. we can be connected with an input
> +	 * device or we are not connected at all)

Shouldn't this have resulted in a pipeline validation error ?

> +	 */
> +	if (pad == NULL || !is_media_entity_v4l2_subdev(pad->entity))
> +		return 0;
> +
> +	entity = pad->entity;
> +	sd = media_entity_to_v4l2_subdev(entity);
> +
> +	ret = v4l2_subdev_call(sd, video, s_stream, enable);
> +	if (ret && ret != -ENOIOCTLCMD)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int
> count)
> +{
> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
> +	struct media_entity *entity;
> +	int ret;
> +
> +	vcap->sequence = 0;
> +
> +	/* Start the media pipeline */
> +	entity = &vcap->vdev.entity;
> +	ret = media_entity_pipeline_start(entity, &vcap->pipe);
> +	if (ret) {
> +		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
> +		return ret;
> +	}
> +
> +	/* Enable streaming from the pipe */
> +	ret = vimc_cap_pipeline_s_stream(vcap, 1);
> +	if (ret) {

You should call media_entity_pipeline_stop() here.

> +		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * Stop the stream engine. Any remaining buffers in the stream queue are
> + * dequeued and passed on to the vb2 framework marked as STATE_ERROR.
> + */
> +static void vimc_cap_stop_streaming(struct vb2_queue *vq)
> +{
> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
> +
> +	/* Disable streaming from the pipe */
> +	vimc_cap_pipeline_s_stream(vcap, 0);
> +
> +	/* Stop the media pipeline */
> +	media_entity_pipeline_stop(&vcap->vdev.entity);
> +
> +	/* Release all active buffers */
> +	vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_ERROR);
> +}
> +
> +static void vimc_cap_buf_queue(struct vb2_buffer *vb2_buf)
> +{
> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb2_buf->vb2_queue);
> +	struct vimc_cap_buffer *buf = container_of(vb2_buf,
> +						   struct vimc_cap_buffer,
> +						   vb2.vb2_buf);
> +
> +	spin_lock(&vcap->qlock);
> +	list_add_tail(&buf->list, &vcap->buf_list);
> +	spin_unlock(&vcap->qlock);
> +}
> +
> +static int vimc_cap_queue_setup(struct vb2_queue *vq, unsigned int
> *nbuffers,
> +				unsigned int *nplanes, unsigned int sizes[],
> +				struct device *alloc_devs[])
> +{
> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
> +
> +	if (*nplanes)

You should also return an error if *nplanes != 1 in this case.

> +		return sizes[0] < vcap->format.sizeimage ? -EINVAL : 0;
> +	/* We don't support multiplanes for now */
> +	*nplanes = 1;
> +	sizes[0] = vcap->format.sizeimage;
> +
> +	return 0;
> +}
> +
> +/*
> + * Prepare the buffer for queueing to the DMA engine: check and set the

There's no DMA engine, this is a virtual device :-)

> + * payload size.
> + */
> +static int vimc_cap_buffer_prepare(struct vb2_buffer *vb)
> +{
> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb->vb2_queue);
> +	unsigned long size = vcap->format.sizeimage;
> +
> +	if (vb2_plane_size(vb, 0) < size) {
> +		dev_err(vcap->dev, "buffer too small (%lu < %lu)\n",
> +			vb2_plane_size(vb, 0), size);
> +		return -EINVAL;
> +	}
> +
> +	vb2_set_plane_payload(vb, 0, size);

If you call this here you don't have to duplicate the call in 
vimc_cap_process_frame(). I would keep the one in vimc_cap_process_frame() and 
remove this one.

> +	return 0;
> +}
> +
> +static const struct vb2_ops vimc_cap_qops = {
> +	.start_streaming	= vimc_cap_start_streaming,
> +	.stop_streaming		= vimc_cap_stop_streaming,
> +	.buf_queue		= vimc_cap_buf_queue,
> +	.queue_setup		= vimc_cap_queue_setup,
> +	.buf_prepare		= vimc_cap_buffer_prepare,
> +	/*
> +	 * Since q->lock is set we can use the standard
> +	 * vb2_ops_wait_prepare/finish helper functions.
> +	 */
> +	.wait_prepare		= vb2_ops_wait_prepare,
> +	.wait_finish		= vb2_ops_wait_finish,
> +};
> +
> +/* NOTE: this function is a copy of v4l2_subdev_link_validate_get_format
> + * maybe the v4l2 function should be public
> + */

Or we should add a standard subdev <-> video devnode link validation function 
in the core :-) So far all drivers validate that link manually, but I don't 
remember a good reason why that couldn't be performed by the link validation 
infrastructure. Sakari, what do you think ?

That's anyway not a blocker to get this patch merged.

> +static int vimc_cap_v4l2_subdev_link_validate_get_format(struct media_pad
> *pad,
> +						struct v4l2_subdev_format 
*fmt)
> +{
> +	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity);
> +
> +	fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +	fmt->pad = pad->index;
> +	return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt);
> +}
> +
> +static int vimc_cap_link_validate(struct media_link *link)
> +{
> +	struct v4l2_subdev_format source_fmt;
> +	struct v4l2_pix_format *sink_fmt;
> +	struct vimc_cap_device *vcap;
> +	const struct vimc_pix_map *vpix;
> +	int ret;
> +
> +	/* Retrieve the video capture device */
> +	vcap = container_of(link->sink->entity,
> +			    struct vimc_cap_device, vdev.entity);
> +
> +	/* If the connected node is not a subdevice type
> +	 * then it's a raw node from vimc-core, ignore the link for now
> +	 * TODO: remove this when there are no more raw nodes in the
> +	 * core and return error instead
> +	 */
> +	if (!is_media_entity_v4l2_subdev(link->source->entity))
> +		return 0;
> +
> +	/* Get the the format of the video device */
> +	sink_fmt = &vcap->format;
> +
> +	/* Get the the format of the subdev */
> +	ret = vimc_cap_v4l2_subdev_link_validate_get_format(link->source,
> +							    &source_fmt);
> +	if (ret)
> +		return ret;
> +
> +	dev_dbg(vcap->dev,
> +		"cap: %s: link validate formats src:%dx%d %d sink:%dx%d %d\n",
> +		vcap->vdev.name,
> +		source_fmt.format.width, source_fmt.format.height,
> +		source_fmt.format.code,
> +		sink_fmt->width, sink_fmt->height,
> +		sink_fmt->pixelformat);
> +
> +	/* Validate the format */
> +
> +	vpix = vimc_pix_map_by_pixelformat(sink_fmt->pixelformat);
> +	if (!vpix)
> +		return -EINVAL;

This can't happen, it should be caught by the set format handler. The driver 
should not allowed the stored pixel format to ever be invalid.

> +	/* The width, height and code must match. */
> +	if (source_fmt.format.width != sink_fmt->width
> +	    || source_fmt.format.height != sink_fmt->height
> +	    || vpix->code != source_fmt.format.code)
> +		return -EINVAL;
> +
> +	/* The field order must match, or the sink field order must be NONE
> +	 * to support interlaced hardware connected to bridges that support
> +	 * progressive formats only.
> +	 */
> +	if (source_fmt.format.field != sink_fmt->field &&
> +	    sink_fmt->field != V4L2_FIELD_NONE)
> +		return -EINVAL;
> +
> +	return 0;
> +}

[snip]

> +struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
> +					const char *const name,
> +					u16 num_pads,
> +					const unsigned long *pads_flag)
> +{
> +	int ret;
> +	struct vb2_queue *q;
> +	struct video_device *vdev;
> +	struct vimc_cap_device *vcap;
> +	const struct vimc_pix_map *vpix;
> +
> +	/* Check entity configuration params
> +	 * NOTE: we only support a single sink pad
> +	 */
> +	if (!name || num_pads != 1 || !pads_flag ||
> +	    !(pads_flag[0] & MEDIA_PAD_FL_SINK))
> +		return ERR_PTR(-EINVAL);
> +
> +	/* Allocate the vimc_cap_device struct */
> +	vcap = kzalloc(sizeof(*vcap), GFP_KERNEL);
> +	if (!vcap)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/* Link the vimc_cap_device struct with v4l2 and dev parent */
> +	vcap->v4l2_dev = v4l2_dev;
> +	vcap->dev = v4l2_dev->dev;
> +
> +	/* Allocate the pads */
> +	vcap->ved.pads = vimc_pads_init(num_pads, pads_flag);
> +	if (IS_ERR(vcap->ved.pads)) {
> +		ret = PTR_ERR(vcap->ved.pads);
> +		goto err_free_vcap;
> +	}
> +
> +	/* Initialize the media entity */
> +	vcap->vdev.entity.name = name;
> +	vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L;
> +	ret = media_entity_pads_init(&vcap->vdev.entity,
> +				     num_pads, vcap->ved.pads);
> +	if (ret)
> +		goto err_clean_pads;
> +
> +	/* Initialize the lock */
> +	mutex_init(&vcap->lock);
> +
> +	/* Initialize the vb2 queue */
> +	q = &vcap->queue;
> +	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	q->io_modes = VB2_MMAP | VB2_DMABUF;
> +	q->drv_priv = vcap;
> +	q->buf_struct_size = sizeof(struct vimc_cap_buffer);
> +	q->ops = &vimc_cap_qops;
> +	q->mem_ops = &vb2_vmalloc_memops;
> +	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> +	q->min_buffers_needed = 2;
> +	q->lock = &vcap->lock;
> +
> +	ret = vb2_queue_init(q);
> +	if (ret) {
> +		dev_err(vcap->dev,
> +			"vb2 queue init failed (err=%d)\n", ret);
> +		goto err_clean_m_ent;
> +	}
> +
> +	/* Initialize buffer list and its lock */
> +	INIT_LIST_HEAD(&vcap->buf_list);
> +	spin_lock_init(&vcap->qlock);
> +
> +	/* Set the frame format (this is hardcoded for now) */
> +	vcap->format.width = 640;
> +	vcap->format.height = 480;
> +	vcap->format.pixelformat = V4L2_PIX_FMT_RGB24;
> +	vcap->format.field = V4L2_FIELD_NONE;
> +	vcap->format.colorspace = V4L2_COLORSPACE_SRGB;
> +
> +	vpix = vimc_pix_map_by_pixelformat(vcap->format.pixelformat);
> +
> +	vcap->format.bytesperline = vcap->format.width * vpix->bpp;
> +	vcap->format.sizeimage = vcap->format.bytesperline *
> +				 vcap->format.height;
> +
> +	/* Fill the vimc_ent_device struct */
> +	vcap->ved.destroy = vimc_cap_destroy;
> +	vcap->ved.ent = &vcap->vdev.entity;
> +	vcap->ved.process_frame = vimc_cap_process_frame;
> +
> +	/* Initialize the video_device struct */
> +	vdev = &vcap->vdev;
> +	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
> +	vdev->entity.ops = &vimc_cap_mops;
> +	vdev->release = video_device_release_empty;

This will result in a crash if you keep the video device node open and unbind 
the vimc device from the driver. However, as the same issue in the media 
controller core hasn't been solved yet, I'm fine fixing all those problems in 
one go later.

> +	vdev->fops = &vimc_cap_fops;
> +	vdev->ioctl_ops = &vimc_cap_ioctl_ops;
> +	vdev->lock = &vcap->lock;
> +	vdev->queue = q;
> +	vdev->v4l2_dev = vcap->v4l2_dev;
> +	vdev->vfl_dir = VFL_DIR_RX;
> +	strlcpy(vdev->name, name, sizeof(vdev->name));
> +	video_set_drvdata(vdev, vcap);
> +
> +	/* Register the video_device with the v4l2 and the media framework */
> +	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
> +	if (ret) {
> +		dev_err(vcap->dev,
> +			"video register failed (err=%d)\n", ret);
> +		goto err_release_queue;
> +	}
> +
> +	return &vcap->ved;
> +
> +err_release_queue:
> +	vb2_queue_release(q);
> +err_clean_m_ent:
> +	media_entity_cleanup(&vcap->vdev.entity);
> +err_clean_pads:
> +	vimc_pads_cleanup(vcap->ved.pads);
> +err_free_vcap:
> +	kfree(vcap);
> +
> +	return ERR_PTR(ret);
> +}

[snip]

> diff --git a/drivers/media/platform/vimc/vimc-core.c
> b/drivers/media/platform/vimc/vimc-core.c new file mode 100644
> index 0000000..0a2b91b
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-core.c

[snip]

> +#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,					\
> +}
> +
> +#define VIMC_PIX_MAP(_code, _bpp, _pixelformat) {	\
> +	.code = _code,					\
> +	.pixelformat = _pixelformat,			\
> +	.bpp = _bpp,					\
> +}
> +
> +struct vimc_device {
> +	/* The pipeline configuration
> +	 * (filled before calling vimc_device_register)
> +	 */
> +	const struct vimc_pipeline_config *pipe_cfg;
> +
> +	/* The Associated media_device parent */
> +	struct media_device mdev;
> +
> +	/* Internal v4l2 parent device*/
> +	struct v4l2_device v4l2_dev;
> +
> +	/* Internal topology */
> +	struct vimc_ent_device **ved;
> +};
> +
> +/**
> + * enum vimc_ent_node - Select the functionality of a node in the topology
> + * @VIMC_ENT_NODE_SENSOR:	A node of type SENSOR simulates a camera 
sensor
> + *				generating internal images in bayer format and
> + *				propagating those images through the pipeline
> + * @VIMC_ENT_NODE_CAPTURE:	A node of type CAPTURE is a v4l2 video_device
> + *				that exposes the received image from the
> + *				pipeline to the user space
> + * @VIMC_ENT_NODE_INPUT:	A node of type INPUT is a v4l2 video_device 
that
> + *				receives images from the user space and
> + *				propagates them through the pipeline
> + * @VIMC_ENT_NODE_DEBAYER:	A node type DEBAYER expects to receive a frame
> + *				in bayer format converts it to RGB
> + * @VIMC_ENT_NODE_SCALER:	A node of type SCALER scales the received 
image
> + *				by a given multiplier
> + *
> + * This enum is used in the entity configuration struct to allow the
> definition + * of a custom topology specifying the role of each node on it.
> + */
> +enum vimc_ent_node {
> +	VIMC_ENT_NODE_SENSOR,
> +	VIMC_ENT_NODE_CAPTURE,
> +	VIMC_ENT_NODE_INPUT,
> +	VIMC_ENT_NODE_DEBAYER,
> +	VIMC_ENT_NODE_SCALER,
> +};
> +
> +/* Structure which describes individual configuration for each entity */
> +struct vimc_ent_config {
> +	const char *name;
> +	size_t pads_qty;
> +	const unsigned long *pads_flag;
> +	enum vimc_ent_node node;
> +};
> +
> +/* Structure which describes links between entities */
> +struct vimc_ent_link {
> +	unsigned int src_ent;
> +	u16 src_pad;
> +	unsigned int sink_ent;
> +	u16 sink_pad;
> +	u32 flags;
> +};
> +
> +/* Structure which describes the whole topology */
> +struct vimc_pipeline_config {
> +	const struct vimc_ent_config *ents;
> +	size_t num_ents;
> +	const struct vimc_ent_link *links;
> +	size_t num_links;
> +};
> +
> +/*
> --------------------------------------------------------------------------
> + * Topology Configuration
> + */
> +
> +static const struct vimc_ent_config ent_config[] = {
> +	{
> +		.name = "Sensor A",
> +		.pads_qty = 1,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
> +		.node = VIMC_ENT_NODE_SENSOR,
> +	},
> +	{
> +		.name = "Sensor B",
> +		.pads_qty = 1,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
> +		.node = VIMC_ENT_NODE_SENSOR,
> +	},
> +	{
> +		.name = "Debayer A",
> +		.pads_qty = 2,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
> +						     MEDIA_PAD_FL_SOURCE},
> +		.node = VIMC_ENT_NODE_DEBAYER,
> +	},
> +	{
> +		.name = "Debayer B",
> +		.pads_qty = 2,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
> +						     MEDIA_PAD_FL_SOURCE},
> +		.node = VIMC_ENT_NODE_DEBAYER,
> +	},
> +	{
> +		.name = "Raw Capture 0",
> +		.pads_qty = 1,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
> +		.node = VIMC_ENT_NODE_CAPTURE,
> +	},
> +	{
> +		.name = "Raw Capture 1",
> +		.pads_qty = 1,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
> +		.node = VIMC_ENT_NODE_CAPTURE,
> +	},
> +	{
> +		.name = "RGB/YUV Input",
> +		.pads_qty = 1,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
> +		.node = VIMC_ENT_NODE_INPUT,
> +	},
> +	{
> +		.name = "Scaler",
> +		.pads_qty = 2,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
> +						     MEDIA_PAD_FL_SOURCE},
> +		.node = VIMC_ENT_NODE_SCALER,
> +	},
> +	{
> +		.name = "RGB/YUV Capture",
> +		.pads_qty = 1,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
> +		.node = VIMC_ENT_NODE_CAPTURE,
> +	},
> +};
> +
> +static const struct vimc_ent_link ent_links[] = {
> +	/* Link: Sensor A (Pad 0)->(Pad 0) Debayer A */
> +	VIMC_ENT_LINK(0, 0, 2, 0, MEDIA_LNK_FL_ENABLED | 
MEDIA_LNK_FL_IMMUTABLE),
> +	/* Link: Sensor A (Pad 0)->(Pad 0) Raw Capture 0 */
> +	VIMC_ENT_LINK(0, 0, 4, 0, MEDIA_LNK_FL_ENABLED | 
MEDIA_LNK_FL_IMMUTABLE),
> +	/* Link: Sensor B (Pad 0)->(Pad 0) Debayer B */
> +	VIMC_ENT_LINK(1, 0, 3, 0, MEDIA_LNK_FL_ENABLED | 
MEDIA_LNK_FL_IMMUTABLE),
> +	/* Link: Sensor B (Pad 0)->(Pad 0) Raw Capture 1 */
> +	VIMC_ENT_LINK(1, 0, 5, 0, MEDIA_LNK_FL_ENABLED | 
MEDIA_LNK_FL_IMMUTABLE),
> +	/* Link: Debayer A (Pad 1)->(Pad 0) Scaler */
> +	VIMC_ENT_LINK(2, 1, 7, 0, MEDIA_LNK_FL_ENABLED),
> +	/* Link: Debayer B (Pad 1)->(Pad 0) Scaler */
> +	VIMC_ENT_LINK(3, 1, 7, 0, 0),
> +	/* Link: RGB/YUV Input (Pad 0)->(Pad 0) Scaler */
> +	VIMC_ENT_LINK(6, 0, 7, 0, 0),
> +	/* Link: Scaler (Pad 1)->(Pad 0) RGB/YUV Capture */
> +	VIMC_ENT_LINK(7, 1, 8, 0, MEDIA_LNK_FL_ENABLED | 
MEDIA_LNK_FL_IMMUTABLE),
> +};
> +
> +static const struct vimc_pipeline_config pipe_cfg = {
> +	.ents		= ent_config,
> +	.num_ents	= ARRAY_SIZE(ent_config),
> +	.links		= ent_links,
> +	.num_links	= ARRAY_SIZE(ent_links)
> +};
> +
> +/* --------------------------------------------------------------------- */
> +
> +static void vimc_dev_release(struct device *dev)
> +{}

Missing line break between the braces.

> +
> +static struct platform_device vimc_pdev = {
> +	.name		= VIMC_PDEV_NAME,
> +	.dev.release	= vimc_dev_release,
> +};
> +
> +const struct vimc_pix_map vimc_pix_map_list[] = {

This array isn't used outside of the compilation unit, you can make it static.

> +	/* TODO: add all missing formats */
> +
> +	/* RGB formats */
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_BGR888_1X24, 3, V4L2_PIX_FMT_BGR24),

Is there really an added value in using the macro compared to direct array 
initialization ?

> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_RGB888_1X24, 3, V4L2_PIX_FMT_RGB24),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_ARGB8888_1X32, 4, V4L2_PIX_FMT_ARGB32),
> +
> +	/* Bayer formats */
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR8_1X8, 1, V4L2_PIX_FMT_SBGGR8),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG8_1X8, 1, V4L2_PIX_FMT_SGBRG8),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG8_1X8, 1, V4L2_PIX_FMT_SGRBG8),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB8_1X8, 1, V4L2_PIX_FMT_SRGGB8),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR10_1X10, 2, V4L2_PIX_FMT_SBGGR10),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG10_1X10, 2, V4L2_PIX_FMT_SGBRG10),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG10_1X10, 2, V4L2_PIX_FMT_SGRBG10),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB10_1X10, 2, V4L2_PIX_FMT_SRGGB10),
> +	/* 10bit raw bayer a-law compressed to 8 bits */
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8, 1,
> V4L2_PIX_FMT_SBGGR10ALAW8),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8, 1,
> V4L2_PIX_FMT_SGBRG10ALAW8),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8, 1,
> V4L2_PIX_FMT_SGRBG10ALAW8),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8, 1,
> V4L2_PIX_FMT_SRGGB10ALAW8),
> +	/* 10bit raw bayer DPCM compressed to 8 bits */
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, 1,
> V4L2_PIX_FMT_SBGGR10DPCM8),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, 1,
> V4L2_PIX_FMT_SGBRG10DPCM8),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, 1,
> V4L2_PIX_FMT_SGRBG10DPCM8),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, 1, 
V4L2_PIX_FMT_SRGGB10DPCM8),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR12_1X12, 2, V4L2_PIX_FMT_SBGGR12),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG12_1X12, 2, V4L2_PIX_FMT_SGBRG12),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG12_1X12, 2, V4L2_PIX_FMT_SGRBG12),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB12_1X12, 2, V4L2_PIX_FMT_SRGGB12),
> +
> +	/* End */
> +	{0, 0, 0}
> +};
> +
> +const struct vimc_pix_map *vimc_pix_map_by_code(u32 code)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; vimc_pix_map_list[i].bpp; i++) {

You can use i < ARRAY_SIZE(vimc_pix_map_list) as the condition here and in the 
function below, and get rid of the termination marker in the array.

> +		if (vimc_pix_map_list[i].code == code)
> +			return &vimc_pix_map_list[i];
> +	}
> +	return NULL;
> +}
> +
> +const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; vimc_pix_map_list[i].bpp; i++) {
> +		if (vimc_pix_map_list[i].pixelformat == pixelformat)
> +			return &vimc_pix_map_list[i];
> +	}
> +	return NULL;
> +}

[snip]

> +/* TODO: remove this function when all the
> + * entities specific code are implemented
> + */
> +static struct vimc_ent_device *vimc_raw_create(struct v4l2_device
> *v4l2_dev,
> +					       const char *const name,
> +					       u16 num_pads,
> +					       const unsigned long *pads_flag)
> +{
> +	int ret;
> +	struct vimc_ent_device *ved;
> +
> +	/* Allocate the main ved struct */
> +	ved = kzalloc(sizeof(*ved), GFP_KERNEL);
> +	if (!ved)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/* Allocate the media entity */
> +	ved->ent = kzalloc(sizeof(*ved->ent), GFP_KERNEL);
> +	if (!ved->ent) {
> +		ret = -ENOMEM;
> +		goto err_free_ved;
> +	}
> +
> +	/* Allocate the pads */
> +	ved->pads = vimc_pads_init(num_pads, pads_flag);
> +	if (IS_ERR(ved->pads)) {
> +		ret = PTR_ERR(ved->pads);
> +		goto err_free_ent;
> +	}
> +
> +	/* Initialize the media entity */
> +	ved->ent->name = name;
> +	ved->ent->function = MEDIA_ENT_F_IO_V4L;

This isn't a video node. You can use MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN for now.

> +	ret = media_entity_pads_init(ved->ent, num_pads, ved->pads);
> +	if (ret)
> +		goto err_cleanup_pads;
> +
> +	/* Register the media entity */
> +	ret = media_device_register_entity(v4l2_dev->mdev, ved->ent);
> +	if (ret)
> +		goto err_cleanup_entity;
> +
> +	/* Fill out the destroy function and return */
> +	ved->destroy = vimc_raw_destroy;
> +	return ved;
> +
> +err_cleanup_entity:
> +	media_entity_cleanup(ved->ent);
> +err_cleanup_pads:
> +	vimc_pads_cleanup(ved->pads);
> +err_free_ent:
> +	kfree(ved->ent);
> +err_free_ved:
> +	kfree(ved);
> +
> +	return ERR_PTR(ret);
> +}
> +
> +static int vimc_device_register(struct vimc_device *vimc)
> +{
> +	unsigned int i;
> +	int ret = 0;

There's no need to assign 0 to ret here.

> +	/* Allocate memory for the vimc_ent_devices pointers */
> +	vimc->ved = devm_kcalloc(vimc->mdev.dev, vimc->pipe_cfg->num_ents,
> +				 sizeof(*vimc->ved), GFP_KERNEL);
> +	if (!vimc->ved)
> +		return -ENOMEM;
> +
> +	/* Register the media device */
> +	ret = media_device_register(&vimc->mdev);

Shouldn't you register the media device after creating the entities and links 
?

> +	if (ret) {
> +		dev_err(vimc->mdev.dev,
> +			"media device register failed (err=%d)\n", ret);
> +		return ret;
> +	}
> +
> +	/* Link the media device within the v4l2_device */
> +	vimc->v4l2_dev.mdev = &vimc->mdev;
> +
> +	/* Register the v4l2 struct */
> +	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);
> +		return ret;
> +	}
> +
> +	/* Initialize entities */
> +	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
> +		struct vimc_ent_device *(*create_func)(struct v4l2_device *,
> +						       const char *const,
> +						       u16,
> +						       const unsigned long *);
> +
> +		/* Register the specific node */
> +		switch (vimc->pipe_cfg->ents[i].node) {
> +		case VIMC_ENT_NODE_SENSOR:
> +			create_func = vimc_sen_create;
> +			break;
> +
> +		case VIMC_ENT_NODE_CAPTURE:
> +			create_func = vimc_cap_create;
> +			break;
> +
> +		/* TODO: Instantiate the specific topology node */
> +		case VIMC_ENT_NODE_INPUT:
> +		case VIMC_ENT_NODE_DEBAYER:
> +		case VIMC_ENT_NODE_SCALER:
> +		default:
> +			/* TODO: remove this when all the entities specific
> +			 * code are implemented
> +			 */
> +			create_func = vimc_raw_create;
> +			break;
> +		}
> +
> +		vimc->ved[i] = create_func(&vimc->v4l2_dev,
> +					   vimc->pipe_cfg->ents[i].name,
> +					   vimc->pipe_cfg->ents[i].pads_qty,
> +					   vimc->pipe_cfg->ents[i].pads_flag);
> +		if (IS_ERR(vimc->ved[i])) {
> +			ret = PTR_ERR(vimc->ved[i]);
> +			vimc->ved[i] = NULL;
> +			goto err;
> +		}
> +
> +		/* Set use_count to keep track of the ved structure */
> +		vimc->ved[i]->ent->use_count = i;

The media_entity::use_count field is managed by the media controller core. You 
can't use it for your own purpose.

> +	}
> +
> +	/* 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];
> +
> +		ret = media_create_pad_link(vimc->ved[link->src_ent]->ent,
> +					    link->src_pad,
> +					    vimc->ved[link->sink_ent]->ent,
> +					    link->sink_pad,
> +					    link->flags);
> +		if (ret)
> +			goto err;
> +	}
> +
> +	/* 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);
> +		goto err;
> +	}
> +
> +	return 0;
> +
> +err:
> +	/* Destroy the so far created topology */
> +	vimc_device_unregister(vimc);
> +
> +	return ret;
> +}
> +
> +static int vimc_probe(struct platform_device *pdev)
> +{
> +	struct vimc_device *vimc;
> +	int ret;
> +
> +	/* Prepare the vimc topology structure */
> +
> +	/* Allocate memory for the vimc structure */
> +	vimc = devm_kzalloc(&pdev->dev, sizeof(*vimc), GFP_KERNEL);
> +	if (!vimc)
> +		return -ENOMEM;

We have recently come to an agreement that devm_kzalloc() is harmful to 
allocate structures that embed instances of struct media_device. While this is 
still an unresolved problem, could you please use kzalloc() to avoid making it 
worse ? It will require a kfree() in the error paths and in the remove 
handler.

> +	/* Set the pipeline configuration struct */
> +	vimc->pipe_cfg = &pipe_cfg;
> +
> +	/* Initialize media device */
> +	strlcpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME,
> +		sizeof(vimc->mdev.model));
> +	vimc->mdev.dev = &pdev->dev;
> +	media_device_init(&vimc->mdev);
> +
> +	/* Create vimc topology */
> +	ret = vimc_device_register(vimc);
> +	if (ret) {
> +		dev_err(vimc->mdev.dev,
> +			"vimc device registration failed (err=%d)\n", ret);
> +		return ret;
> +	}
> +
> +	/* Link the topology object with the platform device object */
> +	platform_set_drvdata(pdev, vimc);
> +
> +	return 0;
> +}
> +
> +static int vimc_remove(struct platform_device *pdev)
> +{
> +	struct vimc_device *vimc;
> +
> +	/* Get the topology object linked with the platform device object */
> +	vimc = platform_get_drvdata(pdev);

You could combine that in a single line

	struct vimc_device *vimc = platform_get_drvdata(pdev);

I understand that the comment was written when you had less experience with 
the kernel code which pushed you on the side of verbosity, but I believe it's 
not really needed :-)

> +
> +	/* Destroy all the topology */
> +	vimc_device_unregister(vimc);
> +
> +	return 0;
> +}

[snip]

> diff --git a/drivers/media/platform/vimc/vimc-sensor.c
> b/drivers/media/platform/vimc/vimc-sensor.c new file mode 100644
> index 0000000..174e5dc
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-sensor.c

[snip]

> +static int vimc_thread_sen(void *data)
> +{
> +	unsigned int i;
> +	struct vimc_sen_device *vsen = data;
> +
> +	set_freezable();
> +
> +	for (;;) {
> +		try_to_freeze();
> +		if (kthread_should_stop())
> +			break;
> +
> +		memset(vsen->frame, 100, vsen->frame_size);
> +
> +		/* Send the frame to all source pads */
> +		for (i = 0; i < vsen->sd.entity.num_pads; i++)
> +			vimc_propagate_frame(vsen->dev,
> +					     &vsen->sd.entity.pads[i],
> +					     vsen->frame);
> +
> +		/* 60 frames per second */
> +		schedule_timeout_interruptible(HZ/60);

What can this be interrupted by ?

> +	}
> +
> +	return 0;
> +}

[snip]

> +struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
> +					const char *const name,
> +					u16 num_pads,
> +					const unsigned long *pads_flag)
> +{
> +	int ret;
> +	unsigned int i;
> +	struct vimc_sen_device *vsen;
> +
> +	if (!name || (num_pads && !pads_flag))
> +		return ERR_PTR(-EINVAL);

I think you need at least one pad. Or actually, exactly one pad.

> +	/* check if all pads are sources */
> +	for (i = 0; i < num_pads; i++)
> +		if (!(pads_flag[i] & MEDIA_PAD_FL_SOURCE))
> +			return ERR_PTR(-EINVAL);
> +
> +	/* Allocate the vsen struct */
> +	vsen = kzalloc(sizeof(*vsen), GFP_KERNEL);
> +	if (!vsen)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/* Link the vimc_sen_device struct with the v4l2 parent */
> +	vsen->v4l2_dev = v4l2_dev;
> +	/* Link the vimc_sen_device struct with the dev parent */
> +	vsen->dev = v4l2_dev->dev;
> +
> +	/* Allocate the pads */
> +	vsen->ved.pads = vimc_pads_init(num_pads, pads_flag);
> +	if (IS_ERR(vsen->ved.pads)) {
> +		ret = PTR_ERR(vsen->ved.pads);
> +		goto err_free_vsen;
> +	}
> +
> +	/* Initialize the media entity */
> +	vsen->sd.entity.name = name;
> +	ret = media_entity_pads_init(&vsen->sd.entity,
> +				     num_pads, vsen->ved.pads);
> +	if (ret)
> +		goto err_clean_pads;
> +
> +	/* Set the active frame format (this is hardcoded for now) */
> +	vsen->mbus_format.width = 640;
> +	vsen->mbus_format.height = 480;
> +	vsen->mbus_format.code = MEDIA_BUS_FMT_RGB888_1X24;
> +	vsen->mbus_format.field = V4L2_FIELD_NONE;
> +	vsen->mbus_format.colorspace = V4L2_COLORSPACE_SRGB;
> +	vsen->mbus_format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
> +	vsen->mbus_format.xfer_func = V4L2_XFER_FUNC_SRGB;
> +
> +	/* Fill the vimc_ent_device struct */
> +	vsen->ved.destroy = vimc_sen_destroy;
> +	vsen->ved.ent = &vsen->sd.entity;
> +
> +	/* Initialize the subdev */
> +	v4l2_subdev_init(&vsen->sd, &vimc_sen_ops);
> +	vsen->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
> +	vsen->sd.entity.ops = &vimc_sen_mops;
> +	vsen->sd.owner = THIS_MODULE;
> +	strlcpy(vsen->sd.name, name, sizeof(vsen->sd.name));
> +	v4l2_set_subdevdata(&vsen->sd, vsen);
> +
> +	/* Expose this subdev to user space */
> +	vsen->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
> +
> +	/* Register the subdev with the v4l2 and the media framework */
> +	ret = v4l2_device_register_subdev(vsen->v4l2_dev, &vsen->sd);
> +	if (ret) {
> +		dev_err(vsen->dev,
> +			"subdev register failed (err=%d)\n", ret);
> +		goto err_clean_m_ent;
> +	}
> +
> +	return &vsen->ved;
> +
> +err_clean_m_ent:
> +	media_entity_cleanup(&vsen->sd.entity);
> +err_clean_pads:
> +	vimc_pads_cleanup(vsen->ved.pads);
> +err_free_vsen:
> +	kfree(vsen);
> +
> +	return ERR_PTR(ret);
> +}

[snip]

-- 
Regards,

Laurent Pinchart


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

* Re: [PATCH v6] [media] vimc: Virtual Media Controller core, capture and sensor
  2017-01-10 19:54         ` [PATCH v6] [media] vimc: Virtual Media Controller core, capture and sensor Laurent Pinchart
@ 2017-01-11  1:30           ` Helen Koike
  2017-03-10 13:08             ` Hans Verkuil
  0 siblings, 1 reply; 41+ messages in thread
From: Helen Koike @ 2017-01-11  1:30 UTC (permalink / raw)
  To: Laurent Pinchart, Helen Koike
  Cc: Hans Verkuil, linux-media, jgebben, mchehab, Helen Fornazier,
	Sakari Ailus

Hi Laurent,

On 2017-01-10 04:54 PM, Laurent Pinchart wrote:
> Hi Helen,
>
> (CC'ing Sakari as there's a question specifically for him)
>
> Thank you for the patch, and so sorry for the late review.
>
> On Sunday 04 Sep 2016 17:02:18 Helen Koike wrote:
>> From: Helen Fornazier <helen.fornazier@gmail.com>
>>
>> First version of the Virtual Media Controller.
>> Add a simple version of the core of the driver, the capture and
>> sensor nodes in the topology, generating a grey image in a hardcoded
>> format.
>>
>> Signed-off-by: Helen Koike <helen.koike@collabora.com>
>
> I've reviewed the whole patch but haven't had time to test it. I've also
> skipped the items marked as TODO or FIXME as they're obviously not ready yet
> :-) Overall this looks good to me, all the issues are minor.
>
>> ---
>>
>> Patch based in media/master tree, and available here:
>> https://github.com/helen-fornazier/opw-staging/tree/vimc/devel/vpu
>>
>> Changes since v5:
>> - Fix message "Entity type for entity Sensor A was not initialized!"
>>   by initializing the sensor entity.function after the calling
>>   v4l2_subded_init
>> - populate device_caps in vimc_cap_create instead of in
>>   vimc_cap_querycap
>> - Fix typo in vimc-core.c s/de/the
>>
>> Changes since v4:
>> - coding style fixes
>> - remove BUG_ON
>> - change copyright to 2016
>> - depens on VIDEO_V4L2_SUBDEV_API instead of select
>> - remove assignement of V4L2_CAP_DEVICE_CAPS
>> - s/vimc_cap_g_fmt_vid_cap/vimc_cap_fmt_vid_cap
>> - fix vimc_cap_queue_setup declaration type
>> - remove wrong buffer size check and add it in vimc_cap_buffer_prepare
>> - vimc_cap_create: remove unecessary check if v4l2_dev or v4l2_dev->dev is
>> null - vimc_cap_create: only allow a single pad
>> - vimc_sen_create: only allow source pads, remove unecessary source pads
>> checks in vimc_thread_sen
>>
>> Changes since v3: fix rmmod crash and built-in compile
>> - Re-order unregister calls in vimc_device_unregister function (remove
>> rmmod issue)
>> - Call media_device_unregister_entity in vimc_raw_destroy
>> - Add depends on VIDEO_DEV && VIDEO_V4L2 and select VIDEOBUF2_VMALLOC
>> - Check *nplanes in queue_setup (this remove v4l2-compliance fail)
>> - Include <linux/kthread.h> in vimc-sensor.c
>> - Move include of <linux/slab.h> from vimc-core.c to vimc-core.h
>> - Generate 60 frames per sec instead of 1 in the sensor
>>
>> Changes since v2: update with current media master tree
>> - Add struct media_pipeline in vimc_cap_device
>> - Use vb2_v4l2_buffer instead of vb2_buffer
>> - Typos
>> - Remove usage of entity->type and use entity->function instead
>> - Remove fmt argument from queue setup
>> - Use ktime_get_ns instead of v4l2_get_timestamp
>> - Iterate over link's list using list_for_each_entry
>> - Use media_device_{init, cleanup}
>> - Use entity->use_count to keep track of entities instead of the old
>> entity->id
>> - Replace media_entity_init by media_entity_pads_init
>> ---
>>  drivers/media/platform/Kconfig             |   2 +
>>  drivers/media/platform/Makefile            |   1 +
>>  drivers/media/platform/vimc/Kconfig        |   7 +
>>  drivers/media/platform/vimc/Makefile       |   3 +
>>  drivers/media/platform/vimc/vimc-capture.c | 553 ++++++++++++++++++++++++++
>>  drivers/media/platform/vimc/vimc-capture.h |  28 ++
>>  drivers/media/platform/vimc/vimc-core.c    | 600 ++++++++++++++++++++++++++
>>  drivers/media/platform/vimc/vimc-core.h    |  57 +++
>>  drivers/media/platform/vimc/vimc-sensor.c  | 279 ++++++++++++++
>>  drivers/media/platform/vimc/vimc-sensor.h  |  28 ++
>>  10 files changed, 1558 insertions(+)
>>  create mode 100644 drivers/media/platform/vimc/Kconfig
>>  create mode 100644 drivers/media/platform/vimc/Makefile
>>  create mode 100644 drivers/media/platform/vimc/vimc-capture.c
>>  create mode 100644 drivers/media/platform/vimc/vimc-capture.h
>>  create mode 100644 drivers/media/platform/vimc/vimc-core.c
>>  create mode 100644 drivers/media/platform/vimc/vimc-core.h
>>  create mode 100644 drivers/media/platform/vimc/vimc-sensor.c
>>  create mode 100644 drivers/media/platform/vimc/vimc-sensor.h
>
> [snip]
>
>> diff --git a/drivers/media/platform/vimc/vimc-capture.c
>> b/drivers/media/platform/vimc/vimc-capture.c new file mode 100644
>> index 0000000..b7636cf
>> --- /dev/null
>> +++ b/drivers/media/platform/vimc/vimc-capture.c
>
> [snip]
>
>> +static void vimc_cap_return_all_buffers(struct vimc_cap_device *vcap,
>> +					enum vb2_buffer_state state)
>> +{
>> +	struct vimc_cap_buffer *vbuf, *node;
>> +
>> +	spin_lock(&vcap->qlock);
>> +
>> +	list_for_each_entry_safe(vbuf, node, &vcap->buf_list, list) {
>> +		vb2_buffer_done(&vbuf->vb2.vb2_buf, state);
>> +		list_del(&vbuf->list);
>
> It shouldn't matter given that you protect this with a spinlock, but moving
> the list_del() above makes the code flow follow a safer order.
>
>> +	}
>> +
>> +	spin_unlock(&vcap->qlock);
>> +}
>> +
>> +static int vimc_cap_pipeline_s_stream(struct vimc_cap_device *vcap, int
>> enable)
>> +{
>> +	int ret;
>> +	struct media_pad *pad;
>> +	struct media_entity *entity;
>> +	struct v4l2_subdev *sd;
>> +
>> +	/* Start the stream in the subdevice direct connected */
>> +	entity = &vcap->vdev.entity;
>> +	pad = media_entity_remote_pad(&entity->pads[0]);
>> +
>> +	/* If we are not connected to any subdev node, it means there is
> nothing
>> +	 * to activate on the pipe (e.g. we can be connected with an input
>> +	 * device or we are not connected at all)
>
> Shouldn't this have resulted in a pipeline validation error ?
>
>> +	 */
>> +	if (pad == NULL || !is_media_entity_v4l2_subdev(pad->entity))
>> +		return 0;
>> +
>> +	entity = pad->entity;
>> +	sd = media_entity_to_v4l2_subdev(entity);
>> +
>> +	ret = v4l2_subdev_call(sd, video, s_stream, enable);
>> +	if (ret && ret != -ENOIOCTLCMD)
>> +		return ret;
>> +
>> +	return 0;
>> +}
>> +
>> +static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int
>> count)
>> +{
>> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
>> +	struct media_entity *entity;
>> +	int ret;
>> +
>> +	vcap->sequence = 0;
>> +
>> +	/* Start the media pipeline */
>> +	entity = &vcap->vdev.entity;
>> +	ret = media_entity_pipeline_start(entity, &vcap->pipe);
>> +	if (ret) {
>> +		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
>> +		return ret;
>> +	}
>> +
>> +	/* Enable streaming from the pipe */
>> +	ret = vimc_cap_pipeline_s_stream(vcap, 1);
>> +	if (ret) {
>
> You should call media_entity_pipeline_stop() here.
>
>> +		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
>> +		return ret;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +/*
>> + * Stop the stream engine. Any remaining buffers in the stream queue are
>> + * dequeued and passed on to the vb2 framework marked as STATE_ERROR.
>> + */
>> +static void vimc_cap_stop_streaming(struct vb2_queue *vq)
>> +{
>> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
>> +
>> +	/* Disable streaming from the pipe */
>> +	vimc_cap_pipeline_s_stream(vcap, 0);
>> +
>> +	/* Stop the media pipeline */
>> +	media_entity_pipeline_stop(&vcap->vdev.entity);
>> +
>> +	/* Release all active buffers */
>> +	vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_ERROR);
>> +}
>> +
>> +static void vimc_cap_buf_queue(struct vb2_buffer *vb2_buf)
>> +{
>> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb2_buf->vb2_queue);
>> +	struct vimc_cap_buffer *buf = container_of(vb2_buf,
>> +						   struct vimc_cap_buffer,
>> +						   vb2.vb2_buf);
>> +
>> +	spin_lock(&vcap->qlock);
>> +	list_add_tail(&buf->list, &vcap->buf_list);
>> +	spin_unlock(&vcap->qlock);
>> +}
>> +
>> +static int vimc_cap_queue_setup(struct vb2_queue *vq, unsigned int
>> *nbuffers,
>> +				unsigned int *nplanes, unsigned int sizes[],
>> +				struct device *alloc_devs[])
>> +{
>> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
>> +
>> +	if (*nplanes)
>
> You should also return an error if *nplanes != 1 in this case.
>
>> +		return sizes[0] < vcap->format.sizeimage ? -EINVAL : 0;
>> +	/* We don't support multiplanes for now */
>> +	*nplanes = 1;
>> +	sizes[0] = vcap->format.sizeimage;
>> +
>> +	return 0;
>> +}
>> +
>> +/*
>> + * Prepare the buffer for queueing to the DMA engine: check and set the
>
> There's no DMA engine, this is a virtual device :-)
>
>> + * payload size.
>> + */
>> +static int vimc_cap_buffer_prepare(struct vb2_buffer *vb)
>> +{
>> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb->vb2_queue);
>> +	unsigned long size = vcap->format.sizeimage;
>> +
>> +	if (vb2_plane_size(vb, 0) < size) {
>> +		dev_err(vcap->dev, "buffer too small (%lu < %lu)\n",
>> +			vb2_plane_size(vb, 0), size);
>> +		return -EINVAL;
>> +	}
>> +
>> +	vb2_set_plane_payload(vb, 0, size);
>
> If you call this here you don't have to duplicate the call in
> vimc_cap_process_frame(). I would keep the one in vimc_cap_process_frame() and
> remove this one.
>
>> +	return 0;
>> +}
>> +
>> +static const struct vb2_ops vimc_cap_qops = {
>> +	.start_streaming	= vimc_cap_start_streaming,
>> +	.stop_streaming		= vimc_cap_stop_streaming,
>> +	.buf_queue		= vimc_cap_buf_queue,
>> +	.queue_setup		= vimc_cap_queue_setup,
>> +	.buf_prepare		= vimc_cap_buffer_prepare,
>> +	/*
>> +	 * Since q->lock is set we can use the standard
>> +	 * vb2_ops_wait_prepare/finish helper functions.
>> +	 */
>> +	.wait_prepare		= vb2_ops_wait_prepare,
>> +	.wait_finish		= vb2_ops_wait_finish,
>> +};
>> +
>> +/* NOTE: this function is a copy of v4l2_subdev_link_validate_get_format
>> + * maybe the v4l2 function should be public
>> + */
>
> Or we should add a standard subdev <-> video devnode link validation function
> in the core :-) So far all drivers validate that link manually, but I don't
> remember a good reason why that couldn't be performed by the link validation
> infrastructure. Sakari, what do you think ?
>
> That's anyway not a blocker to get this patch merged.
>
>> +static int vimc_cap_v4l2_subdev_link_validate_get_format(struct media_pad
>> *pad,
>> +						struct v4l2_subdev_format
> *fmt)
>> +{
>> +	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity);
>> +
>> +	fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
>> +	fmt->pad = pad->index;
>> +	return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt);
>> +}
>> +
>> +static int vimc_cap_link_validate(struct media_link *link)
>> +{
>> +	struct v4l2_subdev_format source_fmt;
>> +	struct v4l2_pix_format *sink_fmt;
>> +	struct vimc_cap_device *vcap;
>> +	const struct vimc_pix_map *vpix;
>> +	int ret;
>> +
>> +	/* Retrieve the video capture device */
>> +	vcap = container_of(link->sink->entity,
>> +			    struct vimc_cap_device, vdev.entity);
>> +
>> +	/* If the connected node is not a subdevice type
>> +	 * then it's a raw node from vimc-core, ignore the link for now
>> +	 * TODO: remove this when there are no more raw nodes in the
>> +	 * core and return error instead
>> +	 */
>> +	if (!is_media_entity_v4l2_subdev(link->source->entity))
>> +		return 0;
>> +
>> +	/* Get the the format of the video device */
>> +	sink_fmt = &vcap->format;
>> +
>> +	/* Get the the format of the subdev */
>> +	ret = vimc_cap_v4l2_subdev_link_validate_get_format(link->source,
>> +							    &source_fmt);
>> +	if (ret)
>> +		return ret;
>> +
>> +	dev_dbg(vcap->dev,
>> +		"cap: %s: link validate formats src:%dx%d %d sink:%dx%d %d\n",
>> +		vcap->vdev.name,
>> +		source_fmt.format.width, source_fmt.format.height,
>> +		source_fmt.format.code,
>> +		sink_fmt->width, sink_fmt->height,
>> +		sink_fmt->pixelformat);
>> +
>> +	/* Validate the format */
>> +
>> +	vpix = vimc_pix_map_by_pixelformat(sink_fmt->pixelformat);
>> +	if (!vpix)
>> +		return -EINVAL;
>
> This can't happen, it should be caught by the set format handler. The driver
> should not allowed the stored pixel format to ever be invalid.
>
>> +	/* The width, height and code must match. */
>> +	if (source_fmt.format.width != sink_fmt->width
>> +	    || source_fmt.format.height != sink_fmt->height
>> +	    || vpix->code != source_fmt.format.code)
>> +		return -EINVAL;
>> +
>> +	/* The field order must match, or the sink field order must be NONE
>> +	 * to support interlaced hardware connected to bridges that support
>> +	 * progressive formats only.
>> +	 */
>> +	if (source_fmt.format.field != sink_fmt->field &&
>> +	    sink_fmt->field != V4L2_FIELD_NONE)
>> +		return -EINVAL;
>> +
>> +	return 0;
>> +}
>
> [snip]
>
>> +struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
>> +					const char *const name,
>> +					u16 num_pads,
>> +					const unsigned long *pads_flag)
>> +{
>> +	int ret;
>> +	struct vb2_queue *q;
>> +	struct video_device *vdev;
>> +	struct vimc_cap_device *vcap;
>> +	const struct vimc_pix_map *vpix;
>> +
>> +	/* Check entity configuration params
>> +	 * NOTE: we only support a single sink pad
>> +	 */
>> +	if (!name || num_pads != 1 || !pads_flag ||
>> +	    !(pads_flag[0] & MEDIA_PAD_FL_SINK))
>> +		return ERR_PTR(-EINVAL);
>> +
>> +	/* Allocate the vimc_cap_device struct */
>> +	vcap = kzalloc(sizeof(*vcap), GFP_KERNEL);
>> +	if (!vcap)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	/* Link the vimc_cap_device struct with v4l2 and dev parent */
>> +	vcap->v4l2_dev = v4l2_dev;
>> +	vcap->dev = v4l2_dev->dev;
>> +
>> +	/* Allocate the pads */
>> +	vcap->ved.pads = vimc_pads_init(num_pads, pads_flag);
>> +	if (IS_ERR(vcap->ved.pads)) {
>> +		ret = PTR_ERR(vcap->ved.pads);
>> +		goto err_free_vcap;
>> +	}
>> +
>> +	/* Initialize the media entity */
>> +	vcap->vdev.entity.name = name;
>> +	vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L;
>> +	ret = media_entity_pads_init(&vcap->vdev.entity,
>> +				     num_pads, vcap->ved.pads);
>> +	if (ret)
>> +		goto err_clean_pads;
>> +
>> +	/* Initialize the lock */
>> +	mutex_init(&vcap->lock);
>> +
>> +	/* Initialize the vb2 queue */
>> +	q = &vcap->queue;
>> +	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>> +	q->io_modes = VB2_MMAP | VB2_DMABUF;
>> +	q->drv_priv = vcap;
>> +	q->buf_struct_size = sizeof(struct vimc_cap_buffer);
>> +	q->ops = &vimc_cap_qops;
>> +	q->mem_ops = &vb2_vmalloc_memops;
>> +	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
>> +	q->min_buffers_needed = 2;
>> +	q->lock = &vcap->lock;
>> +
>> +	ret = vb2_queue_init(q);
>> +	if (ret) {
>> +		dev_err(vcap->dev,
>> +			"vb2 queue init failed (err=%d)\n", ret);
>> +		goto err_clean_m_ent;
>> +	}
>> +
>> +	/* Initialize buffer list and its lock */
>> +	INIT_LIST_HEAD(&vcap->buf_list);
>> +	spin_lock_init(&vcap->qlock);
>> +
>> +	/* Set the frame format (this is hardcoded for now) */
>> +	vcap->format.width = 640;
>> +	vcap->format.height = 480;
>> +	vcap->format.pixelformat = V4L2_PIX_FMT_RGB24;
>> +	vcap->format.field = V4L2_FIELD_NONE;
>> +	vcap->format.colorspace = V4L2_COLORSPACE_SRGB;
>> +
>> +	vpix = vimc_pix_map_by_pixelformat(vcap->format.pixelformat);
>> +
>> +	vcap->format.bytesperline = vcap->format.width * vpix->bpp;
>> +	vcap->format.sizeimage = vcap->format.bytesperline *
>> +				 vcap->format.height;
>> +
>> +	/* Fill the vimc_ent_device struct */
>> +	vcap->ved.destroy = vimc_cap_destroy;
>> +	vcap->ved.ent = &vcap->vdev.entity;
>> +	vcap->ved.process_frame = vimc_cap_process_frame;
>> +
>> +	/* Initialize the video_device struct */
>> +	vdev = &vcap->vdev;
>> +	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
>> +	vdev->entity.ops = &vimc_cap_mops;
>> +	vdev->release = video_device_release_empty;
>
> This will result in a crash if you keep the video device node open and unbind
> the vimc device from the driver. However, as the same issue in the media
> controller core hasn't been solved yet, I'm fine fixing all those problems in
> one go later.
>
>> +	vdev->fops = &vimc_cap_fops;
>> +	vdev->ioctl_ops = &vimc_cap_ioctl_ops;
>> +	vdev->lock = &vcap->lock;
>> +	vdev->queue = q;
>> +	vdev->v4l2_dev = vcap->v4l2_dev;
>> +	vdev->vfl_dir = VFL_DIR_RX;
>> +	strlcpy(vdev->name, name, sizeof(vdev->name));
>> +	video_set_drvdata(vdev, vcap);
>> +
>> +	/* Register the video_device with the v4l2 and the media framework */
>> +	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
>> +	if (ret) {
>> +		dev_err(vcap->dev,
>> +			"video register failed (err=%d)\n", ret);
>> +		goto err_release_queue;
>> +	}
>> +
>> +	return &vcap->ved;
>> +
>> +err_release_queue:
>> +	vb2_queue_release(q);
>> +err_clean_m_ent:
>> +	media_entity_cleanup(&vcap->vdev.entity);
>> +err_clean_pads:
>> +	vimc_pads_cleanup(vcap->ved.pads);
>> +err_free_vcap:
>> +	kfree(vcap);
>> +
>> +	return ERR_PTR(ret);
>> +}
>
> [snip]
>
>> diff --git a/drivers/media/platform/vimc/vimc-core.c
>> b/drivers/media/platform/vimc/vimc-core.c new file mode 100644
>> index 0000000..0a2b91b
>> --- /dev/null
>> +++ b/drivers/media/platform/vimc/vimc-core.c
>
> [snip]
>
>> +#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,					\
>> +}
>> +
>> +#define VIMC_PIX_MAP(_code, _bpp, _pixelformat) {	\
>> +	.code = _code,					\
>> +	.pixelformat = _pixelformat,			\
>> +	.bpp = _bpp,					\
>> +}
>> +
>> +struct vimc_device {
>> +	/* The pipeline configuration
>> +	 * (filled before calling vimc_device_register)
>> +	 */
>> +	const struct vimc_pipeline_config *pipe_cfg;
>> +
>> +	/* The Associated media_device parent */
>> +	struct media_device mdev;
>> +
>> +	/* Internal v4l2 parent device*/
>> +	struct v4l2_device v4l2_dev;
>> +
>> +	/* Internal topology */
>> +	struct vimc_ent_device **ved;
>> +};
>> +
>> +/**
>> + * enum vimc_ent_node - Select the functionality of a node in the topology
>> + * @VIMC_ENT_NODE_SENSOR:	A node of type SENSOR simulates a camera
> sensor
>> + *				generating internal images in bayer format and
>> + *				propagating those images through the pipeline
>> + * @VIMC_ENT_NODE_CAPTURE:	A node of type CAPTURE is a v4l2 video_device
>> + *				that exposes the received image from the
>> + *				pipeline to the user space
>> + * @VIMC_ENT_NODE_INPUT:	A node of type INPUT is a v4l2 video_device
> that
>> + *				receives images from the user space and
>> + *				propagates them through the pipeline
>> + * @VIMC_ENT_NODE_DEBAYER:	A node type DEBAYER expects to receive a frame
>> + *				in bayer format converts it to RGB
>> + * @VIMC_ENT_NODE_SCALER:	A node of type SCALER scales the received
> image
>> + *				by a given multiplier
>> + *
>> + * This enum is used in the entity configuration struct to allow the
>> definition + * of a custom topology specifying the role of each node on it.
>> + */
>> +enum vimc_ent_node {
>> +	VIMC_ENT_NODE_SENSOR,
>> +	VIMC_ENT_NODE_CAPTURE,
>> +	VIMC_ENT_NODE_INPUT,
>> +	VIMC_ENT_NODE_DEBAYER,
>> +	VIMC_ENT_NODE_SCALER,
>> +};
>> +
>> +/* Structure which describes individual configuration for each entity */
>> +struct vimc_ent_config {
>> +	const char *name;
>> +	size_t pads_qty;
>> +	const unsigned long *pads_flag;
>> +	enum vimc_ent_node node;
>> +};
>> +
>> +/* Structure which describes links between entities */
>> +struct vimc_ent_link {
>> +	unsigned int src_ent;
>> +	u16 src_pad;
>> +	unsigned int sink_ent;
>> +	u16 sink_pad;
>> +	u32 flags;
>> +};
>> +
>> +/* Structure which describes the whole topology */
>> +struct vimc_pipeline_config {
>> +	const struct vimc_ent_config *ents;
>> +	size_t num_ents;
>> +	const struct vimc_ent_link *links;
>> +	size_t num_links;
>> +};
>> +
>> +/*
>> --------------------------------------------------------------------------
>> + * Topology Configuration
>> + */
>> +
>> +static const struct vimc_ent_config ent_config[] = {
>> +	{
>> +		.name = "Sensor A",
>> +		.pads_qty = 1,
>> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
>> +		.node = VIMC_ENT_NODE_SENSOR,
>> +	},
>> +	{
>> +		.name = "Sensor B",
>> +		.pads_qty = 1,
>> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
>> +		.node = VIMC_ENT_NODE_SENSOR,
>> +	},
>> +	{
>> +		.name = "Debayer A",
>> +		.pads_qty = 2,
>> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
>> +						     MEDIA_PAD_FL_SOURCE},
>> +		.node = VIMC_ENT_NODE_DEBAYER,
>> +	},
>> +	{
>> +		.name = "Debayer B",
>> +		.pads_qty = 2,
>> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
>> +						     MEDIA_PAD_FL_SOURCE},
>> +		.node = VIMC_ENT_NODE_DEBAYER,
>> +	},
>> +	{
>> +		.name = "Raw Capture 0",
>> +		.pads_qty = 1,
>> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
>> +		.node = VIMC_ENT_NODE_CAPTURE,
>> +	},
>> +	{
>> +		.name = "Raw Capture 1",
>> +		.pads_qty = 1,
>> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
>> +		.node = VIMC_ENT_NODE_CAPTURE,
>> +	},
>> +	{
>> +		.name = "RGB/YUV Input",
>> +		.pads_qty = 1,
>> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
>> +		.node = VIMC_ENT_NODE_INPUT,
>> +	},
>> +	{
>> +		.name = "Scaler",
>> +		.pads_qty = 2,
>> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
>> +						     MEDIA_PAD_FL_SOURCE},
>> +		.node = VIMC_ENT_NODE_SCALER,
>> +	},
>> +	{
>> +		.name = "RGB/YUV Capture",
>> +		.pads_qty = 1,
>> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
>> +		.node = VIMC_ENT_NODE_CAPTURE,
>> +	},
>> +};
>> +
>> +static const struct vimc_ent_link ent_links[] = {
>> +	/* Link: Sensor A (Pad 0)->(Pad 0) Debayer A */
>> +	VIMC_ENT_LINK(0, 0, 2, 0, MEDIA_LNK_FL_ENABLED |
> MEDIA_LNK_FL_IMMUTABLE),
>> +	/* Link: Sensor A (Pad 0)->(Pad 0) Raw Capture 0 */
>> +	VIMC_ENT_LINK(0, 0, 4, 0, MEDIA_LNK_FL_ENABLED |
> MEDIA_LNK_FL_IMMUTABLE),
>> +	/* Link: Sensor B (Pad 0)->(Pad 0) Debayer B */
>> +	VIMC_ENT_LINK(1, 0, 3, 0, MEDIA_LNK_FL_ENABLED |
> MEDIA_LNK_FL_IMMUTABLE),
>> +	/* Link: Sensor B (Pad 0)->(Pad 0) Raw Capture 1 */
>> +	VIMC_ENT_LINK(1, 0, 5, 0, MEDIA_LNK_FL_ENABLED |
> MEDIA_LNK_FL_IMMUTABLE),
>> +	/* Link: Debayer A (Pad 1)->(Pad 0) Scaler */
>> +	VIMC_ENT_LINK(2, 1, 7, 0, MEDIA_LNK_FL_ENABLED),
>> +	/* Link: Debayer B (Pad 1)->(Pad 0) Scaler */
>> +	VIMC_ENT_LINK(3, 1, 7, 0, 0),
>> +	/* Link: RGB/YUV Input (Pad 0)->(Pad 0) Scaler */
>> +	VIMC_ENT_LINK(6, 0, 7, 0, 0),
>> +	/* Link: Scaler (Pad 1)->(Pad 0) RGB/YUV Capture */
>> +	VIMC_ENT_LINK(7, 1, 8, 0, MEDIA_LNK_FL_ENABLED |
> MEDIA_LNK_FL_IMMUTABLE),
>> +};
>> +
>> +static const struct vimc_pipeline_config pipe_cfg = {
>> +	.ents		= ent_config,
>> +	.num_ents	= ARRAY_SIZE(ent_config),
>> +	.links		= ent_links,
>> +	.num_links	= ARRAY_SIZE(ent_links)
>> +};
>> +
>> +/* --------------------------------------------------------------------- */
>> +
>> +static void vimc_dev_release(struct device *dev)
>> +{}
>
> Missing line break between the braces.
>
>> +
>> +static struct platform_device vimc_pdev = {
>> +	.name		= VIMC_PDEV_NAME,
>> +	.dev.release	= vimc_dev_release,
>> +};
>> +
>> +const struct vimc_pix_map vimc_pix_map_list[] = {
>
> This array isn't used outside of the compilation unit, you can make it static.
>
>> +	/* TODO: add all missing formats */
>> +
>> +	/* RGB formats */
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_BGR888_1X24, 3, V4L2_PIX_FMT_BGR24),
>
> Is there really an added value in using the macro compared to direct array
> initialization ?
>
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_RGB888_1X24, 3, V4L2_PIX_FMT_RGB24),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_ARGB8888_1X32, 4, V4L2_PIX_FMT_ARGB32),
>> +
>> +	/* Bayer formats */
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR8_1X8, 1, V4L2_PIX_FMT_SBGGR8),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG8_1X8, 1, V4L2_PIX_FMT_SGBRG8),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG8_1X8, 1, V4L2_PIX_FMT_SGRBG8),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB8_1X8, 1, V4L2_PIX_FMT_SRGGB8),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR10_1X10, 2, V4L2_PIX_FMT_SBGGR10),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG10_1X10, 2, V4L2_PIX_FMT_SGBRG10),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG10_1X10, 2, V4L2_PIX_FMT_SGRBG10),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB10_1X10, 2, V4L2_PIX_FMT_SRGGB10),
>> +	/* 10bit raw bayer a-law compressed to 8 bits */
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8, 1,
>> V4L2_PIX_FMT_SBGGR10ALAW8),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8, 1,
>> V4L2_PIX_FMT_SGBRG10ALAW8),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8, 1,
>> V4L2_PIX_FMT_SGRBG10ALAW8),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8, 1,
>> V4L2_PIX_FMT_SRGGB10ALAW8),
>> +	/* 10bit raw bayer DPCM compressed to 8 bits */
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, 1,
>> V4L2_PIX_FMT_SBGGR10DPCM8),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, 1,
>> V4L2_PIX_FMT_SGBRG10DPCM8),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, 1,
>> V4L2_PIX_FMT_SGRBG10DPCM8),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, 1,
> V4L2_PIX_FMT_SRGGB10DPCM8),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR12_1X12, 2, V4L2_PIX_FMT_SBGGR12),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG12_1X12, 2, V4L2_PIX_FMT_SGBRG12),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG12_1X12, 2, V4L2_PIX_FMT_SGRBG12),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB12_1X12, 2, V4L2_PIX_FMT_SRGGB12),
>> +
>> +	/* End */
>> +	{0, 0, 0}
>> +};
>> +
>> +const struct vimc_pix_map *vimc_pix_map_by_code(u32 code)
>> +{
>> +	unsigned int i;
>> +
>> +	for (i = 0; vimc_pix_map_list[i].bpp; i++) {
>
> You can use i < ARRAY_SIZE(vimc_pix_map_list) as the condition here and in the
> function below, and get rid of the termination marker in the array.
>
>> +		if (vimc_pix_map_list[i].code == code)
>> +			return &vimc_pix_map_list[i];
>> +	}
>> +	return NULL;
>> +}
>> +
>> +const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat)
>> +{
>> +	unsigned int i;
>> +
>> +	for (i = 0; vimc_pix_map_list[i].bpp; i++) {
>> +		if (vimc_pix_map_list[i].pixelformat == pixelformat)
>> +			return &vimc_pix_map_list[i];
>> +	}
>> +	return NULL;
>> +}
>
> [snip]
>
>> +/* TODO: remove this function when all the
>> + * entities specific code are implemented
>> + */
>> +static struct vimc_ent_device *vimc_raw_create(struct v4l2_device
>> *v4l2_dev,
>> +					       const char *const name,
>> +					       u16 num_pads,
>> +					       const unsigned long *pads_flag)
>> +{
>> +	int ret;
>> +	struct vimc_ent_device *ved;
>> +
>> +	/* Allocate the main ved struct */
>> +	ved = kzalloc(sizeof(*ved), GFP_KERNEL);
>> +	if (!ved)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	/* Allocate the media entity */
>> +	ved->ent = kzalloc(sizeof(*ved->ent), GFP_KERNEL);
>> +	if (!ved->ent) {
>> +		ret = -ENOMEM;
>> +		goto err_free_ved;
>> +	}
>> +
>> +	/* Allocate the pads */
>> +	ved->pads = vimc_pads_init(num_pads, pads_flag);
>> +	if (IS_ERR(ved->pads)) {
>> +		ret = PTR_ERR(ved->pads);
>> +		goto err_free_ent;
>> +	}
>> +
>> +	/* Initialize the media entity */
>> +	ved->ent->name = name;
>> +	ved->ent->function = MEDIA_ENT_F_IO_V4L;
>
> This isn't a video node. You can use MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN for now.
>
>> +	ret = media_entity_pads_init(ved->ent, num_pads, ved->pads);
>> +	if (ret)
>> +		goto err_cleanup_pads;
>> +
>> +	/* Register the media entity */
>> +	ret = media_device_register_entity(v4l2_dev->mdev, ved->ent);
>> +	if (ret)
>> +		goto err_cleanup_entity;
>> +
>> +	/* Fill out the destroy function and return */
>> +	ved->destroy = vimc_raw_destroy;
>> +	return ved;
>> +
>> +err_cleanup_entity:
>> +	media_entity_cleanup(ved->ent);
>> +err_cleanup_pads:
>> +	vimc_pads_cleanup(ved->pads);
>> +err_free_ent:
>> +	kfree(ved->ent);
>> +err_free_ved:
>> +	kfree(ved);
>> +
>> +	return ERR_PTR(ret);
>> +}
>> +
>> +static int vimc_device_register(struct vimc_device *vimc)
>> +{
>> +	unsigned int i;
>> +	int ret = 0;
>
> There's no need to assign 0 to ret here.
>
>> +	/* Allocate memory for the vimc_ent_devices pointers */
>> +	vimc->ved = devm_kcalloc(vimc->mdev.dev, vimc->pipe_cfg->num_ents,
>> +				 sizeof(*vimc->ved), GFP_KERNEL);
>> +	if (!vimc->ved)
>> +		return -ENOMEM;
>> +
>> +	/* Register the media device */
>> +	ret = media_device_register(&vimc->mdev);
>
> Shouldn't you register the media device after creating the entities and links
> ?
>
>> +	if (ret) {
>> +		dev_err(vimc->mdev.dev,
>> +			"media device register failed (err=%d)\n", ret);
>> +		return ret;
>> +	}
>> +
>> +	/* Link the media device within the v4l2_device */
>> +	vimc->v4l2_dev.mdev = &vimc->mdev;
>> +
>> +	/* Register the v4l2 struct */
>> +	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);
>> +		return ret;
>> +	}
>> +
>> +	/* Initialize entities */
>> +	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
>> +		struct vimc_ent_device *(*create_func)(struct v4l2_device *,
>> +						       const char *const,
>> +						       u16,
>> +						       const unsigned long *);
>> +
>> +		/* Register the specific node */
>> +		switch (vimc->pipe_cfg->ents[i].node) {
>> +		case VIMC_ENT_NODE_SENSOR:
>> +			create_func = vimc_sen_create;
>> +			break;
>> +
>> +		case VIMC_ENT_NODE_CAPTURE:
>> +			create_func = vimc_cap_create;
>> +			break;
>> +
>> +		/* TODO: Instantiate the specific topology node */
>> +		case VIMC_ENT_NODE_INPUT:
>> +		case VIMC_ENT_NODE_DEBAYER:
>> +		case VIMC_ENT_NODE_SCALER:
>> +		default:
>> +			/* TODO: remove this when all the entities specific
>> +			 * code are implemented
>> +			 */
>> +			create_func = vimc_raw_create;
>> +			break;
>> +		}
>> +
>> +		vimc->ved[i] = create_func(&vimc->v4l2_dev,
>> +					   vimc->pipe_cfg->ents[i].name,
>> +					   vimc->pipe_cfg->ents[i].pads_qty,
>> +					   vimc->pipe_cfg->ents[i].pads_flag);
>> +		if (IS_ERR(vimc->ved[i])) {
>> +			ret = PTR_ERR(vimc->ved[i]);
>> +			vimc->ved[i] = NULL;
>> +			goto err;
>> +		}
>> +
>> +		/* Set use_count to keep track of the ved structure */
>> +		vimc->ved[i]->ent->use_count = i;
>
> The media_entity::use_count field is managed by the media controller core. You
> can't use it for your own purpose.
>
>> +	}
>> +
>> +	/* 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];
>> +
>> +		ret = media_create_pad_link(vimc->ved[link->src_ent]->ent,
>> +					    link->src_pad,
>> +					    vimc->ved[link->sink_ent]->ent,
>> +					    link->sink_pad,
>> +					    link->flags);
>> +		if (ret)
>> +			goto err;
>> +	}
>> +
>> +	/* 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);
>> +		goto err;
>> +	}
>> +
>> +	return 0;
>> +
>> +err:
>> +	/* Destroy the so far created topology */
>> +	vimc_device_unregister(vimc);
>> +
>> +	return ret;
>> +}
>> +
>> +static int vimc_probe(struct platform_device *pdev)
>> +{
>> +	struct vimc_device *vimc;
>> +	int ret;
>> +
>> +	/* Prepare the vimc topology structure */
>> +
>> +	/* Allocate memory for the vimc structure */
>> +	vimc = devm_kzalloc(&pdev->dev, sizeof(*vimc), GFP_KERNEL);
>> +	if (!vimc)
>> +		return -ENOMEM;
>
> We have recently come to an agreement that devm_kzalloc() is harmful to
> allocate structures that embed instances of struct media_device. While this is
> still an unresolved problem, could you please use kzalloc() to avoid making it
> worse ? It will require a kfree() in the error paths and in the remove
> handler.
>
>> +	/* Set the pipeline configuration struct */
>> +	vimc->pipe_cfg = &pipe_cfg;
>> +
>> +	/* Initialize media device */
>> +	strlcpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME,
>> +		sizeof(vimc->mdev.model));
>> +	vimc->mdev.dev = &pdev->dev;
>> +	media_device_init(&vimc->mdev);
>> +
>> +	/* Create vimc topology */
>> +	ret = vimc_device_register(vimc);
>> +	if (ret) {
>> +		dev_err(vimc->mdev.dev,
>> +			"vimc device registration failed (err=%d)\n", ret);
>> +		return ret;
>> +	}
>> +
>> +	/* Link the topology object with the platform device object */
>> +	platform_set_drvdata(pdev, vimc);
>> +
>> +	return 0;
>> +}
>> +
>> +static int vimc_remove(struct platform_device *pdev)
>> +{
>> +	struct vimc_device *vimc;
>> +
>> +	/* Get the topology object linked with the platform device object */
>> +	vimc = platform_get_drvdata(pdev);
>
> You could combine that in a single line
>
> 	struct vimc_device *vimc = platform_get_drvdata(pdev);
>
> I understand that the comment was written when you had less experience with
> the kernel code which pushed you on the side of verbosity, but I believe it's
> not really needed :-)
>
>> +
>> +	/* Destroy all the topology */
>> +	vimc_device_unregister(vimc);
>> +
>> +	return 0;
>> +}
>
> [snip]
>
>> diff --git a/drivers/media/platform/vimc/vimc-sensor.c
>> b/drivers/media/platform/vimc/vimc-sensor.c new file mode 100644
>> index 0000000..174e5dc
>> --- /dev/null
>> +++ b/drivers/media/platform/vimc/vimc-sensor.c
>
> [snip]
>
>> +static int vimc_thread_sen(void *data)
>> +{
>> +	unsigned int i;
>> +	struct vimc_sen_device *vsen = data;
>> +
>> +	set_freezable();
>> +
>> +	for (;;) {
>> +		try_to_freeze();
>> +		if (kthread_should_stop())
>> +			break;
>> +
>> +		memset(vsen->frame, 100, vsen->frame_size);
>> +
>> +		/* Send the frame to all source pads */
>> +		for (i = 0; i < vsen->sd.entity.num_pads; i++)
>> +			vimc_propagate_frame(vsen->dev,
>> +					     &vsen->sd.entity.pads[i],
>> +					     vsen->frame);
>> +
>> +		/* 60 frames per second */
>> +		schedule_timeout_interruptible(HZ/60);
>
> What can this be interrupted by ?
>
>> +	}
>> +
>> +	return 0;
>> +}
>
> [snip]
>
>> +struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
>> +					const char *const name,
>> +					u16 num_pads,
>> +					const unsigned long *pads_flag)
>> +{
>> +	int ret;
>> +	unsigned int i;
>> +	struct vimc_sen_device *vsen;
>> +
>> +	if (!name || (num_pads && !pads_flag))
>> +		return ERR_PTR(-EINVAL);
>
> I think you need at least one pad. Or actually, exactly one pad.
>
>> +	/* check if all pads are sources */
>> +	for (i = 0; i < num_pads; i++)
>> +		if (!(pads_flag[i] & MEDIA_PAD_FL_SOURCE))
>> +			return ERR_PTR(-EINVAL);
>> +
>> +	/* Allocate the vsen struct */
>> +	vsen = kzalloc(sizeof(*vsen), GFP_KERNEL);
>> +	if (!vsen)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	/* Link the vimc_sen_device struct with the v4l2 parent */
>> +	vsen->v4l2_dev = v4l2_dev;
>> +	/* Link the vimc_sen_device struct with the dev parent */
>> +	vsen->dev = v4l2_dev->dev;
>> +
>> +	/* Allocate the pads */
>> +	vsen->ved.pads = vimc_pads_init(num_pads, pads_flag);
>> +	if (IS_ERR(vsen->ved.pads)) {
>> +		ret = PTR_ERR(vsen->ved.pads);
>> +		goto err_free_vsen;
>> +	}
>> +
>> +	/* Initialize the media entity */
>> +	vsen->sd.entity.name = name;
>> +	ret = media_entity_pads_init(&vsen->sd.entity,
>> +				     num_pads, vsen->ved.pads);
>> +	if (ret)
>> +		goto err_clean_pads;
>> +
>> +	/* Set the active frame format (this is hardcoded for now) */
>> +	vsen->mbus_format.width = 640;
>> +	vsen->mbus_format.height = 480;
>> +	vsen->mbus_format.code = MEDIA_BUS_FMT_RGB888_1X24;
>> +	vsen->mbus_format.field = V4L2_FIELD_NONE;
>> +	vsen->mbus_format.colorspace = V4L2_COLORSPACE_SRGB;
>> +	vsen->mbus_format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
>> +	vsen->mbus_format.xfer_func = V4L2_XFER_FUNC_SRGB;
>> +
>> +	/* Fill the vimc_ent_device struct */
>> +	vsen->ved.destroy = vimc_sen_destroy;
>> +	vsen->ved.ent = &vsen->sd.entity;
>> +
>> +	/* Initialize the subdev */
>> +	v4l2_subdev_init(&vsen->sd, &vimc_sen_ops);
>> +	vsen->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
>> +	vsen->sd.entity.ops = &vimc_sen_mops;
>> +	vsen->sd.owner = THIS_MODULE;
>> +	strlcpy(vsen->sd.name, name, sizeof(vsen->sd.name));
>> +	v4l2_set_subdevdata(&vsen->sd, vsen);
>> +
>> +	/* Expose this subdev to user space */
>> +	vsen->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
>> +
>> +	/* Register the subdev with the v4l2 and the media framework */
>> +	ret = v4l2_device_register_subdev(vsen->v4l2_dev, &vsen->sd);
>> +	if (ret) {
>> +		dev_err(vsen->dev,
>> +			"subdev register failed (err=%d)\n", ret);
>> +		goto err_clean_m_ent;
>> +	}
>> +
>> +	return &vsen->ved;
>> +
>> +err_clean_m_ent:
>> +	media_entity_cleanup(&vsen->sd.entity);
>> +err_clean_pads:
>> +	vimc_pads_cleanup(vsen->ved.pads);
>> +err_free_vsen:
>> +	kfree(vsen);
>> +
>> +	return ERR_PTR(ret);
>> +}
>
> [snip]
>

Thank you for the review, I'll update the patch accordingly and 
re-submit it.

Helen

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

* Re: [PATCH v6] [media] vimc: Virtual Media Controller core, capture and sensor
  2016-09-04 20:02       ` [PATCH v6] " Helen Koike
  2016-09-06  7:33         ` Hans Verkuil
  2017-01-10 19:54         ` [PATCH v6] [media] vimc: Virtual Media Controller core, capture and sensor Laurent Pinchart
@ 2017-01-25 13:03         ` Sakari Ailus
  2017-01-25 15:31           ` Mauro Carvalho Chehab
  2017-03-24 22:11           ` Helen Koike
  2 siblings, 2 replies; 41+ messages in thread
From: Sakari Ailus @ 2017-01-25 13:03 UTC (permalink / raw)
  To: Helen Koike
  Cc: Hans Verkuil, linux-media, laurent.pinchart, jgebben, mchehab,
	Helen Fornazier

Hi Helen,

My apologies for the long review time!

Please see my comments below.

On Sun, Sep 04, 2016 at 05:02:18PM -0300, Helen Koike wrote:
> From: Helen Fornazier <helen.fornazier@gmail.com>
> 
> First version of the Virtual Media Controller.
> Add a simple version of the core of the driver, the capture and
> sensor nodes in the topology, generating a grey image in a hardcoded
> format.
> 
> Signed-off-by: Helen Koike <helen.koike@collabora.com>
> 
> ---
> 
> Patch based in media/master tree, and available here:
> https://github.com/helen-fornazier/opw-staging/tree/vimc/devel/vpu
> 
> Changes since v5:
> - Fix message "Entity type for entity Sensor A was not initialized!"
>   by initializing the sensor entity.function after the calling
>   v4l2_subded_init
> - populate device_caps in vimc_cap_create instead of in
>   vimc_cap_querycap
> - Fix typo in vimc-core.c s/de/the
> 
> Changes since v4:
> - coding style fixes
> - remove BUG_ON
> - change copyright to 2016
> - depens on VIDEO_V4L2_SUBDEV_API instead of select
> - remove assignement of V4L2_CAP_DEVICE_CAPS
> - s/vimc_cap_g_fmt_vid_cap/vimc_cap_fmt_vid_cap
> - fix vimc_cap_queue_setup declaration type
> - remove wrong buffer size check and add it in vimc_cap_buffer_prepare
> - vimc_cap_create: remove unecessary check if v4l2_dev or v4l2_dev->dev is null
> - vimc_cap_create: only allow a single pad
> - vimc_sen_create: only allow source pads, remove unecessary source pads
> checks in vimc_thread_sen
> 
> Changes since v3: fix rmmod crash and built-in compile
> - Re-order unregister calls in vimc_device_unregister function (remove
> rmmod issue)
> - Call media_device_unregister_entity in vimc_raw_destroy
> - Add depends on VIDEO_DEV && VIDEO_V4L2 and select VIDEOBUF2_VMALLOC
> - Check *nplanes in queue_setup (this remove v4l2-compliance fail)
> - Include <linux/kthread.h> in vimc-sensor.c
> - Move include of <linux/slab.h> from vimc-core.c to vimc-core.h
> - Generate 60 frames per sec instead of 1 in the sensor
> 
> Changes since v2: update with current media master tree
> - Add struct media_pipeline in vimc_cap_device
> - Use vb2_v4l2_buffer instead of vb2_buffer
> - Typos
> - Remove usage of entity->type and use entity->function instead
> - Remove fmt argument from queue setup
> - Use ktime_get_ns instead of v4l2_get_timestamp
> - Iterate over link's list using list_for_each_entry
> - Use media_device_{init, cleanup}
> - Use entity->use_count to keep track of entities instead of the old
> entity->id
> - Replace media_entity_init by media_entity_pads_init
> ---
>  drivers/media/platform/Kconfig             |   2 +
>  drivers/media/platform/Makefile            |   1 +
>  drivers/media/platform/vimc/Kconfig        |   7 +
>  drivers/media/platform/vimc/Makefile       |   3 +
>  drivers/media/platform/vimc/vimc-capture.c | 553 ++++++++++++++++++++++++++
>  drivers/media/platform/vimc/vimc-capture.h |  28 ++
>  drivers/media/platform/vimc/vimc-core.c    | 600 +++++++++++++++++++++++++++++
>  drivers/media/platform/vimc/vimc-core.h    |  57 +++
>  drivers/media/platform/vimc/vimc-sensor.c  | 279 ++++++++++++++
>  drivers/media/platform/vimc/vimc-sensor.h  |  28 ++
>  10 files changed, 1558 insertions(+)
>  create mode 100644 drivers/media/platform/vimc/Kconfig
>  create mode 100644 drivers/media/platform/vimc/Makefile
>  create mode 100644 drivers/media/platform/vimc/vimc-capture.c
>  create mode 100644 drivers/media/platform/vimc/vimc-capture.h
>  create mode 100644 drivers/media/platform/vimc/vimc-core.c
>  create mode 100644 drivers/media/platform/vimc/vimc-core.h
>  create mode 100644 drivers/media/platform/vimc/vimc-sensor.c
>  create mode 100644 drivers/media/platform/vimc/vimc-sensor.h
> 
> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> index 46f14dd..4a0577f 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -329,6 +329,8 @@ menuconfig V4L_TEST_DRIVERS
>  
>  if V4L_TEST_DRIVERS
>  
> +source "drivers/media/platform/vimc/Kconfig"
> +
>  source "drivers/media/platform/vivid/Kconfig"
>  
>  config VIDEO_VIM2M
> diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> index 536d1d8..dd4f658 100644
> --- a/drivers/media/platform/Makefile
> +++ b/drivers/media/platform/Makefile
> @@ -12,6 +12,7 @@ obj-$(CONFIG_VIDEO_OMAP3)	+= omap3isp/
>  
>  obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o
>  
> +obj-$(CONFIG_VIDEO_VIMC)		+= vimc/
>  obj-$(CONFIG_VIDEO_VIVID)		+= vivid/
>  obj-$(CONFIG_VIDEO_VIM2M)		+= vim2m.o
>  
> diff --git a/drivers/media/platform/vimc/Kconfig b/drivers/media/platform/vimc/Kconfig
> new file mode 100644
> index 0000000..b48819c
> --- /dev/null
> +++ b/drivers/media/platform/vimc/Kconfig
> @@ -0,0 +1,7 @@
> +config VIDEO_VIMC
> +	tristate "Virtual Media Controller Driver (VIMC)"
> +	depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
> +	select VIDEOBUF2_VMALLOC
> +	default n
> +	---help---
> +	  Skeleton driver for Virtual Media Controller

A bit more verbose description would be nice.

> diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile
> new file mode 100644
> index 0000000..c45195e
> --- /dev/null
> +++ b/drivers/media/platform/vimc/Makefile
> @@ -0,0 +1,3 @@
> +vimc-objs := vimc-core.o vimc-capture.o vimc-sensor.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
> new file mode 100644
> index 0000000..b7636cf
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-capture.c
> @@ -0,0 +1,553 @@
> +/*
> + * vimc-capture.c Virtual Media Controller Driver
> + *
> + * Copyright (C) 2016 Helen Koike F. <helen.fornazier@gmail.com>

2017 might be used now.

> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <media/videobuf2-core.h>
> +#include <media/videobuf2-vmalloc.h>
> +#include <media/v4l2-ioctl.h>

Numbers come before letters.

> +
> +#include "vimc-capture.h"
> +
> +struct vimc_cap_device {
> +	struct vimc_ent_device ved;
> +	struct video_device vdev;
> +	struct v4l2_device *v4l2_dev;

struct video_device also has a pointer to v4l2_dev. I wonder if that could
be used.

> +	struct device *dev;
> +	struct v4l2_pix_format format;
> +	struct vb2_queue queue;
> +	struct list_head buf_list;
> +	/*
> +	 * NOTE: in a real driver, a spin lock must be used to access the
> +	 * queue because the frames are generated from a hardware interruption
> +	 * and the isr is not allowed to sleep.
> +	 * Even if it is not necessary a spinlock in the vimc driver, we
> +	 * use it here as a code reference
> +	 */
> +	spinlock_t qlock;
> +	struct mutex lock;
> +	u32 sequence;
> +	struct media_pipeline pipe;
> +};
> +
> +struct vimc_cap_buffer {
> +	/*
> +	 * vb2_buffer must be the first element

vb2 or struct vb2_v4l2_buffer ?

> +	 * the videobuf2 framework will allocate this struct based on
> +	 * buf_struct_size and use the first sizeof(struct vb2_buffer) bytes of
> +	 * memory as a vb2_buffer
> +	 */
> +	struct vb2_v4l2_buffer vb2;
> +	struct list_head list;
> +};
> +
> +static int vimc_cap_querycap(struct file *file, void *priv,
> +			     struct v4l2_capability *cap)
> +{
> +	struct vimc_cap_device *vcap = video_drvdata(file);
> +
> +	strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
> +	strlcpy(cap->card, KBUILD_MODNAME, sizeof(cap->card));
> +	snprintf(cap->bus_info, sizeof(cap->bus_info),
> +		 "platform:%s", vcap->v4l2_dev->name);
> +
> +	return 0;
> +}
> +
> +static int vimc_cap_enum_input(struct file *file, void *priv,
> +			       struct v4l2_input *i)
> +{
> +	/* We only have one input */
> +	if (i->index > 0)
> +		return -EINVAL;
> +
> +	i->type = V4L2_INPUT_TYPE_CAMERA;
> +	strlcpy(i->name, "VIMC capture", sizeof(i->name));

Isn't this (*INPUT IOCTLs) something that should be handled in a sub-device
driver, such as a TV tuner?

> +
> +	return 0;
> +}
> +
> +static int vimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
> +{
> +	/* We only have one input */
> +	*i = 0;
> +	return 0;
> +}
> +
> +static int vimc_cap_s_input(struct file *file, void *priv, unsigned int i)
> +{
> +	/* We only have one input */
> +	return i ? -EINVAL : 0;
> +}
> +
> +static int vimc_cap_fmt_vid_cap(struct file *file, void *priv,
> +				  struct v4l2_format *f)
> +{
> +	struct vimc_cap_device *vcap = video_drvdata(file);
> +
> +	f->fmt.pix = vcap->format;
> +
> +	return 0;
> +}
> +
> +static int vimc_cap_enum_fmt_vid_cap(struct file *file, void *priv,
> +				     struct v4l2_fmtdesc *f)
> +{
> +	struct vimc_cap_device *vcap = video_drvdata(file);
> +
> +	if (f->index > 0)
> +		return -EINVAL;
> +
> +	/* We only support one format for now */
> +	f->pixelformat = vcap->format.pixelformat;
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_file_operations vimc_cap_fops = {
> +	.owner		= THIS_MODULE,
> +	.open		= v4l2_fh_open,
> +	.release	= vb2_fop_release,
> +	.read           = vb2_fop_read,
> +	.poll		= vb2_fop_poll,
> +	.unlocked_ioctl = video_ioctl2,
> +	.mmap           = vb2_fop_mmap,
> +};
> +
> +static const struct v4l2_ioctl_ops vimc_cap_ioctl_ops = {
> +	.vidioc_querycap = vimc_cap_querycap,
> +
> +	.vidioc_enum_input = vimc_cap_enum_input,
> +	.vidioc_g_input = vimc_cap_g_input,
> +	.vidioc_s_input = vimc_cap_s_input,
> +
> +	.vidioc_g_fmt_vid_cap = vimc_cap_fmt_vid_cap,
> +	.vidioc_s_fmt_vid_cap = vimc_cap_fmt_vid_cap,
> +	.vidioc_try_fmt_vid_cap = vimc_cap_fmt_vid_cap,
> +	.vidioc_enum_fmt_vid_cap = vimc_cap_enum_fmt_vid_cap,
> +
> +	.vidioc_reqbufs = vb2_ioctl_reqbufs,
> +	.vidioc_create_bufs = vb2_ioctl_create_bufs,
> +	.vidioc_querybuf = vb2_ioctl_querybuf,
> +	.vidioc_qbuf = vb2_ioctl_qbuf,
> +	.vidioc_dqbuf = vb2_ioctl_dqbuf,
> +	.vidioc_expbuf = vb2_ioctl_expbuf,
> +	.vidioc_streamon = vb2_ioctl_streamon,
> +	.vidioc_streamoff = vb2_ioctl_streamoff,
> +};
> +
> +static void vimc_cap_return_all_buffers(struct vimc_cap_device *vcap,
> +					enum vb2_buffer_state state)
> +{
> +	struct vimc_cap_buffer *vbuf, *node;
> +
> +	spin_lock(&vcap->qlock);
> +
> +	list_for_each_entry_safe(vbuf, node, &vcap->buf_list, list) {
> +		vb2_buffer_done(&vbuf->vb2.vb2_buf, state);
> +		list_del(&vbuf->list);
> +	}
> +
> +	spin_unlock(&vcap->qlock);
> +}
> +
> +static int vimc_cap_pipeline_s_stream(struct vimc_cap_device *vcap, int enable)
> +{
> +	int ret;
> +	struct media_pad *pad;
> +	struct media_entity *entity;
> +	struct v4l2_subdev *sd;
> +
> +	/* Start the stream in the subdevice direct connected */
> +	entity = &vcap->vdev.entity;
> +	pad = media_entity_remote_pad(&entity->pads[0]);

You could use vcap->vdev.entity.pads here, without assigning to entity. Then
entity would only be used to refer to the remove entity at the other end of
the link. Up to you.

> +
> +	/* If we are not connected to any subdev node, it means there is nothing

/*
 * Multi line
 * comment.
 */

> +	 * to activate on the pipe (e.g. we can be connected with an input
> +	 * device or we are not connected at all)
> +	 */
> +	if (pad == NULL || !is_media_entity_v4l2_subdev(pad->entity))
> +		return 0;
> +
> +	entity = pad->entity;
> +	sd = media_entity_to_v4l2_subdev(entity);

And if you used pad->entity here, you could remove the entity variable
altogether.

> +
> +	ret = v4l2_subdev_call(sd, video, s_stream, enable);
> +	if (ret && ret != -ENOIOCTLCMD)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count)
> +{
> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
> +	struct media_entity *entity;
> +	int ret;
> +
> +	vcap->sequence = 0;
> +
> +	/* Start the media pipeline */
> +	entity = &vcap->vdev.entity;
> +	ret = media_entity_pipeline_start(entity, &vcap->pipe);
> +	if (ret) {
> +		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
> +		return ret;
> +	}
> +
> +	/* Enable streaming from the pipe */
> +	ret = vimc_cap_pipeline_s_stream(vcap, 1);
> +	if (ret) {
> +		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * Stop the stream engine. Any remaining buffers in the stream queue are
> + * dequeued and passed on to the vb2 framework marked as STATE_ERROR.
> + */
> +static void vimc_cap_stop_streaming(struct vb2_queue *vq)
> +{
> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
> +
> +	/* Disable streaming from the pipe */
> +	vimc_cap_pipeline_s_stream(vcap, 0);
> +
> +	/* Stop the media pipeline */
> +	media_entity_pipeline_stop(&vcap->vdev.entity);
> +
> +	/* Release all active buffers */
> +	vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_ERROR);
> +}
> +
> +static void vimc_cap_buf_queue(struct vb2_buffer *vb2_buf)
> +{
> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb2_buf->vb2_queue);
> +	struct vimc_cap_buffer *buf = container_of(vb2_buf,
> +						   struct vimc_cap_buffer,
> +						   vb2.vb2_buf);
> +
> +	spin_lock(&vcap->qlock);
> +	list_add_tail(&buf->list, &vcap->buf_list);
> +	spin_unlock(&vcap->qlock);
> +}
> +
> +static int vimc_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
> +				unsigned int *nplanes, unsigned int sizes[],
> +				struct device *alloc_devs[])
> +{
> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
> +
> +	if (*nplanes)
> +		return sizes[0] < vcap->format.sizeimage ? -EINVAL : 0;

Why? The user could later reconfigure the device to use with a buffer of
this size. This might not be a concern for vimc, but the code from example
drivers tends to get copied around.

> +	/* We don't support multiplanes for now */
> +	*nplanes = 1;
> +	sizes[0] = vcap->format.sizeimage;
> +
> +	return 0;
> +}
> +
> +/*
> + * Prepare the buffer for queueing to the DMA engine: check and set the
> + * payload size.
> + */
> +static int vimc_cap_buffer_prepare(struct vb2_buffer *vb)
> +{
> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb->vb2_queue);
> +	unsigned long size = vcap->format.sizeimage;
> +
> +	if (vb2_plane_size(vb, 0) < size) {
> +		dev_err(vcap->dev, "buffer too small (%lu < %lu)\n",
> +			vb2_plane_size(vb, 0), size);
> +		return -EINVAL;
> +	}
> +
> +	vb2_set_plane_payload(vb, 0, size);

Newline here?

> +	return 0;
> +}
> +
> +static const struct vb2_ops vimc_cap_qops = {
> +	.start_streaming	= vimc_cap_start_streaming,
> +	.stop_streaming		= vimc_cap_stop_streaming,
> +	.buf_queue		= vimc_cap_buf_queue,
> +	.queue_setup		= vimc_cap_queue_setup,
> +	.buf_prepare		= vimc_cap_buffer_prepare,
> +	/*
> +	 * Since q->lock is set we can use the standard
> +	 * vb2_ops_wait_prepare/finish helper functions.
> +	 */
> +	.wait_prepare		= vb2_ops_wait_prepare,
> +	.wait_finish		= vb2_ops_wait_finish,
> +};
> +

> +/* NOTE: this function is a copy of v4l2_subdev_link_validate_get_format

/*
 * ...

> + * maybe the v4l2 function should be public
> + */
> +static int vimc_cap_v4l2_subdev_link_validate_get_format(struct media_pad *pad,
> +						struct v4l2_subdev_format *fmt)
> +{
> +	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity);
> +
> +	fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +	fmt->pad = pad->index;

A newline here would be nice.

> +	return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt);
> +}
> +
> +static int vimc_cap_link_validate(struct media_link *link)
> +{
> +	struct v4l2_subdev_format source_fmt;
> +	struct v4l2_pix_format *sink_fmt;
> +	struct vimc_cap_device *vcap;
> +	const struct vimc_pix_map *vpix;
> +	int ret;
> +
> +	/* Retrieve the video capture device */
> +	vcap = container_of(link->sink->entity,
> +			    struct vimc_cap_device, vdev.entity);

You could assign in variable declaration.

> +
> +	/* If the connected node is not a subdevice type

Ditto.

> +	 * then it's a raw node from vimc-core, ignore the link for now

What's a raw node in this case?

> +	 * TODO: remove this when there are no more raw nodes in the
> +	 * core and return error instead
> +	 */
> +	if (!is_media_entity_v4l2_subdev(link->source->entity))
> +		return 0;
> +
> +	/* Get the the format of the video device */
> +	sink_fmt = &vcap->format;

Same for sink_fmt, you could assign it in variable declaration.

> +
> +	/* Get the the format of the subdev */
> +	ret = vimc_cap_v4l2_subdev_link_validate_get_format(link->source,
> +							    &source_fmt);
> +	if (ret)
> +		return ret;
> +
> +	dev_dbg(vcap->dev,
> +		"cap: %s: link validate formats src:%dx%d %d sink:%dx%d %d\n",
> +		vcap->vdev.name,
> +		source_fmt.format.width, source_fmt.format.height,
> +		source_fmt.format.code,
> +		sink_fmt->width, sink_fmt->height,
> +		sink_fmt->pixelformat);
> +
> +	/* Validate the format */
> +
> +	vpix = vimc_pix_map_by_pixelformat(sink_fmt->pixelformat);
> +	if (!vpix)
> +		return -EINVAL;
> +
> +	/* The width, height and code must match. */
> +	if (source_fmt.format.width != sink_fmt->width
> +	    || source_fmt.format.height != sink_fmt->height
> +	    || vpix->code != source_fmt.format.code)
> +		return -EINVAL;
> +
> +	/* The field order must match, or the sink field order must be NONE
> +	 * to support interlaced hardware connected to bridges that support
> +	 * progressive formats only.
> +	 */
> +	if (source_fmt.format.field != sink_fmt->field &&
> +	    sink_fmt->field != V4L2_FIELD_NONE)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static const struct media_entity_operations vimc_cap_mops = {
> +	.link_validate		= vimc_cap_link_validate,
> +};
> +
> +static void vimc_cap_destroy(struct vimc_ent_device *ved)
> +{
> +	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
> +						    ved);
> +
> +	vb2_queue_release(&vcap->queue);
> +	media_entity_cleanup(ved->ent);
> +	video_unregister_device(&vcap->vdev);
> +	vimc_pads_cleanup(vcap->ved.pads);
> +	kfree(vcap);
> +}
> +
> +static void vimc_cap_process_frame(struct vimc_ent_device *ved,
> +				   struct media_pad *sink, const void *frame)
> +{
> +	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
> +						    ved);
> +	struct vimc_cap_buffer *vimc_buf;
> +	void *vbuf;
> +
> +	/* If the stream in this node is not active, just return */
> +	mutex_lock(&vcap->lock);
> +	if (!vb2_is_busy(&vcap->queue)) {
> +		mutex_unlock(&vcap->lock);
> +		return;
> +	}
> +	mutex_unlock(&vcap->lock);
> +
> +	spin_lock(&vcap->qlock);
> +
> +	/* Get the first entry of the list */
> +	vimc_buf = list_first_entry_or_null(&vcap->buf_list,
> +					    typeof(*vimc_buf), list);
> +	if (!vimc_buf) {
> +		spin_unlock(&vcap->qlock);
> +		return;
> +	}
> +
> +	/* Remove this entry from the list */
> +	list_del(&vimc_buf->list);
> +
> +	spin_unlock(&vcap->qlock);
> +
> +	/* Fill the buffer */
> +	vimc_buf->vb2.vb2_buf.timestamp = ktime_get_ns();
> +	vimc_buf->vb2.sequence = vcap->sequence++;
> +	vimc_buf->vb2.field = vcap->format.field;
> +
> +	vbuf = vb2_plane_vaddr(&vimc_buf->vb2.vb2_buf, 0);
> +
> +	memcpy(vbuf, frame, vcap->format.sizeimage);
> +
> +	/* Set it as ready */
> +	vb2_set_plane_payload(&vimc_buf->vb2.vb2_buf, 0,
> +			      vcap->format.sizeimage);
> +	vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE);
> +}
> +
> +struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
> +					const char *const name,
> +					u16 num_pads,
> +					const unsigned long *pads_flag)
> +{
> +	int ret;

It'd be nice to declare temporary variables, return values and such last.

> +	struct vb2_queue *q;
> +	struct video_device *vdev;
> +	struct vimc_cap_device *vcap;
> +	const struct vimc_pix_map *vpix;
> +
> +	/* Check entity configuration params

/*
 *

> +	 * NOTE: we only support a single sink pad
> +	 */
> +	if (!name || num_pads != 1 || !pads_flag ||
> +	    !(pads_flag[0] & MEDIA_PAD_FL_SINK))
> +		return ERR_PTR(-EINVAL);
> +
> +	/* Allocate the vimc_cap_device struct */
> +	vcap = kzalloc(sizeof(*vcap), GFP_KERNEL);
> +	if (!vcap)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/* Link the vimc_cap_device struct with v4l2 and dev parent */
> +	vcap->v4l2_dev = v4l2_dev;
> +	vcap->dev = v4l2_dev->dev;
> +
> +	/* Allocate the pads */
> +	vcap->ved.pads = vimc_pads_init(num_pads, pads_flag);
> +	if (IS_ERR(vcap->ved.pads)) {
> +		ret = PTR_ERR(vcap->ved.pads);
> +		goto err_free_vcap;
> +	}
> +
> +	/* Initialize the media entity */
> +	vcap->vdev.entity.name = name;
> +	vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L;
> +	ret = media_entity_pads_init(&vcap->vdev.entity,
> +				     num_pads, vcap->ved.pads);
> +	if (ret)
> +		goto err_clean_pads;
> +
> +	/* Initialize the lock */
> +	mutex_init(&vcap->lock);
> +
> +	/* Initialize the vb2 queue */
> +	q = &vcap->queue;
> +	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	q->io_modes = VB2_MMAP | VB2_DMABUF;
> +	q->drv_priv = vcap;
> +	q->buf_struct_size = sizeof(struct vimc_cap_buffer);
> +	q->ops = &vimc_cap_qops;
> +	q->mem_ops = &vb2_vmalloc_memops;
> +	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> +	q->min_buffers_needed = 2;
> +	q->lock = &vcap->lock;
> +
> +	ret = vb2_queue_init(q);
> +	if (ret) {
> +		dev_err(vcap->dev,
> +			"vb2 queue init failed (err=%d)\n", ret);
> +		goto err_clean_m_ent;
> +	}
> +
> +	/* Initialize buffer list and its lock */
> +	INIT_LIST_HEAD(&vcap->buf_list);
> +	spin_lock_init(&vcap->qlock);
> +
> +	/* Set the frame format (this is hardcoded for now) */
> +	vcap->format.width = 640;
> +	vcap->format.height = 480;
> +	vcap->format.pixelformat = V4L2_PIX_FMT_RGB24;
> +	vcap->format.field = V4L2_FIELD_NONE;
> +	vcap->format.colorspace = V4L2_COLORSPACE_SRGB;
> +
> +	vpix = vimc_pix_map_by_pixelformat(vcap->format.pixelformat);
> +
> +	vcap->format.bytesperline = vcap->format.width * vpix->bpp;
> +	vcap->format.sizeimage = vcap->format.bytesperline *
> +				 vcap->format.height;
> +
> +	/* Fill the vimc_ent_device struct */
> +	vcap->ved.destroy = vimc_cap_destroy;
> +	vcap->ved.ent = &vcap->vdev.entity;
> +	vcap->ved.process_frame = vimc_cap_process_frame;
> +
> +	/* Initialize the video_device struct */
> +	vdev = &vcap->vdev;
> +	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
> +	vdev->entity.ops = &vimc_cap_mops;
> +	vdev->release = video_device_release_empty;
> +	vdev->fops = &vimc_cap_fops;
> +	vdev->ioctl_ops = &vimc_cap_ioctl_ops;
> +	vdev->lock = &vcap->lock;
> +	vdev->queue = q;
> +	vdev->v4l2_dev = vcap->v4l2_dev;
> +	vdev->vfl_dir = VFL_DIR_RX;
> +	strlcpy(vdev->name, name, sizeof(vdev->name));
> +	video_set_drvdata(vdev, vcap);
> +
> +	/* Register the video_device with the v4l2 and the media framework */
> +	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
> +	if (ret) {
> +		dev_err(vcap->dev,
> +			"video register failed (err=%d)\n", ret);
> +		goto err_release_queue;
> +	}
> +
> +	return &vcap->ved;
> +
> +err_release_queue:
> +	vb2_queue_release(q);
> +err_clean_m_ent:
> +	media_entity_cleanup(&vcap->vdev.entity);
> +err_clean_pads:
> +	vimc_pads_cleanup(vcap->ved.pads);
> +err_free_vcap:
> +	kfree(vcap);
> +
> +	return ERR_PTR(ret);
> +}
> diff --git a/drivers/media/platform/vimc/vimc-capture.h b/drivers/media/platform/vimc/vimc-capture.h
> new file mode 100644
> index 0000000..bcf9fc37
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-capture.h
> @@ -0,0 +1,28 @@
> +/*
> + * vimc-capture.h Virtual Media Controller Driver
> + *
> + * Copyright (C) 2016 Helen Koike F. <helen.fornazier@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef _VIMC_CAPTURE_H_
> +#define _VIMC_CAPTURE_H_
> +
> +#include "vimc-core.h"
> +
> +struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
> +					const char *const name,
> +					u16 num_pads,
> +					const unsigned long *pads_flag);
> +
> +#endif
> diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c
> new file mode 100644
> index 0000000..0a2b91b
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-core.c
> @@ -0,0 +1,600 @@
> +/*
> + * vimc-core.c Virtual Media Controller Driver
> + *
> + * Copyright (C) 2016 Helen Koike F. <helen.fornazier@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <media/media-device.h>
> +#include <media/v4l2-device.h>
> +
> +#include "vimc-capture.h"
> +#include "vimc-core.h"
> +#include "vimc-sensor.h"
> +
> +#define VIMC_PDEV_NAME "vimc"
> +#define VIMC_MDEV_MODEL_NAME "VIMC MDEV"
> +
> +#define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) {	\
> +	.src_ent = src,						\
> +	.src_pad = srcpad,					\
> +	.sink_ent = sink,					\
> +	.sink_pad = sinkpad,					\
> +	.flags = link_flags,					\
> +}
> +
> +#define VIMC_PIX_MAP(_code, _bpp, _pixelformat) {	\
> +	.code = _code,					\
> +	.pixelformat = _pixelformat,			\
> +	.bpp = _bpp,					\
> +}
> +
> +struct vimc_device {
> +	/* The pipeline configuration

/*
 *

> +	 * (filled before calling vimc_device_register)
> +	 */
> +	const struct vimc_pipeline_config *pipe_cfg;
> +
> +	/* The Associated media_device parent */
> +	struct media_device mdev;
> +
> +	/* Internal v4l2 parent device*/
> +	struct v4l2_device v4l2_dev;
> +
> +	/* Internal topology */
> +	struct vimc_ent_device **ved;
> +};
> +
> +/**
> + * enum vimc_ent_node - Select the functionality of a node in the topology
> + * @VIMC_ENT_NODE_SENSOR:	A node of type SENSOR simulates a camera sensor
> + *				generating internal images in bayer format and
> + *				propagating those images through the pipeline
> + * @VIMC_ENT_NODE_CAPTURE:	A node of type CAPTURE is a v4l2 video_device
> + *				that exposes the received image from the
> + *				pipeline to the user space
> + * @VIMC_ENT_NODE_INPUT:	A node of type INPUT is a v4l2 video_device that
> + *				receives images from the user space and
> + *				propagates them through the pipeline
> + * @VIMC_ENT_NODE_DEBAYER:	A node type DEBAYER expects to receive a frame
> + *				in bayer format converts it to RGB
> + * @VIMC_ENT_NODE_SCALER:	A node of type SCALER scales the received image
> + *				by a given multiplier
> + *
> + * This enum is used in the entity configuration struct to allow the definition
> + * of a custom topology specifying the role of each node on it.
> + */
> +enum vimc_ent_node {
> +	VIMC_ENT_NODE_SENSOR,
> +	VIMC_ENT_NODE_CAPTURE,
> +	VIMC_ENT_NODE_INPUT,
> +	VIMC_ENT_NODE_DEBAYER,
> +	VIMC_ENT_NODE_SCALER,
> +};
> +
> +/* Structure which describes individual configuration for each entity */
> +struct vimc_ent_config {
> +	const char *name;
> +	size_t pads_qty;
> +	const unsigned long *pads_flag;
> +	enum vimc_ent_node node;
> +};
> +
> +/* Structure which describes links between entities */
> +struct vimc_ent_link {
> +	unsigned int src_ent;
> +	u16 src_pad;
> +	unsigned int sink_ent;
> +	u16 sink_pad;
> +	u32 flags;
> +};
> +
> +/* Structure which describes the whole topology */
> +struct vimc_pipeline_config {
> +	const struct vimc_ent_config *ents;
> +	size_t num_ents;
> +	const struct vimc_ent_link *links;
> +	size_t num_links;
> +};
> +
> +/* --------------------------------------------------------------------------
> + * Topology Configuration
> + */
> +
> +static const struct vimc_ent_config ent_config[] = {
> +	{
> +		.name = "Sensor A",
> +		.pads_qty = 1,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
> +		.node = VIMC_ENT_NODE_SENSOR,
> +	},
> +	{
> +		.name = "Sensor B",
> +		.pads_qty = 1,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
> +		.node = VIMC_ENT_NODE_SENSOR,
> +	},
> +	{
> +		.name = "Debayer A",
> +		.pads_qty = 2,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
> +						     MEDIA_PAD_FL_SOURCE},
> +		.node = VIMC_ENT_NODE_DEBAYER,
> +	},
> +	{
> +		.name = "Debayer B",
> +		.pads_qty = 2,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
> +						     MEDIA_PAD_FL_SOURCE},
> +		.node = VIMC_ENT_NODE_DEBAYER,
> +	},
> +	{
> +		.name = "Raw Capture 0",
> +		.pads_qty = 1,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
> +		.node = VIMC_ENT_NODE_CAPTURE,
> +	},
> +	{
> +		.name = "Raw Capture 1",
> +		.pads_qty = 1,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
> +		.node = VIMC_ENT_NODE_CAPTURE,
> +	},
> +	{
> +		.name = "RGB/YUV Input",
> +		.pads_qty = 1,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
> +		.node = VIMC_ENT_NODE_INPUT,
> +	},
> +	{
> +		.name = "Scaler",
> +		.pads_qty = 2,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
> +						     MEDIA_PAD_FL_SOURCE},
> +		.node = VIMC_ENT_NODE_SCALER,
> +	},
> +	{
> +		.name = "RGB/YUV Capture",
> +		.pads_qty = 1,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
> +		.node = VIMC_ENT_NODE_CAPTURE,
> +	},
> +};
> +
> +static const struct vimc_ent_link ent_links[] = {
> +	/* Link: Sensor A (Pad 0)->(Pad 0) Debayer A */
> +	VIMC_ENT_LINK(0, 0, 2, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
> +	/* Link: Sensor A (Pad 0)->(Pad 0) Raw Capture 0 */
> +	VIMC_ENT_LINK(0, 0, 4, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
> +	/* Link: Sensor B (Pad 0)->(Pad 0) Debayer B */
> +	VIMC_ENT_LINK(1, 0, 3, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
> +	/* Link: Sensor B (Pad 0)->(Pad 0) Raw Capture 1 */
> +	VIMC_ENT_LINK(1, 0, 5, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
> +	/* Link: Debayer A (Pad 1)->(Pad 0) Scaler */
> +	VIMC_ENT_LINK(2, 1, 7, 0, MEDIA_LNK_FL_ENABLED),
> +	/* Link: Debayer B (Pad 1)->(Pad 0) Scaler */
> +	VIMC_ENT_LINK(3, 1, 7, 0, 0),
> +	/* Link: RGB/YUV Input (Pad 0)->(Pad 0) Scaler */
> +	VIMC_ENT_LINK(6, 0, 7, 0, 0),
> +	/* Link: Scaler (Pad 1)->(Pad 0) RGB/YUV Capture */
> +	VIMC_ENT_LINK(7, 1, 8, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
> +};
> +
> +static const struct vimc_pipeline_config pipe_cfg = {
> +	.ents		= ent_config,
> +	.num_ents	= ARRAY_SIZE(ent_config),
> +	.links		= ent_links,
> +	.num_links	= ARRAY_SIZE(ent_links)
> +};
> +
> +/* -------------------------------------------------------------------------- */
> +
> +static void vimc_dev_release(struct device *dev)
> +{}
> +
> +static struct platform_device vimc_pdev = {
> +	.name		= VIMC_PDEV_NAME,
> +	.dev.release	= vimc_dev_release,
> +};
> +
> +const struct vimc_pix_map vimc_pix_map_list[] = {
> +	/* TODO: add all missing formats */
> +
> +	/* RGB formats */
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_BGR888_1X24, 3, V4L2_PIX_FMT_BGR24),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_RGB888_1X24, 3, V4L2_PIX_FMT_RGB24),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_ARGB8888_1X32, 4, V4L2_PIX_FMT_ARGB32),
> +
> +	/* Bayer formats */
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR8_1X8, 1, V4L2_PIX_FMT_SBGGR8),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG8_1X8, 1, V4L2_PIX_FMT_SGBRG8),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG8_1X8, 1, V4L2_PIX_FMT_SGRBG8),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB8_1X8, 1, V4L2_PIX_FMT_SRGGB8),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR10_1X10, 2, V4L2_PIX_FMT_SBGGR10),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG10_1X10, 2, V4L2_PIX_FMT_SGBRG10),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG10_1X10, 2, V4L2_PIX_FMT_SGRBG10),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB10_1X10, 2, V4L2_PIX_FMT_SRGGB10),
> +	/* 10bit raw bayer a-law compressed to 8 bits */
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8, 1, V4L2_PIX_FMT_SBGGR10ALAW8),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8, 1, V4L2_PIX_FMT_SGBRG10ALAW8),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8, 1, V4L2_PIX_FMT_SGRBG10ALAW8),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8, 1, V4L2_PIX_FMT_SRGGB10ALAW8),
> +	/* 10bit raw bayer DPCM compressed to 8 bits */
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, 1, V4L2_PIX_FMT_SBGGR10DPCM8),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, 1, V4L2_PIX_FMT_SGBRG10DPCM8),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, 1, V4L2_PIX_FMT_SGRBG10DPCM8),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, 1, V4L2_PIX_FMT_SRGGB10DPCM8),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR12_1X12, 2, V4L2_PIX_FMT_SBGGR12),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG12_1X12, 2, V4L2_PIX_FMT_SGBRG12),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG12_1X12, 2, V4L2_PIX_FMT_SGRBG12),
> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB12_1X12, 2, V4L2_PIX_FMT_SRGGB12),
> +
> +	/* End */
> +	{0, 0, 0}
> +};
> +
> +const struct vimc_pix_map *vimc_pix_map_by_code(u32 code)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; vimc_pix_map_list[i].bpp; i++) {
> +		if (vimc_pix_map_list[i].code == code)
> +			return &vimc_pix_map_list[i];
> +	}
> +	return NULL;
> +}
> +
> +const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; vimc_pix_map_list[i].bpp; i++) {
> +		if (vimc_pix_map_list[i].pixelformat == pixelformat)
> +			return &vimc_pix_map_list[i];
> +	}
> +	return NULL;
> +}
> +
> +int vimc_propagate_frame(struct device *dev,
> +			 struct media_pad *src, const void *frame)
> +{
> +	struct media_link *link;
> +	struct vimc_device *vimc = dev_get_drvdata(dev);
> +
> +	if (!(src->flags & MEDIA_PAD_FL_SOURCE))
> +		return -EINVAL;
> +
> +	/* Send this frame to all sink pads that are direct linked */
> +	list_for_each_entry(link, &src->entity->links, list) {
> +		if (link->source == src &&
> +		    (link->flags & MEDIA_LNK_FL_ENABLED)) {
> +			struct vimc_ent_device *ved;
> +
> +			ved = vimc->ved[link->sink->entity->use_count];
> +			if (ved && ved->process_frame)
> +				ved->process_frame(ved, link->sink, frame);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static void vimc_device_unregister(struct vimc_device *vimc)
> +{
> +	unsigned int i;
> +
> +	/* Cleanup (only initialized) entities */
> +	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
> +		if (vimc->ved[i] && vimc->ved[i]->destroy)
> +			vimc->ved[i]->destroy(vimc->ved[i]);
> +
> +		vimc->ved[i] = NULL;
> +	}
> +	v4l2_device_unregister(&vimc->v4l2_dev);
> +
> +	media_device_unregister(&vimc->mdev);
> +	media_device_cleanup(&vimc->mdev);
> +}
> +
> +/* Helper function to allocate and initialize pads */
> +struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag)
> +{
> +	unsigned int i;
> +	struct media_pad *pads;
> +
> +	/* Allocate memory for the pads */
> +	pads = kcalloc(num_pads, sizeof(*pads), GFP_KERNEL);
> +	if (!pads)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/* Initialize the pads */
> +	for (i = 0; i < num_pads; i++) {
> +		pads[i].index = i;
> +		pads[i].flags = pads_flag[i];
> +	}
> +
> +	return pads;
> +}
> +
> +/* TODO: remove this function when all the
> + * entities specific code are implemented
> + */
> +static void vimc_raw_destroy(struct vimc_ent_device *ved)
> +{
> +	media_device_unregister_entity(ved->ent);
> +
> +	media_entity_cleanup(ved->ent);
> +
> +	vimc_pads_cleanup(ved->pads);
> +
> +	kfree(ved->ent);
> +
> +	kfree(ved);
> +}
> +
> +/* TODO: remove this function when all the
> + * entities specific code are implemented
> + */
> +static struct vimc_ent_device *vimc_raw_create(struct v4l2_device *v4l2_dev,
> +					       const char *const name,
> +					       u16 num_pads,
> +					       const unsigned long *pads_flag)
> +{
> +	int ret;
> +	struct vimc_ent_device *ved;
> +
> +	/* Allocate the main ved struct */
> +	ved = kzalloc(sizeof(*ved), GFP_KERNEL);
> +	if (!ved)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/* Allocate the media entity */
> +	ved->ent = kzalloc(sizeof(*ved->ent), GFP_KERNEL);
> +	if (!ved->ent) {
> +		ret = -ENOMEM;
> +		goto err_free_ved;
> +	}
> +
> +	/* Allocate the pads */
> +	ved->pads = vimc_pads_init(num_pads, pads_flag);
> +	if (IS_ERR(ved->pads)) {
> +		ret = PTR_ERR(ved->pads);
> +		goto err_free_ent;
> +	}
> +
> +	/* Initialize the media entity */
> +	ved->ent->name = name;
> +	ved->ent->function = MEDIA_ENT_F_IO_V4L;
> +	ret = media_entity_pads_init(ved->ent, num_pads, ved->pads);
> +	if (ret)
> +		goto err_cleanup_pads;
> +
> +	/* Register the media entity */
> +	ret = media_device_register_entity(v4l2_dev->mdev, ved->ent);
> +	if (ret)
> +		goto err_cleanup_entity;
> +
> +	/* Fill out the destroy function and return */
> +	ved->destroy = vimc_raw_destroy;
> +	return ved;
> +
> +err_cleanup_entity:
> +	media_entity_cleanup(ved->ent);
> +err_cleanup_pads:
> +	vimc_pads_cleanup(ved->pads);
> +err_free_ent:
> +	kfree(ved->ent);
> +err_free_ved:
> +	kfree(ved);
> +
> +	return ERR_PTR(ret);
> +}
> +
> +static int vimc_device_register(struct vimc_device *vimc)
> +{
> +	unsigned int i;
> +	int ret = 0;
> +
> +	/* Allocate memory for the vimc_ent_devices pointers */
> +	vimc->ved = devm_kcalloc(vimc->mdev.dev, vimc->pipe_cfg->num_ents,
> +				 sizeof(*vimc->ved), GFP_KERNEL);
> +	if (!vimc->ved)
> +		return -ENOMEM;
> +
> +	/* Register the media device */
> +	ret = media_device_register(&vimc->mdev);
> +	if (ret) {
> +		dev_err(vimc->mdev.dev,
> +			"media device register failed (err=%d)\n", ret);
> +		return ret;
> +	}
> +
> +	/* Link the media device within the v4l2_device */
> +	vimc->v4l2_dev.mdev = &vimc->mdev;
> +
> +	/* Register the v4l2 struct */
> +	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);
> +		return ret;
> +	}
> +
> +	/* Initialize entities */
> +	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
> +		struct vimc_ent_device *(*create_func)(struct v4l2_device *,
> +						       const char *const,
> +						       u16,
> +						       const unsigned long *);
> +
> +		/* Register the specific node */
> +		switch (vimc->pipe_cfg->ents[i].node) {
> +		case VIMC_ENT_NODE_SENSOR:
> +			create_func = vimc_sen_create;
> +			break;
> +
> +		case VIMC_ENT_NODE_CAPTURE:
> +			create_func = vimc_cap_create;
> +			break;
> +
> +		/* TODO: Instantiate the specific topology node */
> +		case VIMC_ENT_NODE_INPUT:
> +		case VIMC_ENT_NODE_DEBAYER:
> +		case VIMC_ENT_NODE_SCALER:
> +		default:
> +			/* TODO: remove this when all the entities specific
> +			 * code are implemented
> +			 */
> +			create_func = vimc_raw_create;
> +			break;
> +		}
> +
> +		vimc->ved[i] = create_func(&vimc->v4l2_dev,
> +					   vimc->pipe_cfg->ents[i].name,
> +					   vimc->pipe_cfg->ents[i].pads_qty,
> +					   vimc->pipe_cfg->ents[i].pads_flag);
> +		if (IS_ERR(vimc->ved[i])) {
> +			ret = PTR_ERR(vimc->ved[i]);
> +			vimc->ved[i] = NULL;
> +			goto err;
> +		}
> +
> +		/* Set use_count to keep track of the ved structure */
> +		vimc->ved[i]->ent->use_count = i;
> +	}
> +
> +	/* 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];
> +
> +		ret = media_create_pad_link(vimc->ved[link->src_ent]->ent,
> +					    link->src_pad,
> +					    vimc->ved[link->sink_ent]->ent,
> +					    link->sink_pad,
> +					    link->flags);
> +		if (ret)
> +			goto err;
> +	}
> +
> +	/* 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);
> +		goto err;
> +	}
> +
> +	return 0;
> +
> +err:
> +	/* Destroy the so far created topology */
> +	vimc_device_unregister(vimc);
> +
> +	return ret;
> +}
> +
> +static int vimc_probe(struct platform_device *pdev)
> +{
> +	struct vimc_device *vimc;
> +	int ret;
> +
> +	/* Prepare the vimc topology structure */
> +
> +	/* Allocate memory for the vimc structure */
> +	vimc = devm_kzalloc(&pdev->dev, sizeof(*vimc), GFP_KERNEL);
> +	if (!vimc)
> +		return -ENOMEM;
> +
> +	/* Set the pipeline configuration struct */
> +	vimc->pipe_cfg = &pipe_cfg;
> +
> +	/* Initialize media device */
> +	strlcpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME,
> +		sizeof(vimc->mdev.model));
> +	vimc->mdev.dev = &pdev->dev;
> +	media_device_init(&vimc->mdev);
> +
> +	/* Create vimc topology */
> +	ret = vimc_device_register(vimc);
> +	if (ret) {
> +		dev_err(vimc->mdev.dev,
> +			"vimc device registration failed (err=%d)\n", ret);
> +		return ret;
> +	}
> +
> +	/* Link the topology object with the platform device object */
> +	platform_set_drvdata(pdev, vimc);
> +
> +	return 0;
> +}
> +
> +static int vimc_remove(struct platform_device *pdev)
> +{
> +	struct vimc_device *vimc;
> +
> +	/* Get the topology object linked with the platform device object */
> +	vimc = platform_get_drvdata(pdev);

You should assign in variable declaration.

> +
> +	/* Destroy all the topology */
> +	vimc_device_unregister(vimc);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver vimc_pdrv = {
> +	.probe		= vimc_probe,
> +	.remove		= vimc_remove,
> +	.driver		= {
> +		.name	= VIMC_PDEV_NAME,
> +	},
> +};
> +
> +static int __init vimc_init(void)
> +{
> +	int ret;
> +
> +	ret = platform_device_register(&vimc_pdev);

You could declare vimc_pdev closer to this.

> +	if (ret) {
> +		dev_err(&vimc_pdev.dev,
> +			"platform device registration failed (err=%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = platform_driver_register(&vimc_pdrv);
> +	if (ret) {
> +		dev_err(&vimc_pdev.dev,
> +			"platform driver registration failed (err=%d)\n", ret);
> +
> +		platform_device_unregister(&vimc_pdev);
> +	}
> +
> +	return ret;
> +}
> +
> +static void __exit vimc_exit(void)
> +{
> +	platform_driver_unregister(&vimc_pdrv);
> +
> +	platform_device_unregister(&vimc_pdev);
> +}
> +
> +module_init(vimc_init);
> +module_exit(vimc_exit);
> +
> +MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC)");
> +MODULE_AUTHOR("Helen Fornazier <helen.fornazier@gmail.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/media/platform/vimc/vimc-core.h b/drivers/media/platform/vimc/vimc-core.h
> new file mode 100644
> index 0000000..bd7abff
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-core.h
> @@ -0,0 +1,57 @@
> +/*
> + * vimc-core.h Virtual Media Controller Driver
> + *
> + * Copyright (C) 2016 Helen Koike F. <helen.fornazier@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef _VIMC_CORE_H_
> +#define _VIMC_CORE_H_
> +
> +#include <linux/slab.h>
> +#include <media/v4l2-device.h>
> +
> +/* Struct which matches the MEDIA_BUS_FMT_ codes with the corresponding

/*

> + * V4L2_PIX_FMT_ fourcc pixelformat and its bytes per pixel (bpp)
> + */
> +struct vimc_pix_map {
> +	unsigned int code;
> +	unsigned int bpp;
> +	u32 pixelformat;
> +};
> +extern const struct vimc_pix_map vimc_pix_map_list[];
> +
> +struct vimc_ent_device {
> +	struct media_entity *ent;
> +	struct media_pad *pads;
> +	void (*destroy)(struct vimc_ent_device *);
> +	void (*process_frame)(struct vimc_ent_device *ved,
> +			      struct media_pad *sink, const void *frame);
> +};
> +
> +int vimc_propagate_frame(struct device *dev,
> +			 struct media_pad *src, const void *frame);
> +
> +/* Helper functions to allocate/initialize pads and free them */
> +struct media_pad *vimc_pads_init(u16 num_pads,
> +				 const unsigned long *pads_flag);
> +static inline void vimc_pads_cleanup(struct media_pad *pads)
> +{
> +	kfree(pads);
> +}
> +
> +const struct vimc_pix_map *vimc_pix_map_by_code(u32 code);
> +
> +const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat);
> +
> +#endif
> diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c
> new file mode 100644
> index 0000000..174e5dc
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-sensor.c
> @@ -0,0 +1,279 @@
> +/*
> + * vimc-sensor.c Virtual Media Controller Driver
> + *
> + * Copyright (C) 2016 Helen Koike F. <helen.fornazier@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/freezer.h>
> +#include <linux/kthread.h>
> +#include <linux/vmalloc.h>
> +#include <linux/v4l2-mediabus.h>
> +#include <media/v4l2-subdev.h>

Alphabetic order, please.

> +
> +#include "vimc-sensor.h"
> +
> +struct vimc_sen_device {
> +	struct vimc_ent_device ved;
> +	struct v4l2_subdev sd;
> +	struct v4l2_device *v4l2_dev;
> +	struct device *dev;
> +	struct task_struct *kthread_sen;
> +	u8 *frame;
> +	/* The active format */
> +	struct v4l2_mbus_framefmt mbus_format;
> +	int frame_size;
> +};
> +
> +static int vimc_sen_enum_mbus_code(struct v4l2_subdev *sd,
> +				   struct v4l2_subdev_pad_config *cfg,
> +				   struct v4l2_subdev_mbus_code_enum *code)
> +{
> +	struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd);
> +
> +	/* Check if it is a valid pad */
> +	if (code->pad >= vsen->sd.entity.num_pads)
> +		return -EINVAL;

The check is already performed in v4l2-subdev.c.

> +
> +	code->code = vsen->mbus_format.code;
> +
> +	return 0;
> +}
> +
> +static int vimc_sen_enum_frame_size(struct v4l2_subdev *sd,
> +				    struct v4l2_subdev_pad_config *cfg,
> +				    struct v4l2_subdev_frame_size_enum *fse)
> +{
> +	struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd);
> +
> +	/* Check if it is a valid pad */
> +	if (fse->pad >= vsen->sd.entity.num_pads)
> +		return -EINVAL;

Same here.

> +
> +	/* TODO: Add support to other formats */
> +	if (fse->index)
> +		return -EINVAL;
> +
> +	/* TODO: Add support for other codes */
> +	if (fse->code != vsen->mbus_format.code)
> +		return -EINVAL;
> +
> +	fse->min_width = vsen->mbus_format.width;
> +	fse->max_width = vsen->mbus_format.width;
> +	fse->min_height = vsen->mbus_format.height;
> +	fse->max_height = vsen->mbus_format.height;
> +
> +	return 0;
> +}
> +
> +static int vimc_sen_get_fmt(struct v4l2_subdev *sd,
> +			    struct v4l2_subdev_pad_config *cfg,
> +			    struct v4l2_subdev_format *format)
> +{
> +	struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd);
> +
> +	format->format = vsen->mbus_format;
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = {
> +	.enum_mbus_code		= vimc_sen_enum_mbus_code,
> +	.enum_frame_size	= vimc_sen_enum_frame_size,
> +	.get_fmt		= vimc_sen_get_fmt,
> +	/* TODO: Add support to other formats */
> +	.set_fmt		= vimc_sen_get_fmt,
> +};
> +
> +/* media operations */
> +static const struct media_entity_operations vimc_sen_mops = {
> +	.link_validate = v4l2_subdev_link_validate,
> +};
> +
> +static int vimc_thread_sen(void *data)
> +{
> +	unsigned int i;
> +	struct vimc_sen_device *vsen = data;
> +
> +	set_freezable();
> +
> +	for (;;) {
> +		try_to_freeze();
> +		if (kthread_should_stop())
> +			break;
> +
> +		memset(vsen->frame, 100, vsen->frame_size);
> +
> +		/* Send the frame to all source pads */
> +		for (i = 0; i < vsen->sd.entity.num_pads; i++)
> +			vimc_propagate_frame(vsen->dev,
> +					     &vsen->sd.entity.pads[i],
> +					     vsen->frame);
> +
> +		/* 60 frames per second */
> +		schedule_timeout_interruptible(HZ/60);
> +	}
> +
> +	return 0;
> +}
> +
> +static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	int ret;
> +	struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd);
> +
> +	if (enable) {
> +		const struct vimc_pix_map *vpix;
> +
> +		if (vsen->kthread_sen)
> +			return -EINVAL;
> +
> +		/* Calculate the frame size */
> +		vpix = vimc_pix_map_by_code(vsen->mbus_format.code);
> +		vsen->frame_size = vsen->mbus_format.width * vpix->bpp *
> +				   vsen->mbus_format.height;
> +
> +		/* Allocate the frame buffer. Use vmalloc to be able to

/*
 *

> +		 * allocate a large amount of memory
> +		 */
> +		vsen->frame = vmalloc(vsen->frame_size);
> +		if (!vsen->frame)
> +			return -ENOMEM;
> +
> +		/* Initialize the image generator thread */
> +		vsen->kthread_sen = kthread_run(vimc_thread_sen, vsen,
> +						"%s-sen", vsen->v4l2_dev->name);

We have no other kthreads, just the video device name should be enough.

> +		if (IS_ERR(vsen->kthread_sen)) {
> +			v4l2_err(vsen->v4l2_dev, "kernel_thread() failed\n");

How about dev_err() instead? You use dev_*() macros elsewhere for printing.

> +			vfree(vsen->frame);
> +			vsen->frame = NULL;
> +			return PTR_ERR(vsen->kthread_sen);
> +		}
> +	} else {
> +		if (!vsen->kthread_sen)
> +			return -EINVAL;
> +
> +		/* Stop image generator */
> +		ret = kthread_stop(vsen->kthread_sen);
> +		vsen->kthread_sen = NULL;
> +
> +		vfree(vsen->frame);
> +		vsen->frame = NULL;
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +struct v4l2_subdev_video_ops vimc_sen_video_ops = {
> +	.s_stream = vimc_sen_s_stream,
> +};
> +
> +static const struct v4l2_subdev_ops vimc_sen_ops = {
> +	.pad = &vimc_sen_pad_ops,
> +	.video = &vimc_sen_video_ops,
> +};
> +
> +static void vimc_sen_destroy(struct vimc_ent_device *ved)
> +{
> +	struct vimc_sen_device *vsen = container_of(ved,
> +						struct vimc_sen_device, ved);
> +
> +	media_entity_cleanup(ved->ent);
> +	v4l2_device_unregister_subdev(&vsen->sd);

I'd say media_entity_cleanup() would belong here but it's an empty function
nowadays...

> +	kfree(vsen);
> +}
> +
> +struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
> +					const char *const name,
> +					u16 num_pads,
> +					const unsigned long *pads_flag)
> +{
> +	int ret;
> +	unsigned int i;
> +	struct vimc_sen_device *vsen;

vsen first?

> +
> +	if (!name || (num_pads && !pads_flag))
> +		return ERR_PTR(-EINVAL);
> +	/* check if all pads are sources */
> +	for (i = 0; i < num_pads; i++)
> +		if (!(pads_flag[i] & MEDIA_PAD_FL_SOURCE))
> +			return ERR_PTR(-EINVAL);
> +
> +	/* Allocate the vsen struct */
> +	vsen = kzalloc(sizeof(*vsen), GFP_KERNEL);
> +	if (!vsen)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/* Link the vimc_sen_device struct with the v4l2 parent */
> +	vsen->v4l2_dev = v4l2_dev;
> +	/* Link the vimc_sen_device struct with the dev parent */
> +	vsen->dev = v4l2_dev->dev;
> +
> +	/* Allocate the pads */
> +	vsen->ved.pads = vimc_pads_init(num_pads, pads_flag);
> +	if (IS_ERR(vsen->ved.pads)) {
> +		ret = PTR_ERR(vsen->ved.pads);
> +		goto err_free_vsen;
> +	}
> +
> +	/* Initialize the media entity */
> +	vsen->sd.entity.name = name;
> +	ret = media_entity_pads_init(&vsen->sd.entity,
> +				     num_pads, vsen->ved.pads);
> +	if (ret)
> +		goto err_clean_pads;
> +
> +	/* Set the active frame format (this is hardcoded for now) */
> +	vsen->mbus_format.width = 640;
> +	vsen->mbus_format.height = 480;
> +	vsen->mbus_format.code = MEDIA_BUS_FMT_RGB888_1X24;
> +	vsen->mbus_format.field = V4L2_FIELD_NONE;
> +	vsen->mbus_format.colorspace = V4L2_COLORSPACE_SRGB;
> +	vsen->mbus_format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
> +	vsen->mbus_format.xfer_func = V4L2_XFER_FUNC_SRGB;
> +
> +	/* Fill the vimc_ent_device struct */
> +	vsen->ved.destroy = vimc_sen_destroy;
> +	vsen->ved.ent = &vsen->sd.entity;
> +
> +	/* Initialize the subdev */
> +	v4l2_subdev_init(&vsen->sd, &vimc_sen_ops);
> +	vsen->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
> +	vsen->sd.entity.ops = &vimc_sen_mops;
> +	vsen->sd.owner = THIS_MODULE;
> +	strlcpy(vsen->sd.name, name, sizeof(vsen->sd.name));
> +	v4l2_set_subdevdata(&vsen->sd, vsen);
> +
> +	/* Expose this subdev to user space */
> +	vsen->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
> +
> +	/* Register the subdev with the v4l2 and the media framework */
> +	ret = v4l2_device_register_subdev(vsen->v4l2_dev, &vsen->sd);
> +	if (ret) {
> +		dev_err(vsen->dev,
> +			"subdev register failed (err=%d)\n", ret);
> +		goto err_clean_m_ent;
> +	}
> +
> +	return &vsen->ved;
> +
> +err_clean_m_ent:
> +	media_entity_cleanup(&vsen->sd.entity);
> +err_clean_pads:
> +	vimc_pads_cleanup(vsen->ved.pads);
> +err_free_vsen:
> +	kfree(vsen);
> +
> +	return ERR_PTR(ret);
> +}
> diff --git a/drivers/media/platform/vimc/vimc-sensor.h b/drivers/media/platform/vimc/vimc-sensor.h
> new file mode 100644
> index 0000000..ffeaf6c
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-sensor.h
> @@ -0,0 +1,28 @@
> +/*
> + * vimc-sensor.h Virtual Media Controller Driver
> + *
> + * Copyright (C) 2016 Helen Koike F. <helen.fornazier@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef _VIMC_SENSOR_H_
> +#define _VIMC_SENSOR_H_
> +
> +#include "vimc-core.h"
> +
> +struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
> +					const char *const name,
> +					u16 num_pads,
> +					const unsigned long *pads_flag);
> +
> +#endif

Looks very nice in general! I'll try this out soon.

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Re: [PATCH v6] [media] vimc: Virtual Media Controller core, capture and sensor
  2017-01-25 13:03         ` [PATCH v6] " Sakari Ailus
@ 2017-01-25 15:31           ` Mauro Carvalho Chehab
  2017-03-24 22:11           ` Helen Koike
  1 sibling, 0 replies; 41+ messages in thread
From: Mauro Carvalho Chehab @ 2017-01-25 15:31 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: Helen Koike, Hans Verkuil, linux-media, laurent.pinchart,
	jgebben, Helen Fornazier

I won't be doing a review on it yet... I'll try to do it on the next
version. Just a quick notice:

Em Wed, 25 Jan 2017 15:03:46 +0200
Sakari Ailus <sakari.ailus@iki.fi> escreveu:

> > + * Copyright (C) 2016 Helen Koike F. <helen.fornazier@gmail.com>  
> 
> 2017 might be used now.

Please keep 2016 as the year you did the initial development. It
makes sense to change it to:
	2016-2017

In order to reflect that you're also doing some work on it this year,
but preserving the initial date is important, IMHO.

-- 
Thanks,
Mauro

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

* Re: [PATCH v6] [media] vimc: Virtual Media Controller core, capture and sensor
  2017-01-11  1:30           ` Helen Koike
@ 2017-03-10 13:08             ` Hans Verkuil
  2017-03-10 13:42               ` Helen Koike
  0 siblings, 1 reply; 41+ messages in thread
From: Hans Verkuil @ 2017-03-10 13:08 UTC (permalink / raw)
  To: Helen Koike, Laurent Pinchart, Helen Koike
  Cc: linux-media, jgebben, mchehab, Helen Fornazier, Sakari Ailus

Hi Helen,

On 11/01/17 02:30, Helen Koike wrote:
> 
> Thank you for the review, I'll update the patch accordingly and re-submit it.
> 
> Helen

Do you know when you have a v7 ready?

We really need a vimc driver so people without hardware can actually fiddle around
with the MC.

See also my rant here (not directed at you!):

https://www.spinics.net/lists/kernel/msg2462009.html

Regards,

	Hans

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

* Re: [PATCH v6] [media] vimc: Virtual Media Controller core, capture and sensor
  2017-03-10 13:08             ` Hans Verkuil
@ 2017-03-10 13:42               ` Helen Koike
  2017-03-25 17:11                 ` [PATCH v7] " Helen Koike
  0 siblings, 1 reply; 41+ messages in thread
From: Helen Koike @ 2017-03-10 13:42 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-media, jgebben, mchehab, Helen Fornazier, Sakari Ailus,
	Laurent Pinchart, Helen Koike

Hi Hans,

On 2017-03-10 10:08 AM, Hans Verkuil wrote:
> Hi Helen,
>
> On 11/01/17 02:30, Helen Koike wrote:
> >
> > Thank you for the review, I'll update the patch accordingly and re-submit it.
> >
> > Helen
>
> Do you know when you have a v7 ready?

Thanks for your interest. I don't have the next version entirely ready yet but I'll work on it to send this next version asap and the other patches I have on top of it as well.

>
> We really need a vimc driver so people without hardware can actually fiddle around
> with the MC.
>
> See also my rant here (not directed at you!):
>
> https://www.spinics.net/lists/kernel/msg2462009.html
>
> Regards,
>
>     Hans
>
Helen

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

* Re: [PATCH v6] [media] vimc: Virtual Media Controller core, capture and sensor
  2017-01-25 13:03         ` [PATCH v6] " Sakari Ailus
  2017-01-25 15:31           ` Mauro Carvalho Chehab
@ 2017-03-24 22:11           ` Helen Koike
  2017-03-26 13:25             ` Sakari Ailus
  1 sibling, 1 reply; 41+ messages in thread
From: Helen Koike @ 2017-03-24 22:11 UTC (permalink / raw)
  To: Sakari Ailus, Helen Koike
  Cc: Hans Verkuil, linux-media, laurent.pinchart, jgebben, mchehab,
	Helen Fornazier

Hi Sakari,

Thanks for your review, I have some questions below:

On 2017-01-25 11:03 AM, Sakari Ailus wrote:
> Hi Helen,
>
> My apologies for the long review time!
>
> Please see my comments below.
>
> On Sun, Sep 04, 2016 at 05:02:18PM -0300, Helen Koike wrote:
>> From: Helen Fornazier <helen.fornazier@gmail.com>
>>
>> First version of the Virtual Media Controller.
>> Add a simple version of the core of the driver, the capture and
>> sensor nodes in the topology, generating a grey image in a hardcoded
>> format.
>>
>> Signed-off-by: Helen Koike <helen.koike@collabora.com>
>>
>> ---
>>
>> Patch based in media/master tree, and available here:
>> https://github.com/helen-fornazier/opw-staging/tree/vimc/devel/vpu
>>
>> Changes since v5:
>> - Fix message "Entity type for entity Sensor A was not initialized!"
>>   by initializing the sensor entity.function after the calling
>>   v4l2_subded_init
>> - populate device_caps in vimc_cap_create instead of in
>>   vimc_cap_querycap
>> - Fix typo in vimc-core.c s/de/the
>>
>> Changes since v4:
>> - coding style fixes
>> - remove BUG_ON
>> - change copyright to 2016
>> - depens on VIDEO_V4L2_SUBDEV_API instead of select
>> - remove assignement of V4L2_CAP_DEVICE_CAPS
>> - s/vimc_cap_g_fmt_vid_cap/vimc_cap_fmt_vid_cap
>> - fix vimc_cap_queue_setup declaration type
>> - remove wrong buffer size check and add it in vimc_cap_buffer_prepare
>> - vimc_cap_create: remove unecessary check if v4l2_dev or v4l2_dev->dev is null
>> - vimc_cap_create: only allow a single pad
>> - vimc_sen_create: only allow source pads, remove unecessary source pads
>> checks in vimc_thread_sen
>>
>> Changes since v3: fix rmmod crash and built-in compile
>> - Re-order unregister calls in vimc_device_unregister function (remove
>> rmmod issue)
>> - Call media_device_unregister_entity in vimc_raw_destroy
>> - Add depends on VIDEO_DEV && VIDEO_V4L2 and select VIDEOBUF2_VMALLOC
>> - Check *nplanes in queue_setup (this remove v4l2-compliance fail)
>> - Include <linux/kthread.h> in vimc-sensor.c
>> - Move include of <linux/slab.h> from vimc-core.c to vimc-core.h
>> - Generate 60 frames per sec instead of 1 in the sensor
>>
>> Changes since v2: update with current media master tree
>> - Add struct media_pipeline in vimc_cap_device
>> - Use vb2_v4l2_buffer instead of vb2_buffer
>> - Typos
>> - Remove usage of entity->type and use entity->function instead
>> - Remove fmt argument from queue setup
>> - Use ktime_get_ns instead of v4l2_get_timestamp
>> - Iterate over link's list using list_for_each_entry
>> - Use media_device_{init, cleanup}
>> - Use entity->use_count to keep track of entities instead of the old
>> entity->id
>> - Replace media_entity_init by media_entity_pads_init
>> ---
>>  drivers/media/platform/Kconfig             |   2 +
>>  drivers/media/platform/Makefile            |   1 +
>>  drivers/media/platform/vimc/Kconfig        |   7 +
>>  drivers/media/platform/vimc/Makefile       |   3 +
>>  drivers/media/platform/vimc/vimc-capture.c | 553 ++++++++++++++++++++++++++
>>  drivers/media/platform/vimc/vimc-capture.h |  28 ++
>>  drivers/media/platform/vimc/vimc-core.c    | 600 +++++++++++++++++++++++++++++
>>  drivers/media/platform/vimc/vimc-core.h    |  57 +++
>>  drivers/media/platform/vimc/vimc-sensor.c  | 279 ++++++++++++++
>>  drivers/media/platform/vimc/vimc-sensor.h  |  28 ++
>>  10 files changed, 1558 insertions(+)
>>  create mode 100644 drivers/media/platform/vimc/Kconfig
>>  create mode 100644 drivers/media/platform/vimc/Makefile
>>  create mode 100644 drivers/media/platform/vimc/vimc-capture.c
>>  create mode 100644 drivers/media/platform/vimc/vimc-capture.h
>>  create mode 100644 drivers/media/platform/vimc/vimc-core.c
>>  create mode 100644 drivers/media/platform/vimc/vimc-core.h
>>  create mode 100644 drivers/media/platform/vimc/vimc-sensor.c
>>  create mode 100644 drivers/media/platform/vimc/vimc-sensor.h
>>
>> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
>> index 46f14dd..4a0577f 100644
>> --- a/drivers/media/platform/Kconfig
>> +++ b/drivers/media/platform/Kconfig
>> @@ -329,6 +329,8 @@ menuconfig V4L_TEST_DRIVERS
>>
>>  if V4L_TEST_DRIVERS
>>
>> +source "drivers/media/platform/vimc/Kconfig"
>> +
>>  source "drivers/media/platform/vivid/Kconfig"
>>
>>  config VIDEO_VIM2M
>> diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
>> index 536d1d8..dd4f658 100644
>> --- a/drivers/media/platform/Makefile
>> +++ b/drivers/media/platform/Makefile
>> @@ -12,6 +12,7 @@ obj-$(CONFIG_VIDEO_OMAP3)	+= omap3isp/
>>
>>  obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o
>>
>> +obj-$(CONFIG_VIDEO_VIMC)		+= vimc/
>>  obj-$(CONFIG_VIDEO_VIVID)		+= vivid/
>>  obj-$(CONFIG_VIDEO_VIM2M)		+= vim2m.o
>>
>> diff --git a/drivers/media/platform/vimc/Kconfig b/drivers/media/platform/vimc/Kconfig
>> new file mode 100644
>> index 0000000..b48819c
>> --- /dev/null
>> +++ b/drivers/media/platform/vimc/Kconfig
>> @@ -0,0 +1,7 @@
>> +config VIDEO_VIMC
>> +	tristate "Virtual Media Controller Driver (VIMC)"
>> +	depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
>> +	select VIDEOBUF2_VMALLOC
>> +	default n
>> +	---help---
>> +	  Skeleton driver for Virtual Media Controller
>
> A bit more verbose description would be nice.
>
>> diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile
>> new file mode 100644
>> index 0000000..c45195e
>> --- /dev/null
>> +++ b/drivers/media/platform/vimc/Makefile
>> @@ -0,0 +1,3 @@
>> +vimc-objs := vimc-core.o vimc-capture.o vimc-sensor.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
>> new file mode 100644
>> index 0000000..b7636cf
>> --- /dev/null
>> +++ b/drivers/media/platform/vimc/vimc-capture.c
>> @@ -0,0 +1,553 @@
>> +/*
>> + * vimc-capture.c Virtual Media Controller Driver
>> + *
>> + * Copyright (C) 2016 Helen Koike F. <helen.fornazier@gmail.com>
>
> 2017 might be used now.
>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + */
>> +
>> +#include <media/videobuf2-core.h>
>> +#include <media/videobuf2-vmalloc.h>
>> +#include <media/v4l2-ioctl.h>
>
> Numbers come before letters.
>
>> +
>> +#include "vimc-capture.h"
>> +
>> +struct vimc_cap_device {
>> +	struct vimc_ent_device ved;
>> +	struct video_device vdev;
>> +	struct v4l2_device *v4l2_dev;
>
> struct video_device also has a pointer to v4l2_dev. I wonder if that could
> be used.
>
>> +	struct device *dev;
>> +	struct v4l2_pix_format format;
>> +	struct vb2_queue queue;
>> +	struct list_head buf_list;
>> +	/*
>> +	 * NOTE: in a real driver, a spin lock must be used to access the
>> +	 * queue because the frames are generated from a hardware interruption
>> +	 * and the isr is not allowed to sleep.
>> +	 * Even if it is not necessary a spinlock in the vimc driver, we
>> +	 * use it here as a code reference
>> +	 */
>> +	spinlock_t qlock;
>> +	struct mutex lock;
>> +	u32 sequence;
>> +	struct media_pipeline pipe;
>> +};
>> +
>> +struct vimc_cap_buffer {
>> +	/*
>> +	 * vb2_buffer must be the first element
>
> vb2 or struct vb2_v4l2_buffer ?
>
>> +	 * the videobuf2 framework will allocate this struct based on
>> +	 * buf_struct_size and use the first sizeof(struct vb2_buffer) bytes of
>> +	 * memory as a vb2_buffer
>> +	 */
>> +	struct vb2_v4l2_buffer vb2;
>> +	struct list_head list;
>> +};
>> +
>> +static int vimc_cap_querycap(struct file *file, void *priv,
>> +			     struct v4l2_capability *cap)
>> +{
>> +	struct vimc_cap_device *vcap = video_drvdata(file);
>> +
>> +	strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
>> +	strlcpy(cap->card, KBUILD_MODNAME, sizeof(cap->card));
>> +	snprintf(cap->bus_info, sizeof(cap->bus_info),
>> +		 "platform:%s", vcap->v4l2_dev->name);
>> +
>> +	return 0;
>> +}
>> +
>> +static int vimc_cap_enum_input(struct file *file, void *priv,
>> +			       struct v4l2_input *i)
>> +{
>> +	/* We only have one input */
>> +	if (i->index > 0)
>> +		return -EINVAL;
>> +
>> +	i->type = V4L2_INPUT_TYPE_CAMERA;
>> +	strlcpy(i->name, "VIMC capture", sizeof(i->name));
>
> Isn't this (*INPUT IOCTLs) something that should be handled in a sub-device
> driver, such as a TV tuner?


Can the ioctl VIDIOC_ENUMINPUT enumerate no inputs at all? Can I just 
return -EINVAL here in G_INPUT and S_INPUT as well?
I thought I had to enumerate at least one input, and between 
V4L2_INPUT_TYPE_TUNER and V4L2_INPUT_TYPE_CAMERA, this last
one seems more appropriated


>
>> +
>> +	return 0;
>> +}
>> +
>> +static int vimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
>> +{
>> +	/* We only have one input */
>> +	*i = 0;
>> +	return 0;
>> +}
>> +
>> +static int vimc_cap_s_input(struct file *file, void *priv, unsigned int i)
>> +{
>> +	/* We only have one input */
>> +	return i ? -EINVAL : 0;
>> +}
>> +
>> +static int vimc_cap_fmt_vid_cap(struct file *file, void *priv,
>> +				  struct v4l2_format *f)
>> +{
>> +	struct vimc_cap_device *vcap = video_drvdata(file);
>> +
>> +	f->fmt.pix = vcap->format;
>> +
>> +	return 0;
>> +}
>> +
>> +static int vimc_cap_enum_fmt_vid_cap(struct file *file, void *priv,
>> +				     struct v4l2_fmtdesc *f)
>> +{
>> +	struct vimc_cap_device *vcap = video_drvdata(file);
>> +
>> +	if (f->index > 0)
>> +		return -EINVAL;
>> +
>> +	/* We only support one format for now */
>> +	f->pixelformat = vcap->format.pixelformat;
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct v4l2_file_operations vimc_cap_fops = {
>> +	.owner		= THIS_MODULE,
>> +	.open		= v4l2_fh_open,
>> +	.release	= vb2_fop_release,
>> +	.read           = vb2_fop_read,
>> +	.poll		= vb2_fop_poll,
>> +	.unlocked_ioctl = video_ioctl2,
>> +	.mmap           = vb2_fop_mmap,
>> +};
>> +
>> +static const struct v4l2_ioctl_ops vimc_cap_ioctl_ops = {
>> +	.vidioc_querycap = vimc_cap_querycap,
>> +
>> +	.vidioc_enum_input = vimc_cap_enum_input,
>> +	.vidioc_g_input = vimc_cap_g_input,
>> +	.vidioc_s_input = vimc_cap_s_input,
>> +
>> +	.vidioc_g_fmt_vid_cap = vimc_cap_fmt_vid_cap,
>> +	.vidioc_s_fmt_vid_cap = vimc_cap_fmt_vid_cap,
>> +	.vidioc_try_fmt_vid_cap = vimc_cap_fmt_vid_cap,
>> +	.vidioc_enum_fmt_vid_cap = vimc_cap_enum_fmt_vid_cap,
>> +
>> +	.vidioc_reqbufs = vb2_ioctl_reqbufs,
>> +	.vidioc_create_bufs = vb2_ioctl_create_bufs,
>> +	.vidioc_querybuf = vb2_ioctl_querybuf,
>> +	.vidioc_qbuf = vb2_ioctl_qbuf,
>> +	.vidioc_dqbuf = vb2_ioctl_dqbuf,
>> +	.vidioc_expbuf = vb2_ioctl_expbuf,
>> +	.vidioc_streamon = vb2_ioctl_streamon,
>> +	.vidioc_streamoff = vb2_ioctl_streamoff,
>> +};
>> +
>> +static void vimc_cap_return_all_buffers(struct vimc_cap_device *vcap,
>> +					enum vb2_buffer_state state)
>> +{
>> +	struct vimc_cap_buffer *vbuf, *node;
>> +
>> +	spin_lock(&vcap->qlock);
>> +
>> +	list_for_each_entry_safe(vbuf, node, &vcap->buf_list, list) {
>> +		vb2_buffer_done(&vbuf->vb2.vb2_buf, state);
>> +		list_del(&vbuf->list);
>> +	}
>> +
>> +	spin_unlock(&vcap->qlock);
>> +}
>> +
>> +static int vimc_cap_pipeline_s_stream(struct vimc_cap_device *vcap, int enable)
>> +{
>> +	int ret;
>> +	struct media_pad *pad;
>> +	struct media_entity *entity;
>> +	struct v4l2_subdev *sd;
>> +
>> +	/* Start the stream in the subdevice direct connected */
>> +	entity = &vcap->vdev.entity;
>> +	pad = media_entity_remote_pad(&entity->pads[0]);
>
> You could use vcap->vdev.entity.pads here, without assigning to entity. Then
> entity would only be used to refer to the remove entity at the other end of
> the link. Up to you.
>
>> +
>> +	/* If we are not connected to any subdev node, it means there is nothing
>
> /*
>  * Multi line
>  * comment.
>  */
>
>> +	 * to activate on the pipe (e.g. we can be connected with an input
>> +	 * device or we are not connected at all)
>> +	 */
>> +	if (pad == NULL || !is_media_entity_v4l2_subdev(pad->entity))
>> +		return 0;
>> +
>> +	entity = pad->entity;
>> +	sd = media_entity_to_v4l2_subdev(entity);
>
> And if you used pad->entity here, you could remove the entity variable
> altogether.
>
>> +
>> +	ret = v4l2_subdev_call(sd, video, s_stream, enable);
>> +	if (ret && ret != -ENOIOCTLCMD)
>> +		return ret;
>> +
>> +	return 0;
>> +}
>> +
>> +static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count)
>> +{
>> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
>> +	struct media_entity *entity;
>> +	int ret;
>> +
>> +	vcap->sequence = 0;
>> +
>> +	/* Start the media pipeline */
>> +	entity = &vcap->vdev.entity;
>> +	ret = media_entity_pipeline_start(entity, &vcap->pipe);
>> +	if (ret) {
>> +		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
>> +		return ret;
>> +	}
>> +
>> +	/* Enable streaming from the pipe */
>> +	ret = vimc_cap_pipeline_s_stream(vcap, 1);
>> +	if (ret) {
>> +		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
>> +		return ret;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +/*
>> + * Stop the stream engine. Any remaining buffers in the stream queue are
>> + * dequeued and passed on to the vb2 framework marked as STATE_ERROR.
>> + */
>> +static void vimc_cap_stop_streaming(struct vb2_queue *vq)
>> +{
>> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
>> +
>> +	/* Disable streaming from the pipe */
>> +	vimc_cap_pipeline_s_stream(vcap, 0);
>> +
>> +	/* Stop the media pipeline */
>> +	media_entity_pipeline_stop(&vcap->vdev.entity);
>> +
>> +	/* Release all active buffers */
>> +	vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_ERROR);
>> +}
>> +
>> +static void vimc_cap_buf_queue(struct vb2_buffer *vb2_buf)
>> +{
>> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb2_buf->vb2_queue);
>> +	struct vimc_cap_buffer *buf = container_of(vb2_buf,
>> +						   struct vimc_cap_buffer,
>> +						   vb2.vb2_buf);
>> +
>> +	spin_lock(&vcap->qlock);
>> +	list_add_tail(&buf->list, &vcap->buf_list);
>> +	spin_unlock(&vcap->qlock);
>> +}
>> +
>> +static int vimc_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
>> +				unsigned int *nplanes, unsigned int sizes[],
>> +				struct device *alloc_devs[])
>> +{
>> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
>> +
>> +	if (*nplanes)
>> +		return sizes[0] < vcap->format.sizeimage ? -EINVAL : 0;
>
> Why? The user could later reconfigure the device to use with a buffer of
> this size. This might not be a concern for vimc, but the code from example
> drivers tends to get copied around.


This first version only support a fixed sizeimage, this will be changed 
in the next patch series where
I add support for multiple sizes


>
>> +	/* We don't support multiplanes for now */
>> +	*nplanes = 1;
>> +	sizes[0] = vcap->format.sizeimage;
>> +
>> +	return 0;
>> +}
>> +
>> +/*
>> + * Prepare the buffer for queueing to the DMA engine: check and set the
>> + * payload size.
>> + */
>> +static int vimc_cap_buffer_prepare(struct vb2_buffer *vb)
>> +{
>> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb->vb2_queue);
>> +	unsigned long size = vcap->format.sizeimage;
>> +
>> +	if (vb2_plane_size(vb, 0) < size) {
>> +		dev_err(vcap->dev, "buffer too small (%lu < %lu)\n",
>> +			vb2_plane_size(vb, 0), size);
>> +		return -EINVAL;
>> +	}
>> +
>> +	vb2_set_plane_payload(vb, 0, size);
>
> Newline here?
>
>> +	return 0;
>> +}
>> +
>> +static const struct vb2_ops vimc_cap_qops = {
>> +	.start_streaming	= vimc_cap_start_streaming,
>> +	.stop_streaming		= vimc_cap_stop_streaming,
>> +	.buf_queue		= vimc_cap_buf_queue,
>> +	.queue_setup		= vimc_cap_queue_setup,
>> +	.buf_prepare		= vimc_cap_buffer_prepare,
>> +	/*
>> +	 * Since q->lock is set we can use the standard
>> +	 * vb2_ops_wait_prepare/finish helper functions.
>> +	 */
>> +	.wait_prepare		= vb2_ops_wait_prepare,
>> +	.wait_finish		= vb2_ops_wait_finish,
>> +};
>> +
>
>> +/* NOTE: this function is a copy of v4l2_subdev_link_validate_get_format
>
> /*
>  * ...
>
>> + * maybe the v4l2 function should be public
>> + */
>> +static int vimc_cap_v4l2_subdev_link_validate_get_format(struct media_pad *pad,
>> +						struct v4l2_subdev_format *fmt)
>> +{
>> +	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity);
>> +
>> +	fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
>> +	fmt->pad = pad->index;
>
> A newline here would be nice.
>
>> +	return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt);
>> +}
>> +
>> +static int vimc_cap_link_validate(struct media_link *link)
>> +{
>> +	struct v4l2_subdev_format source_fmt;
>> +	struct v4l2_pix_format *sink_fmt;
>> +	struct vimc_cap_device *vcap;
>> +	const struct vimc_pix_map *vpix;
>> +	int ret;
>> +
>> +	/* Retrieve the video capture device */
>> +	vcap = container_of(link->sink->entity,
>> +			    struct vimc_cap_device, vdev.entity);
>
> You could assign in variable declaration.
>
>> +
>> +	/* If the connected node is not a subdevice type
>
> Ditto.
>
>> +	 * then it's a raw node from vimc-core, ignore the link for now
>
> What's a raw node in this case?
>


It's a dummy node created in vimc-core.c just to fill gaps in the 
topology in the place of other future nodes as
scaler, input, debayer filter that are not yet present.


>> +	 * TODO: remove this when there are no more raw nodes in the
>> +	 * core and return error instead
>> +	 */
>> +	if (!is_media_entity_v4l2_subdev(link->source->entity))
>> +		return 0;
>> +
>> +	/* Get the the format of the video device */
>> +	sink_fmt = &vcap->format;
>
> Same for sink_fmt, you could assign it in variable declaration.
>
>> +
>> +	/* Get the the format of the subdev */
>> +	ret = vimc_cap_v4l2_subdev_link_validate_get_format(link->source,
>> +							    &source_fmt);
>> +	if (ret)
>> +		return ret;
>> +
>> +	dev_dbg(vcap->dev,
>> +		"cap: %s: link validate formats src:%dx%d %d sink:%dx%d %d\n",
>> +		vcap->vdev.name,
>> +		source_fmt.format.width, source_fmt.format.height,
>> +		source_fmt.format.code,
>> +		sink_fmt->width, sink_fmt->height,
>> +		sink_fmt->pixelformat);
>> +
>> +	/* Validate the format */
>> +
>> +	vpix = vimc_pix_map_by_pixelformat(sink_fmt->pixelformat);
>> +	if (!vpix)
>> +		return -EINVAL;
>> +
>> +	/* The width, height and code must match. */
>> +	if (source_fmt.format.width != sink_fmt->width
>> +	    || source_fmt.format.height != sink_fmt->height
>> +	    || vpix->code != source_fmt.format.code)
>> +		return -EINVAL;
>> +
>> +	/* The field order must match, or the sink field order must be NONE
>> +	 * to support interlaced hardware connected to bridges that support
>> +	 * progressive formats only.
>> +	 */
>> +	if (source_fmt.format.field != sink_fmt->field &&
>> +	    sink_fmt->field != V4L2_FIELD_NONE)
>> +		return -EINVAL;
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct media_entity_operations vimc_cap_mops = {
>> +	.link_validate		= vimc_cap_link_validate,
>> +};
>> +
>> +static void vimc_cap_destroy(struct vimc_ent_device *ved)
>> +{
>> +	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
>> +						    ved);
>> +
>> +	vb2_queue_release(&vcap->queue);
>> +	media_entity_cleanup(ved->ent);
>> +	video_unregister_device(&vcap->vdev);
>> +	vimc_pads_cleanup(vcap->ved.pads);
>> +	kfree(vcap);
>> +}
>> +
>> +static void vimc_cap_process_frame(struct vimc_ent_device *ved,
>> +				   struct media_pad *sink, const void *frame)
>> +{
>> +	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
>> +						    ved);
>> +	struct vimc_cap_buffer *vimc_buf;
>> +	void *vbuf;
>> +
>> +	/* If the stream in this node is not active, just return */
>> +	mutex_lock(&vcap->lock);
>> +	if (!vb2_is_busy(&vcap->queue)) {
>> +		mutex_unlock(&vcap->lock);
>> +		return;
>> +	}
>> +	mutex_unlock(&vcap->lock);
>> +
>> +	spin_lock(&vcap->qlock);
>> +
>> +	/* Get the first entry of the list */
>> +	vimc_buf = list_first_entry_or_null(&vcap->buf_list,
>> +					    typeof(*vimc_buf), list);
>> +	if (!vimc_buf) {
>> +		spin_unlock(&vcap->qlock);
>> +		return;
>> +	}
>> +
>> +	/* Remove this entry from the list */
>> +	list_del(&vimc_buf->list);
>> +
>> +	spin_unlock(&vcap->qlock);
>> +
>> +	/* Fill the buffer */
>> +	vimc_buf->vb2.vb2_buf.timestamp = ktime_get_ns();
>> +	vimc_buf->vb2.sequence = vcap->sequence++;
>> +	vimc_buf->vb2.field = vcap->format.field;
>> +
>> +	vbuf = vb2_plane_vaddr(&vimc_buf->vb2.vb2_buf, 0);
>> +
>> +	memcpy(vbuf, frame, vcap->format.sizeimage);
>> +
>> +	/* Set it as ready */
>> +	vb2_set_plane_payload(&vimc_buf->vb2.vb2_buf, 0,
>> +			      vcap->format.sizeimage);
>> +	vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE);
>> +}
>> +
>> +struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
>> +					const char *const name,
>> +					u16 num_pads,
>> +					const unsigned long *pads_flag)
>> +{
>> +	int ret;
>
> It'd be nice to declare temporary variables, return values and such last.
>
>> +	struct vb2_queue *q;
>> +	struct video_device *vdev;
>> +	struct vimc_cap_device *vcap;
>> +	const struct vimc_pix_map *vpix;
>> +
>> +	/* Check entity configuration params
>
> /*
>  *
>
>> +	 * NOTE: we only support a single sink pad
>> +	 */
>> +	if (!name || num_pads != 1 || !pads_flag ||
>> +	    !(pads_flag[0] & MEDIA_PAD_FL_SINK))
>> +		return ERR_PTR(-EINVAL);
>> +
>> +	/* Allocate the vimc_cap_device struct */
>> +	vcap = kzalloc(sizeof(*vcap), GFP_KERNEL);
>> +	if (!vcap)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	/* Link the vimc_cap_device struct with v4l2 and dev parent */
>> +	vcap->v4l2_dev = v4l2_dev;
>> +	vcap->dev = v4l2_dev->dev;
>> +
>> +	/* Allocate the pads */
>> +	vcap->ved.pads = vimc_pads_init(num_pads, pads_flag);
>> +	if (IS_ERR(vcap->ved.pads)) {
>> +		ret = PTR_ERR(vcap->ved.pads);
>> +		goto err_free_vcap;
>> +	}
>> +
>> +	/* Initialize the media entity */
>> +	vcap->vdev.entity.name = name;
>> +	vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L;
>> +	ret = media_entity_pads_init(&vcap->vdev.entity,
>> +				     num_pads, vcap->ved.pads);
>> +	if (ret)
>> +		goto err_clean_pads;
>> +
>> +	/* Initialize the lock */
>> +	mutex_init(&vcap->lock);
>> +
>> +	/* Initialize the vb2 queue */
>> +	q = &vcap->queue;
>> +	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>> +	q->io_modes = VB2_MMAP | VB2_DMABUF;
>> +	q->drv_priv = vcap;
>> +	q->buf_struct_size = sizeof(struct vimc_cap_buffer);
>> +	q->ops = &vimc_cap_qops;
>> +	q->mem_ops = &vb2_vmalloc_memops;
>> +	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
>> +	q->min_buffers_needed = 2;
>> +	q->lock = &vcap->lock;
>> +
>> +	ret = vb2_queue_init(q);
>> +	if (ret) {
>> +		dev_err(vcap->dev,
>> +			"vb2 queue init failed (err=%d)\n", ret);
>> +		goto err_clean_m_ent;
>> +	}
>> +
>> +	/* Initialize buffer list and its lock */
>> +	INIT_LIST_HEAD(&vcap->buf_list);
>> +	spin_lock_init(&vcap->qlock);
>> +
>> +	/* Set the frame format (this is hardcoded for now) */
>> +	vcap->format.width = 640;
>> +	vcap->format.height = 480;
>> +	vcap->format.pixelformat = V4L2_PIX_FMT_RGB24;
>> +	vcap->format.field = V4L2_FIELD_NONE;
>> +	vcap->format.colorspace = V4L2_COLORSPACE_SRGB;
>> +
>> +	vpix = vimc_pix_map_by_pixelformat(vcap->format.pixelformat);
>> +
>> +	vcap->format.bytesperline = vcap->format.width * vpix->bpp;
>> +	vcap->format.sizeimage = vcap->format.bytesperline *
>> +				 vcap->format.height;
>> +
>> +	/* Fill the vimc_ent_device struct */
>> +	vcap->ved.destroy = vimc_cap_destroy;
>> +	vcap->ved.ent = &vcap->vdev.entity;
>> +	vcap->ved.process_frame = vimc_cap_process_frame;
>> +
>> +	/* Initialize the video_device struct */
>> +	vdev = &vcap->vdev;
>> +	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
>> +	vdev->entity.ops = &vimc_cap_mops;
>> +	vdev->release = video_device_release_empty;
>> +	vdev->fops = &vimc_cap_fops;
>> +	vdev->ioctl_ops = &vimc_cap_ioctl_ops;
>> +	vdev->lock = &vcap->lock;
>> +	vdev->queue = q;
>> +	vdev->v4l2_dev = vcap->v4l2_dev;
>> +	vdev->vfl_dir = VFL_DIR_RX;
>> +	strlcpy(vdev->name, name, sizeof(vdev->name));
>> +	video_set_drvdata(vdev, vcap);
>> +
>> +	/* Register the video_device with the v4l2 and the media framework */
>> +	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
>> +	if (ret) {
>> +		dev_err(vcap->dev,
>> +			"video register failed (err=%d)\n", ret);
>> +		goto err_release_queue;
>> +	}
>> +
>> +	return &vcap->ved;
>> +
>> +err_release_queue:
>> +	vb2_queue_release(q);
>> +err_clean_m_ent:
>> +	media_entity_cleanup(&vcap->vdev.entity);
>> +err_clean_pads:
>> +	vimc_pads_cleanup(vcap->ved.pads);
>> +err_free_vcap:
>> +	kfree(vcap);
>> +
>> +	return ERR_PTR(ret);
>> +}
>> diff --git a/drivers/media/platform/vimc/vimc-capture.h b/drivers/media/platform/vimc/vimc-capture.h
>> new file mode 100644
>> index 0000000..bcf9fc37
>> --- /dev/null
>> +++ b/drivers/media/platform/vimc/vimc-capture.h
>> @@ -0,0 +1,28 @@
>> +/*
>> + * vimc-capture.h Virtual Media Controller Driver
>> + *
>> + * Copyright (C) 2016 Helen Koike F. <helen.fornazier@gmail.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + */
>> +
>> +#ifndef _VIMC_CAPTURE_H_
>> +#define _VIMC_CAPTURE_H_
>> +
>> +#include "vimc-core.h"
>> +
>> +struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
>> +					const char *const name,
>> +					u16 num_pads,
>> +					const unsigned long *pads_flag);
>> +
>> +#endif
>> diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c
>> new file mode 100644
>> index 0000000..0a2b91b
>> --- /dev/null
>> +++ b/drivers/media/platform/vimc/vimc-core.c
>> @@ -0,0 +1,600 @@
>> +/*
>> + * vimc-core.c Virtual Media Controller Driver
>> + *
>> + * Copyright (C) 2016 Helen Koike F. <helen.fornazier@gmail.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + */
>> +
>> +#include <linux/init.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <media/media-device.h>
>> +#include <media/v4l2-device.h>
>> +
>> +#include "vimc-capture.h"
>> +#include "vimc-core.h"
>> +#include "vimc-sensor.h"
>> +
>> +#define VIMC_PDEV_NAME "vimc"
>> +#define VIMC_MDEV_MODEL_NAME "VIMC MDEV"
>> +
>> +#define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) {	\
>> +	.src_ent = src,						\
>> +	.src_pad = srcpad,					\
>> +	.sink_ent = sink,					\
>> +	.sink_pad = sinkpad,					\
>> +	.flags = link_flags,					\
>> +}
>> +
>> +#define VIMC_PIX_MAP(_code, _bpp, _pixelformat) {	\
>> +	.code = _code,					\
>> +	.pixelformat = _pixelformat,			\
>> +	.bpp = _bpp,					\
>> +}
>> +
>> +struct vimc_device {
>> +	/* The pipeline configuration
>
> /*
>  *
>
>> +	 * (filled before calling vimc_device_register)
>> +	 */
>> +	const struct vimc_pipeline_config *pipe_cfg;
>> +
>> +	/* The Associated media_device parent */
>> +	struct media_device mdev;
>> +
>> +	/* Internal v4l2 parent device*/
>> +	struct v4l2_device v4l2_dev;
>> +
>> +	/* Internal topology */
>> +	struct vimc_ent_device **ved;
>> +};
>> +
>> +/**
>> + * enum vimc_ent_node - Select the functionality of a node in the topology
>> + * @VIMC_ENT_NODE_SENSOR:	A node of type SENSOR simulates a camera sensor
>> + *				generating internal images in bayer format and
>> + *				propagating those images through the pipeline
>> + * @VIMC_ENT_NODE_CAPTURE:	A node of type CAPTURE is a v4l2 video_device
>> + *				that exposes the received image from the
>> + *				pipeline to the user space
>> + * @VIMC_ENT_NODE_INPUT:	A node of type INPUT is a v4l2 video_device that
>> + *				receives images from the user space and
>> + *				propagates them through the pipeline
>> + * @VIMC_ENT_NODE_DEBAYER:	A node type DEBAYER expects to receive a frame
>> + *				in bayer format converts it to RGB
>> + * @VIMC_ENT_NODE_SCALER:	A node of type SCALER scales the received image
>> + *				by a given multiplier
>> + *
>> + * This enum is used in the entity configuration struct to allow the definition
>> + * of a custom topology specifying the role of each node on it.
>> + */
>> +enum vimc_ent_node {
>> +	VIMC_ENT_NODE_SENSOR,
>> +	VIMC_ENT_NODE_CAPTURE,
>> +	VIMC_ENT_NODE_INPUT,
>> +	VIMC_ENT_NODE_DEBAYER,
>> +	VIMC_ENT_NODE_SCALER,
>> +};
>> +
>> +/* Structure which describes individual configuration for each entity */
>> +struct vimc_ent_config {
>> +	const char *name;
>> +	size_t pads_qty;
>> +	const unsigned long *pads_flag;
>> +	enum vimc_ent_node node;
>> +};
>> +
>> +/* Structure which describes links between entities */
>> +struct vimc_ent_link {
>> +	unsigned int src_ent;
>> +	u16 src_pad;
>> +	unsigned int sink_ent;
>> +	u16 sink_pad;
>> +	u32 flags;
>> +};
>> +
>> +/* Structure which describes the whole topology */
>> +struct vimc_pipeline_config {
>> +	const struct vimc_ent_config *ents;
>> +	size_t num_ents;
>> +	const struct vimc_ent_link *links;
>> +	size_t num_links;
>> +};
>> +
>> +/* --------------------------------------------------------------------------
>> + * Topology Configuration
>> + */
>> +
>> +static const struct vimc_ent_config ent_config[] = {
>> +	{
>> +		.name = "Sensor A",
>> +		.pads_qty = 1,
>> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
>> +		.node = VIMC_ENT_NODE_SENSOR,
>> +	},
>> +	{
>> +		.name = "Sensor B",
>> +		.pads_qty = 1,
>> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
>> +		.node = VIMC_ENT_NODE_SENSOR,
>> +	},
>> +	{
>> +		.name = "Debayer A",
>> +		.pads_qty = 2,
>> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
>> +						     MEDIA_PAD_FL_SOURCE},
>> +		.node = VIMC_ENT_NODE_DEBAYER,
>> +	},
>> +	{
>> +		.name = "Debayer B",
>> +		.pads_qty = 2,
>> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
>> +						     MEDIA_PAD_FL_SOURCE},
>> +		.node = VIMC_ENT_NODE_DEBAYER,
>> +	},
>> +	{
>> +		.name = "Raw Capture 0",
>> +		.pads_qty = 1,
>> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
>> +		.node = VIMC_ENT_NODE_CAPTURE,
>> +	},
>> +	{
>> +		.name = "Raw Capture 1",
>> +		.pads_qty = 1,
>> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
>> +		.node = VIMC_ENT_NODE_CAPTURE,
>> +	},
>> +	{
>> +		.name = "RGB/YUV Input",
>> +		.pads_qty = 1,
>> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
>> +		.node = VIMC_ENT_NODE_INPUT,
>> +	},
>> +	{
>> +		.name = "Scaler",
>> +		.pads_qty = 2,
>> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
>> +						     MEDIA_PAD_FL_SOURCE},
>> +		.node = VIMC_ENT_NODE_SCALER,
>> +	},
>> +	{
>> +		.name = "RGB/YUV Capture",
>> +		.pads_qty = 1,
>> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
>> +		.node = VIMC_ENT_NODE_CAPTURE,
>> +	},
>> +};
>> +
>> +static const struct vimc_ent_link ent_links[] = {
>> +	/* Link: Sensor A (Pad 0)->(Pad 0) Debayer A */
>> +	VIMC_ENT_LINK(0, 0, 2, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
>> +	/* Link: Sensor A (Pad 0)->(Pad 0) Raw Capture 0 */
>> +	VIMC_ENT_LINK(0, 0, 4, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
>> +	/* Link: Sensor B (Pad 0)->(Pad 0) Debayer B */
>> +	VIMC_ENT_LINK(1, 0, 3, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
>> +	/* Link: Sensor B (Pad 0)->(Pad 0) Raw Capture 1 */
>> +	VIMC_ENT_LINK(1, 0, 5, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
>> +	/* Link: Debayer A (Pad 1)->(Pad 0) Scaler */
>> +	VIMC_ENT_LINK(2, 1, 7, 0, MEDIA_LNK_FL_ENABLED),
>> +	/* Link: Debayer B (Pad 1)->(Pad 0) Scaler */
>> +	VIMC_ENT_LINK(3, 1, 7, 0, 0),
>> +	/* Link: RGB/YUV Input (Pad 0)->(Pad 0) Scaler */
>> +	VIMC_ENT_LINK(6, 0, 7, 0, 0),
>> +	/* Link: Scaler (Pad 1)->(Pad 0) RGB/YUV Capture */
>> +	VIMC_ENT_LINK(7, 1, 8, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
>> +};
>> +
>> +static const struct vimc_pipeline_config pipe_cfg = {
>> +	.ents		= ent_config,
>> +	.num_ents	= ARRAY_SIZE(ent_config),
>> +	.links		= ent_links,
>> +	.num_links	= ARRAY_SIZE(ent_links)
>> +};
>> +
>> +/* -------------------------------------------------------------------------- */
>> +
>> +static void vimc_dev_release(struct device *dev)
>> +{}
>> +
>> +static struct platform_device vimc_pdev = {
>> +	.name		= VIMC_PDEV_NAME,
>> +	.dev.release	= vimc_dev_release,
>> +};
>> +
>> +const struct vimc_pix_map vimc_pix_map_list[] = {
>> +	/* TODO: add all missing formats */
>> +
>> +	/* RGB formats */
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_BGR888_1X24, 3, V4L2_PIX_FMT_BGR24),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_RGB888_1X24, 3, V4L2_PIX_FMT_RGB24),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_ARGB8888_1X32, 4, V4L2_PIX_FMT_ARGB32),
>> +
>> +	/* Bayer formats */
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR8_1X8, 1, V4L2_PIX_FMT_SBGGR8),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG8_1X8, 1, V4L2_PIX_FMT_SGBRG8),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG8_1X8, 1, V4L2_PIX_FMT_SGRBG8),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB8_1X8, 1, V4L2_PIX_FMT_SRGGB8),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR10_1X10, 2, V4L2_PIX_FMT_SBGGR10),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG10_1X10, 2, V4L2_PIX_FMT_SGBRG10),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG10_1X10, 2, V4L2_PIX_FMT_SGRBG10),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB10_1X10, 2, V4L2_PIX_FMT_SRGGB10),
>> +	/* 10bit raw bayer a-law compressed to 8 bits */
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8, 1, V4L2_PIX_FMT_SBGGR10ALAW8),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8, 1, V4L2_PIX_FMT_SGBRG10ALAW8),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8, 1, V4L2_PIX_FMT_SGRBG10ALAW8),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8, 1, V4L2_PIX_FMT_SRGGB10ALAW8),
>> +	/* 10bit raw bayer DPCM compressed to 8 bits */
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, 1, V4L2_PIX_FMT_SBGGR10DPCM8),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, 1, V4L2_PIX_FMT_SGBRG10DPCM8),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, 1, V4L2_PIX_FMT_SGRBG10DPCM8),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, 1, V4L2_PIX_FMT_SRGGB10DPCM8),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SBGGR12_1X12, 2, V4L2_PIX_FMT_SBGGR12),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGBRG12_1X12, 2, V4L2_PIX_FMT_SGBRG12),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SGRBG12_1X12, 2, V4L2_PIX_FMT_SGRBG12),
>> +	VIMC_PIX_MAP(MEDIA_BUS_FMT_SRGGB12_1X12, 2, V4L2_PIX_FMT_SRGGB12),
>> +
>> +	/* End */
>> +	{0, 0, 0}
>> +};
>> +
>> +const struct vimc_pix_map *vimc_pix_map_by_code(u32 code)
>> +{
>> +	unsigned int i;
>> +
>> +	for (i = 0; vimc_pix_map_list[i].bpp; i++) {
>> +		if (vimc_pix_map_list[i].code == code)
>> +			return &vimc_pix_map_list[i];
>> +	}
>> +	return NULL;
>> +}
>> +
>> +const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat)
>> +{
>> +	unsigned int i;
>> +
>> +	for (i = 0; vimc_pix_map_list[i].bpp; i++) {
>> +		if (vimc_pix_map_list[i].pixelformat == pixelformat)
>> +			return &vimc_pix_map_list[i];
>> +	}
>> +	return NULL;
>> +}
>> +
>> +int vimc_propagate_frame(struct device *dev,
>> +			 struct media_pad *src, const void *frame)
>> +{
>> +	struct media_link *link;
>> +	struct vimc_device *vimc = dev_get_drvdata(dev);
>> +
>> +	if (!(src->flags & MEDIA_PAD_FL_SOURCE))
>> +		return -EINVAL;
>> +
>> +	/* Send this frame to all sink pads that are direct linked */
>> +	list_for_each_entry(link, &src->entity->links, list) {
>> +		if (link->source == src &&
>> +		    (link->flags & MEDIA_LNK_FL_ENABLED)) {
>> +			struct vimc_ent_device *ved;
>> +
>> +			ved = vimc->ved[link->sink->entity->use_count];
>> +			if (ved && ved->process_frame)
>> +				ved->process_frame(ved, link->sink, frame);
>> +		}
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static void vimc_device_unregister(struct vimc_device *vimc)
>> +{
>> +	unsigned int i;
>> +
>> +	/* Cleanup (only initialized) entities */
>> +	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
>> +		if (vimc->ved[i] && vimc->ved[i]->destroy)
>> +			vimc->ved[i]->destroy(vimc->ved[i]);
>> +
>> +		vimc->ved[i] = NULL;
>> +	}
>> +	v4l2_device_unregister(&vimc->v4l2_dev);
>> +
>> +	media_device_unregister(&vimc->mdev);
>> +	media_device_cleanup(&vimc->mdev);
>> +}
>> +
>> +/* Helper function to allocate and initialize pads */
>> +struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag)
>> +{
>> +	unsigned int i;
>> +	struct media_pad *pads;
>> +
>> +	/* Allocate memory for the pads */
>> +	pads = kcalloc(num_pads, sizeof(*pads), GFP_KERNEL);
>> +	if (!pads)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	/* Initialize the pads */
>> +	for (i = 0; i < num_pads; i++) {
>> +		pads[i].index = i;
>> +		pads[i].flags = pads_flag[i];
>> +	}
>> +
>> +	return pads;
>> +}
>> +
>> +/* TODO: remove this function when all the
>> + * entities specific code are implemented
>> + */
>> +static void vimc_raw_destroy(struct vimc_ent_device *ved)
>> +{
>> +	media_device_unregister_entity(ved->ent);
>> +
>> +	media_entity_cleanup(ved->ent);
>> +
>> +	vimc_pads_cleanup(ved->pads);
>> +
>> +	kfree(ved->ent);
>> +
>> +	kfree(ved);
>> +}
>> +
>> +/* TODO: remove this function when all the
>> + * entities specific code are implemented
>> + */
>> +static struct vimc_ent_device *vimc_raw_create(struct v4l2_device *v4l2_dev,
>> +					       const char *const name,
>> +					       u16 num_pads,
>> +					       const unsigned long *pads_flag)
>> +{
>> +	int ret;
>> +	struct vimc_ent_device *ved;
>> +
>> +	/* Allocate the main ved struct */
>> +	ved = kzalloc(sizeof(*ved), GFP_KERNEL);
>> +	if (!ved)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	/* Allocate the media entity */
>> +	ved->ent = kzalloc(sizeof(*ved->ent), GFP_KERNEL);
>> +	if (!ved->ent) {
>> +		ret = -ENOMEM;
>> +		goto err_free_ved;
>> +	}
>> +
>> +	/* Allocate the pads */
>> +	ved->pads = vimc_pads_init(num_pads, pads_flag);
>> +	if (IS_ERR(ved->pads)) {
>> +		ret = PTR_ERR(ved->pads);
>> +		goto err_free_ent;
>> +	}
>> +
>> +	/* Initialize the media entity */
>> +	ved->ent->name = name;
>> +	ved->ent->function = MEDIA_ENT_F_IO_V4L;
>> +	ret = media_entity_pads_init(ved->ent, num_pads, ved->pads);
>> +	if (ret)
>> +		goto err_cleanup_pads;
>> +
>> +	/* Register the media entity */
>> +	ret = media_device_register_entity(v4l2_dev->mdev, ved->ent);
>> +	if (ret)
>> +		goto err_cleanup_entity;
>> +
>> +	/* Fill out the destroy function and return */
>> +	ved->destroy = vimc_raw_destroy;
>> +	return ved;
>> +
>> +err_cleanup_entity:
>> +	media_entity_cleanup(ved->ent);
>> +err_cleanup_pads:
>> +	vimc_pads_cleanup(ved->pads);
>> +err_free_ent:
>> +	kfree(ved->ent);
>> +err_free_ved:
>> +	kfree(ved);
>> +
>> +	return ERR_PTR(ret);
>> +}
>> +
>> +static int vimc_device_register(struct vimc_device *vimc)
>> +{
>> +	unsigned int i;
>> +	int ret = 0;
>> +
>> +	/* Allocate memory for the vimc_ent_devices pointers */
>> +	vimc->ved = devm_kcalloc(vimc->mdev.dev, vimc->pipe_cfg->num_ents,
>> +				 sizeof(*vimc->ved), GFP_KERNEL);
>> +	if (!vimc->ved)
>> +		return -ENOMEM;
>> +
>> +	/* Register the media device */
>> +	ret = media_device_register(&vimc->mdev);
>> +	if (ret) {
>> +		dev_err(vimc->mdev.dev,
>> +			"media device register failed (err=%d)\n", ret);
>> +		return ret;
>> +	}
>> +
>> +	/* Link the media device within the v4l2_device */
>> +	vimc->v4l2_dev.mdev = &vimc->mdev;
>> +
>> +	/* Register the v4l2 struct */
>> +	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);
>> +		return ret;
>> +	}
>> +
>> +	/* Initialize entities */
>> +	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
>> +		struct vimc_ent_device *(*create_func)(struct v4l2_device *,
>> +						       const char *const,
>> +						       u16,
>> +						       const unsigned long *);
>> +
>> +		/* Register the specific node */
>> +		switch (vimc->pipe_cfg->ents[i].node) {
>> +		case VIMC_ENT_NODE_SENSOR:
>> +			create_func = vimc_sen_create;
>> +			break;
>> +
>> +		case VIMC_ENT_NODE_CAPTURE:
>> +			create_func = vimc_cap_create;
>> +			break;
>> +
>> +		/* TODO: Instantiate the specific topology node */
>> +		case VIMC_ENT_NODE_INPUT:
>> +		case VIMC_ENT_NODE_DEBAYER:
>> +		case VIMC_ENT_NODE_SCALER:
>> +		default:
>> +			/* TODO: remove this when all the entities specific
>> +			 * code are implemented
>> +			 */
>> +			create_func = vimc_raw_create;
>> +			break;
>> +		}
>> +
>> +		vimc->ved[i] = create_func(&vimc->v4l2_dev,
>> +					   vimc->pipe_cfg->ents[i].name,
>> +					   vimc->pipe_cfg->ents[i].pads_qty,
>> +					   vimc->pipe_cfg->ents[i].pads_flag);
>> +		if (IS_ERR(vimc->ved[i])) {
>> +			ret = PTR_ERR(vimc->ved[i]);
>> +			vimc->ved[i] = NULL;
>> +			goto err;
>> +		}
>> +
>> +		/* Set use_count to keep track of the ved structure */
>> +		vimc->ved[i]->ent->use_count = i;
>> +	}
>> +
>> +	/* 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];
>> +
>> +		ret = media_create_pad_link(vimc->ved[link->src_ent]->ent,
>> +					    link->src_pad,
>> +					    vimc->ved[link->sink_ent]->ent,
>> +					    link->sink_pad,
>> +					    link->flags);
>> +		if (ret)
>> +			goto err;
>> +	}
>> +
>> +	/* 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);
>> +		goto err;
>> +	}
>> +
>> +	return 0;
>> +
>> +err:
>> +	/* Destroy the so far created topology */
>> +	vimc_device_unregister(vimc);
>> +
>> +	return ret;
>> +}
>> +
>> +static int vimc_probe(struct platform_device *pdev)
>> +{
>> +	struct vimc_device *vimc;
>> +	int ret;
>> +
>> +	/* Prepare the vimc topology structure */
>> +
>> +	/* Allocate memory for the vimc structure */
>> +	vimc = devm_kzalloc(&pdev->dev, sizeof(*vimc), GFP_KERNEL);
>> +	if (!vimc)
>> +		return -ENOMEM;
>> +
>> +	/* Set the pipeline configuration struct */
>> +	vimc->pipe_cfg = &pipe_cfg;
>> +
>> +	/* Initialize media device */
>> +	strlcpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME,
>> +		sizeof(vimc->mdev.model));
>> +	vimc->mdev.dev = &pdev->dev;
>> +	media_device_init(&vimc->mdev);
>> +
>> +	/* Create vimc topology */
>> +	ret = vimc_device_register(vimc);
>> +	if (ret) {
>> +		dev_err(vimc->mdev.dev,
>> +			"vimc device registration failed (err=%d)\n", ret);
>> +		return ret;
>> +	}
>> +
>> +	/* Link the topology object with the platform device object */
>> +	platform_set_drvdata(pdev, vimc);
>> +
>> +	return 0;
>> +}
>> +
>> +static int vimc_remove(struct platform_device *pdev)
>> +{
>> +	struct vimc_device *vimc;
>> +
>> +	/* Get the topology object linked with the platform device object */
>> +	vimc = platform_get_drvdata(pdev);
>
> You should assign in variable declaration.
>
>> +
>> +	/* Destroy all the topology */
>> +	vimc_device_unregister(vimc);
>> +
>> +	return 0;
>> +}
>> +
>> +static struct platform_driver vimc_pdrv = {
>> +	.probe		= vimc_probe,
>> +	.remove		= vimc_remove,
>> +	.driver		= {
>> +		.name	= VIMC_PDEV_NAME,
>> +	},
>> +};
>> +
>> +static int __init vimc_init(void)
>> +{
>> +	int ret;
>> +
>> +	ret = platform_device_register(&vimc_pdev);
>
> You could declare vimc_pdev closer to this.
>
>> +	if (ret) {
>> +		dev_err(&vimc_pdev.dev,
>> +			"platform device registration failed (err=%d)\n", ret);
>> +		return ret;
>> +	}
>> +
>> +	ret = platform_driver_register(&vimc_pdrv);
>> +	if (ret) {
>> +		dev_err(&vimc_pdev.dev,
>> +			"platform driver registration failed (err=%d)\n", ret);
>> +
>> +		platform_device_unregister(&vimc_pdev);
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static void __exit vimc_exit(void)
>> +{
>> +	platform_driver_unregister(&vimc_pdrv);
>> +
>> +	platform_device_unregister(&vimc_pdev);
>> +}
>> +
>> +module_init(vimc_init);
>> +module_exit(vimc_exit);
>> +
>> +MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC)");
>> +MODULE_AUTHOR("Helen Fornazier <helen.fornazier@gmail.com>");
>> +MODULE_LICENSE("GPL");
>> diff --git a/drivers/media/platform/vimc/vimc-core.h b/drivers/media/platform/vimc/vimc-core.h
>> new file mode 100644
>> index 0000000..bd7abff
>> --- /dev/null
>> +++ b/drivers/media/platform/vimc/vimc-core.h
>> @@ -0,0 +1,57 @@
>> +/*
>> + * vimc-core.h Virtual Media Controller Driver
>> + *
>> + * Copyright (C) 2016 Helen Koike F. <helen.fornazier@gmail.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + */
>> +
>> +#ifndef _VIMC_CORE_H_
>> +#define _VIMC_CORE_H_
>> +
>> +#include <linux/slab.h>
>> +#include <media/v4l2-device.h>
>> +
>> +/* Struct which matches the MEDIA_BUS_FMT_ codes with the corresponding
>
> /*
>
>> + * V4L2_PIX_FMT_ fourcc pixelformat and its bytes per pixel (bpp)
>> + */
>> +struct vimc_pix_map {
>> +	unsigned int code;
>> +	unsigned int bpp;
>> +	u32 pixelformat;
>> +};
>> +extern const struct vimc_pix_map vimc_pix_map_list[];
>> +
>> +struct vimc_ent_device {
>> +	struct media_entity *ent;
>> +	struct media_pad *pads;
>> +	void (*destroy)(struct vimc_ent_device *);
>> +	void (*process_frame)(struct vimc_ent_device *ved,
>> +			      struct media_pad *sink, const void *frame);
>> +};
>> +
>> +int vimc_propagate_frame(struct device *dev,
>> +			 struct media_pad *src, const void *frame);
>> +
>> +/* Helper functions to allocate/initialize pads and free them */
>> +struct media_pad *vimc_pads_init(u16 num_pads,
>> +				 const unsigned long *pads_flag);
>> +static inline void vimc_pads_cleanup(struct media_pad *pads)
>> +{
>> +	kfree(pads);
>> +}
>> +
>> +const struct vimc_pix_map *vimc_pix_map_by_code(u32 code);
>> +
>> +const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat);
>> +
>> +#endif
>> diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c
>> new file mode 100644
>> index 0000000..174e5dc
>> --- /dev/null
>> +++ b/drivers/media/platform/vimc/vimc-sensor.c
>> @@ -0,0 +1,279 @@
>> +/*
>> + * vimc-sensor.c Virtual Media Controller Driver
>> + *
>> + * Copyright (C) 2016 Helen Koike F. <helen.fornazier@gmail.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + */
>> +
>> +#include <linux/freezer.h>
>> +#include <linux/kthread.h>
>> +#include <linux/vmalloc.h>
>> +#include <linux/v4l2-mediabus.h>
>> +#include <media/v4l2-subdev.h>
>
> Alphabetic order, please.
>
>> +
>> +#include "vimc-sensor.h"
>> +
>> +struct vimc_sen_device {
>> +	struct vimc_ent_device ved;
>> +	struct v4l2_subdev sd;
>> +	struct v4l2_device *v4l2_dev;
>> +	struct device *dev;
>> +	struct task_struct *kthread_sen;
>> +	u8 *frame;
>> +	/* The active format */
>> +	struct v4l2_mbus_framefmt mbus_format;
>> +	int frame_size;
>> +};
>> +
>> +static int vimc_sen_enum_mbus_code(struct v4l2_subdev *sd,
>> +				   struct v4l2_subdev_pad_config *cfg,
>> +				   struct v4l2_subdev_mbus_code_enum *code)
>> +{
>> +	struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd);
>> +
>> +	/* Check if it is a valid pad */
>> +	if (code->pad >= vsen->sd.entity.num_pads)
>> +		return -EINVAL;
>
> The check is already performed in v4l2-subdev.c.
>
>> +
>> +	code->code = vsen->mbus_format.code;
>> +
>> +	return 0;
>> +}
>> +
>> +static int vimc_sen_enum_frame_size(struct v4l2_subdev *sd,
>> +				    struct v4l2_subdev_pad_config *cfg,
>> +				    struct v4l2_subdev_frame_size_enum *fse)
>> +{
>> +	struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd);
>> +
>> +	/* Check if it is a valid pad */
>> +	if (fse->pad >= vsen->sd.entity.num_pads)
>> +		return -EINVAL;
>
> Same here.
>
>> +
>> +	/* TODO: Add support to other formats */
>> +	if (fse->index)
>> +		return -EINVAL;
>> +
>> +	/* TODO: Add support for other codes */
>> +	if (fse->code != vsen->mbus_format.code)
>> +		return -EINVAL;
>> +
>> +	fse->min_width = vsen->mbus_format.width;
>> +	fse->max_width = vsen->mbus_format.width;
>> +	fse->min_height = vsen->mbus_format.height;
>> +	fse->max_height = vsen->mbus_format.height;
>> +
>> +	return 0;
>> +}
>> +
>> +static int vimc_sen_get_fmt(struct v4l2_subdev *sd,
>> +			    struct v4l2_subdev_pad_config *cfg,
>> +			    struct v4l2_subdev_format *format)
>> +{
>> +	struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd);
>> +
>> +	format->format = vsen->mbus_format;
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = {
>> +	.enum_mbus_code		= vimc_sen_enum_mbus_code,
>> +	.enum_frame_size	= vimc_sen_enum_frame_size,
>> +	.get_fmt		= vimc_sen_get_fmt,
>> +	/* TODO: Add support to other formats */
>> +	.set_fmt		= vimc_sen_get_fmt,
>> +};
>> +
>> +/* media operations */
>> +static const struct media_entity_operations vimc_sen_mops = {
>> +	.link_validate = v4l2_subdev_link_validate,
>> +};
>> +
>> +static int vimc_thread_sen(void *data)
>> +{
>> +	unsigned int i;
>> +	struct vimc_sen_device *vsen = data;
>> +
>> +	set_freezable();
>> +
>> +	for (;;) {
>> +		try_to_freeze();
>> +		if (kthread_should_stop())
>> +			break;
>> +
>> +		memset(vsen->frame, 100, vsen->frame_size);
>> +
>> +		/* Send the frame to all source pads */
>> +		for (i = 0; i < vsen->sd.entity.num_pads; i++)
>> +			vimc_propagate_frame(vsen->dev,
>> +					     &vsen->sd.entity.pads[i],
>> +					     vsen->frame);
>> +
>> +		/* 60 frames per second */
>> +		schedule_timeout_interruptible(HZ/60);
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable)
>> +{
>> +	int ret;
>> +	struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd);
>> +
>> +	if (enable) {
>> +		const struct vimc_pix_map *vpix;
>> +
>> +		if (vsen->kthread_sen)
>> +			return -EINVAL;
>> +
>> +		/* Calculate the frame size */
>> +		vpix = vimc_pix_map_by_code(vsen->mbus_format.code);
>> +		vsen->frame_size = vsen->mbus_format.width * vpix->bpp *
>> +				   vsen->mbus_format.height;
>> +
>> +		/* Allocate the frame buffer. Use vmalloc to be able to
>
> /*
>  *
>
>> +		 * allocate a large amount of memory
>> +		 */
>> +		vsen->frame = vmalloc(vsen->frame_size);
>> +		if (!vsen->frame)
>> +			return -ENOMEM;
>> +
>> +		/* Initialize the image generator thread */
>> +		vsen->kthread_sen = kthread_run(vimc_thread_sen, vsen,
>> +						"%s-sen", vsen->v4l2_dev->name);
>
> We have no other kthreads, just the video device name should be enough.
>
>> +		if (IS_ERR(vsen->kthread_sen)) {
>> +			v4l2_err(vsen->v4l2_dev, "kernel_thread() failed\n");
>
> How about dev_err() instead? You use dev_*() macros elsewhere for printing.
>
>> +			vfree(vsen->frame);
>> +			vsen->frame = NULL;
>> +			return PTR_ERR(vsen->kthread_sen);
>> +		}
>> +	} else {
>> +		if (!vsen->kthread_sen)
>> +			return -EINVAL;
>> +
>> +		/* Stop image generator */
>> +		ret = kthread_stop(vsen->kthread_sen);
>> +		vsen->kthread_sen = NULL;
>> +
>> +		vfree(vsen->frame);
>> +		vsen->frame = NULL;
>> +		return ret;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +struct v4l2_subdev_video_ops vimc_sen_video_ops = {
>> +	.s_stream = vimc_sen_s_stream,
>> +};
>> +
>> +static const struct v4l2_subdev_ops vimc_sen_ops = {
>> +	.pad = &vimc_sen_pad_ops,
>> +	.video = &vimc_sen_video_ops,
>> +};
>> +
>> +static void vimc_sen_destroy(struct vimc_ent_device *ved)
>> +{
>> +	struct vimc_sen_device *vsen = container_of(ved,
>> +						struct vimc_sen_device, ved);
>> +
>> +	media_entity_cleanup(ved->ent);
>> +	v4l2_device_unregister_subdev(&vsen->sd);
>
> I'd say media_entity_cleanup() would belong here but it's an empty function
> nowadays...
>
>> +	kfree(vsen);
>> +}
>> +
>> +struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
>> +					const char *const name,
>> +					u16 num_pads,
>> +					const unsigned long *pads_flag)
>> +{
>> +	int ret;
>> +	unsigned int i;
>> +	struct vimc_sen_device *vsen;
>
> vsen first?
>
>> +
>> +	if (!name || (num_pads && !pads_flag))
>> +		return ERR_PTR(-EINVAL);
>> +	/* check if all pads are sources */
>> +	for (i = 0; i < num_pads; i++)
>> +		if (!(pads_flag[i] & MEDIA_PAD_FL_SOURCE))
>> +			return ERR_PTR(-EINVAL);
>> +
>> +	/* Allocate the vsen struct */
>> +	vsen = kzalloc(sizeof(*vsen), GFP_KERNEL);
>> +	if (!vsen)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	/* Link the vimc_sen_device struct with the v4l2 parent */
>> +	vsen->v4l2_dev = v4l2_dev;
>> +	/* Link the vimc_sen_device struct with the dev parent */
>> +	vsen->dev = v4l2_dev->dev;
>> +
>> +	/* Allocate the pads */
>> +	vsen->ved.pads = vimc_pads_init(num_pads, pads_flag);
>> +	if (IS_ERR(vsen->ved.pads)) {
>> +		ret = PTR_ERR(vsen->ved.pads);
>> +		goto err_free_vsen;
>> +	}
>> +
>> +	/* Initialize the media entity */
>> +	vsen->sd.entity.name = name;
>> +	ret = media_entity_pads_init(&vsen->sd.entity,
>> +				     num_pads, vsen->ved.pads);
>> +	if (ret)
>> +		goto err_clean_pads;
>> +
>> +	/* Set the active frame format (this is hardcoded for now) */
>> +	vsen->mbus_format.width = 640;
>> +	vsen->mbus_format.height = 480;
>> +	vsen->mbus_format.code = MEDIA_BUS_FMT_RGB888_1X24;
>> +	vsen->mbus_format.field = V4L2_FIELD_NONE;
>> +	vsen->mbus_format.colorspace = V4L2_COLORSPACE_SRGB;
>> +	vsen->mbus_format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
>> +	vsen->mbus_format.xfer_func = V4L2_XFER_FUNC_SRGB;
>> +
>> +	/* Fill the vimc_ent_device struct */
>> +	vsen->ved.destroy = vimc_sen_destroy;
>> +	vsen->ved.ent = &vsen->sd.entity;
>> +
>> +	/* Initialize the subdev */
>> +	v4l2_subdev_init(&vsen->sd, &vimc_sen_ops);
>> +	vsen->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
>> +	vsen->sd.entity.ops = &vimc_sen_mops;
>> +	vsen->sd.owner = THIS_MODULE;
>> +	strlcpy(vsen->sd.name, name, sizeof(vsen->sd.name));
>> +	v4l2_set_subdevdata(&vsen->sd, vsen);
>> +
>> +	/* Expose this subdev to user space */
>> +	vsen->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
>> +
>> +	/* Register the subdev with the v4l2 and the media framework */
>> +	ret = v4l2_device_register_subdev(vsen->v4l2_dev, &vsen->sd);
>> +	if (ret) {
>> +		dev_err(vsen->dev,
>> +			"subdev register failed (err=%d)\n", ret);
>> +		goto err_clean_m_ent;
>> +	}
>> +
>> +	return &vsen->ved;
>> +
>> +err_clean_m_ent:
>> +	media_entity_cleanup(&vsen->sd.entity);
>> +err_clean_pads:
>> +	vimc_pads_cleanup(vsen->ved.pads);
>> +err_free_vsen:
>> +	kfree(vsen);
>> +
>> +	return ERR_PTR(ret);
>> +}
>> diff --git a/drivers/media/platform/vimc/vimc-sensor.h b/drivers/media/platform/vimc/vimc-sensor.h
>> new file mode 100644
>> index 0000000..ffeaf6c
>> --- /dev/null
>> +++ b/drivers/media/platform/vimc/vimc-sensor.h
>> @@ -0,0 +1,28 @@
>> +/*
>> + * vimc-sensor.h Virtual Media Controller Driver
>> + *
>> + * Copyright (C) 2016 Helen Koike F. <helen.fornazier@gmail.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + */
>> +
>> +#ifndef _VIMC_SENSOR_H_
>> +#define _VIMC_SENSOR_H_
>> +
>> +#include "vimc-core.h"
>> +
>> +struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
>> +					const char *const name,
>> +					u16 num_pads,
>> +					const unsigned long *pads_flag);
>> +
>> +#endif
>
> Looks very nice in general! I'll try this out soon.
>

Helen Koike

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

* [PATCH v7] [media] vimc: Virtual Media Controller core, capture and sensor
  2017-03-10 13:42               ` Helen Koike
@ 2017-03-25 17:11                 ` Helen Koike
  2017-03-25 23:04                   ` Helen Koike
                                     ` (2 more replies)
  0 siblings, 3 replies; 41+ messages in thread
From: Helen Koike @ 2017-03-25 17:11 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-media, jgebben, mchehab, Helen Fornazier, Sakari Ailus,
	Laurent Pinchart, Helen Koike

First version of the Virtual Media Controller.
Add a simple version of the core of the driver, the capture and
sensor nodes in the topology, generating a grey image in a hardcoded
format.

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

---

Patch based in media/master tree, and available here:
https://github.com/helen-fornazier/opw-staging/tree/vimc/devel/v7

Changes since v6:
- add kernel-docs in vimc-core.h
- reorder list_del call in vimc_cap_return_all_buffers()
- call media_pipeline_stop in vimc_cap_start_streaming when fail
- remove DMA comment (left over from the sample driver)
- remove vb2_set_plane_payload call in vimc_cap_buffer_prepare
- remove format verification in vimc_cap_link_validate
- set vimc_pix_map_list as static
- use MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN in vimc_raw_create()
- register media device after creating the topology and unregister it before destroying the topology
- replace devm_kzalloc by kzalloc for allocating struct vimc_device
- uset TASK_UNINTERRUPTIBLE for vimc_thread_sen
- do not allow the creation of a sensor with no pads
- add more verbose description in Kconfig
- change copyright to 2017
- arrange includes: number before letters
- remove v4l2_dev pointer from vimc_cap_device structure
- coding style adjustments
- remove entity variable in vimc_cap_pipeline_s_stream
- declare and assign variables in vimc_cap_link_validate
- declare vimc_dev_release() and vimc_pdev closer to vimc_init()
- remove pad check in vimc_sen_enum_{mbus_code,frame_size} that is already performed by v4l2-subdev.c
- fix multiline comments to start with /*
- reorder variable declaration in functions
- remove pad and subdevice type check in vimc_cap_pipeline_s_stream
- add a note that sensor nodes can can have more then one source pad
- remove the use of entity->use_count in the core and attach the ved structure in sd or vd, use
  ent->obj_type to know which structure (sd or vd) to use
- rename vdev (video_device) to vd to be similar to sd (subdev)

Changes since v5:
- Fix message "Entity type for entity Sensor A was not initialized!"
  by initializing the sensor entity.function after the calling
  v4l2_subded_init
- populate device_caps in vimc_cap_create instead of in
  vimc_cap_querycap
- Fix typo in vimc-core.c s/de/the

Changes since v4:
- coding style fixes
- remove BUG_ON
- change copyright to 2016
- depens on VIDEO_V4L2_SUBDEV_API instead of select
- remove assignement of V4L2_CAP_DEVICE_CAPS
- s/vimc_cap_g_fmt_vid_cap/vimc_cap_fmt_vid_cap
- fix vimc_cap_queue_setup declaration type
- remove wrong buffer size check and add it in vimc_cap_buffer_prepare
- vimc_cap_create: remove unecessary check if v4l2_dev or v4l2_dev->dev is null
- vimc_cap_create: only allow a single pad
- vimc_sen_create: only allow source pads, remove unecessary source pads
checks in vimc_thread_sen

Changes since v3: fix rmmod crash and built-in compile
- Re-order unregister calls in vimc_device_unregister function (remove
rmmod issue)
- Call media_device_unregister_entity in vimc_raw_destroy
- Add depends on VIDEO_DEV && VIDEO_V4L2 and select VIDEOBUF2_VMALLOC
- Check *nplanes in queue_setup (this remove v4l2-compliance fail)
- Include <linux/kthread.h> in vimc-sensor.c
- Move include of <linux/slab.h> from vimc-core.c to vimc-core.h
- Generate 60 frames per sec instead of 1 in the sensor

Changes since v2: update with current media master tree
- Add struct media_pipeline in vimc_cap_device
- Use vb2_v4l2_buffer instead of vb2_buffer
- Typos
- Remove usage of entity->type and use entity->function instead
- Remove fmt argument from queue setup
- Use ktime_get_ns instead of v4l2_get_timestamp
- Iterate over link's list using list_for_each_entry
- Use media_device_{init, cleanup}
- Use entity->use_count to keep track of entities instead of the old
entity->id
- Replace media_entity_init by media_entity_pads_init
---
 drivers/media/platform/Kconfig             |   2 +
 drivers/media/platform/Makefile            |   1 +
 drivers/media/platform/vimc/Kconfig        |  14 +
 drivers/media/platform/vimc/Makefile       |   3 +
 drivers/media/platform/vimc/vimc-capture.c | 536 ++++++++++++++++++++++
 drivers/media/platform/vimc/vimc-capture.h |  28 ++
 drivers/media/platform/vimc/vimc-core.c    | 696 +++++++++++++++++++++++++++++
 drivers/media/platform/vimc/vimc-core.h    | 123 +++++
 drivers/media/platform/vimc/vimc-sensor.c  | 279 ++++++++++++
 drivers/media/platform/vimc/vimc-sensor.h  |  28 ++
 10 files changed, 1710 insertions(+)
 create mode 100644 drivers/media/platform/vimc/Kconfig
 create mode 100644 drivers/media/platform/vimc/Makefile
 create mode 100644 drivers/media/platform/vimc/vimc-capture.c
 create mode 100644 drivers/media/platform/vimc/vimc-capture.h
 create mode 100644 drivers/media/platform/vimc/vimc-core.c
 create mode 100644 drivers/media/platform/vimc/vimc-core.h
 create mode 100644 drivers/media/platform/vimc/vimc-sensor.c
 create mode 100644 drivers/media/platform/vimc/vimc-sensor.h

diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 53f6f12..6dab7e6 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -466,6 +466,8 @@ menuconfig V4L_TEST_DRIVERS
 
 if V4L_TEST_DRIVERS
 
+source "drivers/media/platform/vimc/Kconfig"
+
 source "drivers/media/platform/vivid/Kconfig"
 
 config VIDEO_VIM2M
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 8959f6e..4af4cce 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_VIDEO_PXA27x)	+= pxa_camera.o
 
 obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o
 
+obj-$(CONFIG_VIDEO_VIMC)		+= vimc/
 obj-$(CONFIG_VIDEO_VIVID)		+= vivid/
 obj-$(CONFIG_VIDEO_VIM2M)		+= vim2m.o
 
diff --git a/drivers/media/platform/vimc/Kconfig b/drivers/media/platform/vimc/Kconfig
new file mode 100644
index 0000000..dd285fa
--- /dev/null
+++ b/drivers/media/platform/vimc/Kconfig
@@ -0,0 +1,14 @@
+config VIDEO_VIMC
+	tristate "Virtual Media Controller Driver (VIMC)"
+	depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+	select VIDEOBUF2_VMALLOC
+	default n
+	---help---
+	  Skeleton driver for Virtual Media Controller
+
+	  This drivers can be compared to the Vivid driver for emulating
+	  a media node that exposes a complex media topology. The topology
+	  is hard coded for now but is meant to be highly configurable in
+	  the future.
+
+	  When in doubt, say N.
diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile
new file mode 100644
index 0000000..c45195e
--- /dev/null
+++ b/drivers/media/platform/vimc/Makefile
@@ -0,0 +1,3 @@
+vimc-objs := vimc-core.o vimc-capture.o vimc-sensor.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
new file mode 100644
index 0000000..8f77fc8
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-capture.c
@@ -0,0 +1,536 @@
+/*
+ * vimc-capture.c Virtual Media Controller Driver
+ *
+ * Copyright (C) 2017 Helen Koike F. <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-vmalloc.h>
+
+#include "vimc-capture.h"
+
+struct vimc_cap_device {
+	struct vimc_ent_device ved;
+	struct video_device vd;
+	struct device *dev;
+	struct v4l2_pix_format format;
+	struct vb2_queue queue;
+	struct list_head buf_list;
+	/*
+	 * NOTE: in a real driver, a spin lock must be used to access the
+	 * queue because the frames are generated from a hardware interruption
+	 * and the isr is not allowed to sleep.
+	 * Even if it is not necessary a spinlock in the vimc driver, we
+	 * use it here as a code reference
+	 */
+	spinlock_t qlock;
+	struct mutex lock;
+	u32 sequence;
+	struct media_pipeline pipe;
+};
+
+struct vimc_cap_buffer {
+	/*
+	 * struct vb2_v4l2_buffer must be the first element
+	 * the videobuf2 framework will allocate this struct based on
+	 * buf_struct_size and use the first sizeof(struct vb2_buffer) bytes of
+	 * memory as a vb2_buffer
+	 */
+	struct vb2_v4l2_buffer vb2;
+	struct list_head list;
+};
+
+static int vimc_cap_querycap(struct file *file, void *priv,
+			     struct v4l2_capability *cap)
+{
+	struct vimc_cap_device *vcap = video_drvdata(file);
+
+	strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
+	strlcpy(cap->card, KBUILD_MODNAME, sizeof(cap->card));
+	snprintf(cap->bus_info, sizeof(cap->bus_info),
+		 "platform:%s", vcap->vd.v4l2_dev->name);
+
+	return 0;
+}
+
+static int vimc_cap_enum_input(struct file *file, void *priv,
+			       struct v4l2_input *i)
+{
+	/* We only have one input */
+	if (i->index > 0)
+		return -EINVAL;
+
+	i->type = V4L2_INPUT_TYPE_CAMERA;
+	strlcpy(i->name, "VIMC capture", sizeof(i->name));
+
+	return 0;
+}
+
+static int vimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
+{
+	/* We only have one input */
+	*i = 0;
+	return 0;
+}
+
+static int vimc_cap_s_input(struct file *file, void *priv, unsigned int i)
+{
+	/* We only have one input */
+	return i ? -EINVAL : 0;
+}
+
+static int vimc_cap_fmt_vid_cap(struct file *file, void *priv,
+				  struct v4l2_format *f)
+{
+	struct vimc_cap_device *vcap = video_drvdata(file);
+
+	f->fmt.pix = vcap->format;
+
+	return 0;
+}
+
+static int vimc_cap_enum_fmt_vid_cap(struct file *file, void *priv,
+				     struct v4l2_fmtdesc *f)
+{
+	struct vimc_cap_device *vcap = video_drvdata(file);
+
+	if (f->index > 0)
+		return -EINVAL;
+
+	/* We only support one format for now */
+	f->pixelformat = vcap->format.pixelformat;
+
+	return 0;
+}
+
+static const struct v4l2_file_operations vimc_cap_fops = {
+	.owner		= THIS_MODULE,
+	.open		= v4l2_fh_open,
+	.release	= vb2_fop_release,
+	.read           = vb2_fop_read,
+	.poll		= vb2_fop_poll,
+	.unlocked_ioctl = video_ioctl2,
+	.mmap           = vb2_fop_mmap,
+};
+
+static const struct v4l2_ioctl_ops vimc_cap_ioctl_ops = {
+	.vidioc_querycap = vimc_cap_querycap,
+
+	.vidioc_enum_input = vimc_cap_enum_input,
+	.vidioc_g_input = vimc_cap_g_input,
+	.vidioc_s_input = vimc_cap_s_input,
+
+	.vidioc_g_fmt_vid_cap = vimc_cap_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap = vimc_cap_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap = vimc_cap_fmt_vid_cap,
+	.vidioc_enum_fmt_vid_cap = vimc_cap_enum_fmt_vid_cap,
+
+	.vidioc_reqbufs = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs = vb2_ioctl_create_bufs,
+	.vidioc_querybuf = vb2_ioctl_querybuf,
+	.vidioc_qbuf = vb2_ioctl_qbuf,
+	.vidioc_dqbuf = vb2_ioctl_dqbuf,
+	.vidioc_expbuf = vb2_ioctl_expbuf,
+	.vidioc_streamon = vb2_ioctl_streamon,
+	.vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+static void vimc_cap_return_all_buffers(struct vimc_cap_device *vcap,
+					enum vb2_buffer_state state)
+{
+	struct vimc_cap_buffer *vbuf, *node;
+
+	spin_lock(&vcap->qlock);
+
+	list_for_each_entry_safe(vbuf, node, &vcap->buf_list, list) {
+		list_del(&vbuf->list);
+		vb2_buffer_done(&vbuf->vb2.vb2_buf, state);
+	}
+
+	spin_unlock(&vcap->qlock);
+}
+
+static int vimc_cap_pipeline_s_stream(struct vimc_cap_device *vcap, int enable)
+{
+	struct v4l2_subdev *sd;
+	struct media_pad *pad;
+	int ret;
+
+	/* Start the stream in the subdevice direct connected */
+	pad = media_entity_remote_pad(&vcap->vd.entity.pads[0]);
+
+	/*
+	 * if it is a raw node from vimc-core, there is nothing to activate
+	 * TODO: remove this when there are no more raw nodes in the
+	 * core and return error instead
+	 */
+	if (pad->entity->obj_type == MEDIA_ENTITY_TYPE_BASE)
+		return 0;
+
+	sd = media_entity_to_v4l2_subdev(pad->entity);
+	ret = v4l2_subdev_call(sd, video, s_stream, enable);
+	if (ret && ret != -ENOIOCTLCMD)
+		return ret;
+
+	return 0;
+}
+
+static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
+	struct media_entity *entity = &vcap->vd.entity;
+	int ret;
+
+	vcap->sequence = 0;
+
+	/* Start the media pipeline */
+	ret = media_pipeline_start(entity, &vcap->pipe);
+	if (ret) {
+		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
+		return ret;
+	}
+
+	/* Enable streaming from the pipe */
+	ret = vimc_cap_pipeline_s_stream(vcap, 1);
+	if (ret) {
+		media_pipeline_stop(entity);
+		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Stop the stream engine. Any remaining buffers in the stream queue are
+ * dequeued and passed on to the vb2 framework marked as STATE_ERROR.
+ */
+static void vimc_cap_stop_streaming(struct vb2_queue *vq)
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
+
+	/* Disable streaming from the pipe */
+	vimc_cap_pipeline_s_stream(vcap, 0);
+
+	/* Stop the media pipeline */
+	media_pipeline_stop(&vcap->vd.entity);
+
+	/* Release all active buffers */
+	vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_ERROR);
+}
+
+static void vimc_cap_buf_queue(struct vb2_buffer *vb2_buf)
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb2_buf->vb2_queue);
+	struct vimc_cap_buffer *buf = container_of(vb2_buf,
+						   struct vimc_cap_buffer,
+						   vb2.vb2_buf);
+
+	spin_lock(&vcap->qlock);
+	list_add_tail(&buf->list, &vcap->buf_list);
+	spin_unlock(&vcap->qlock);
+}
+
+static int vimc_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
+				unsigned int *nplanes, unsigned int sizes[],
+				struct device *alloc_devs[])
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
+
+	if (*nplanes)
+		return sizes[0] < vcap->format.sizeimage ? -EINVAL : 0;
+	/* We don't support multiplanes for now */
+	*nplanes = 1;
+	sizes[0] = vcap->format.sizeimage;
+
+	return 0;
+}
+
+static int vimc_cap_buffer_prepare(struct vb2_buffer *vb)
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb->vb2_queue);
+	unsigned long size = vcap->format.sizeimage;
+
+	if (vb2_plane_size(vb, 0) < size) {
+		dev_err(vcap->dev, "buffer too small (%lu < %lu)\n",
+			vb2_plane_size(vb, 0), size);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static const struct vb2_ops vimc_cap_qops = {
+	.start_streaming	= vimc_cap_start_streaming,
+	.stop_streaming		= vimc_cap_stop_streaming,
+	.buf_queue		= vimc_cap_buf_queue,
+	.queue_setup		= vimc_cap_queue_setup,
+	.buf_prepare		= vimc_cap_buffer_prepare,
+	/*
+	 * Since q->lock is set we can use the standard
+	 * vb2_ops_wait_prepare/finish helper functions.
+	 */
+	.wait_prepare		= vb2_ops_wait_prepare,
+	.wait_finish		= vb2_ops_wait_finish,
+};
+
+/*
+ * NOTE: this function is a copy of v4l2_subdev_link_validate_get_format
+ * maybe the v4l2 function should be public
+ */
+static int vimc_cap_v4l2_subdev_link_validate_get_format(struct media_pad *pad,
+						struct v4l2_subdev_format *fmt)
+{
+	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity);
+
+	fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	fmt->pad = pad->index;
+
+	return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt);
+}
+
+static int vimc_cap_link_validate(struct media_link *link)
+{
+	struct v4l2_subdev_format source_fmt;
+	const struct vimc_pix_map *vpix;
+	struct vimc_cap_device *vcap = container_of(link->sink->entity,
+						    struct vimc_cap_device,
+						    vd.entity);
+	struct v4l2_pix_format *sink_fmt = &vcap->format;
+	int ret;
+
+	/*
+	 * if it is a raw node from vimc-core, ignore the link for now
+	 * TODO: remove this when there are no more raw nodes in the
+	 * core and return error instead
+	 */
+	if (link->source->entity->obj_type == MEDIA_ENTITY_TYPE_BASE)
+		return 0;
+
+	/* Get the the format of the subdev */
+	ret = vimc_cap_v4l2_subdev_link_validate_get_format(link->source,
+							    &source_fmt);
+	if (ret)
+		return ret;
+
+	dev_dbg(vcap->dev,
+		"cap: %s: link validate formats src:%dx%d %d sink:%dx%d %d\n",
+		vcap->vd.name,
+		source_fmt.format.width, source_fmt.format.height,
+		source_fmt.format.code,
+		sink_fmt->width, sink_fmt->height,
+		sink_fmt->pixelformat);
+
+	/* The width, height and code must match. */
+	vpix = vimc_pix_map_by_pixelformat(sink_fmt->pixelformat);
+	if (source_fmt.format.width != sink_fmt->width
+	    || source_fmt.format.height != sink_fmt->height
+	    || vpix->code != source_fmt.format.code)
+		return -EINVAL;
+
+	/*
+	 * The field order must match, or the sink field order must be NONE
+	 * to support interlaced hardware connected to bridges that support
+	 * progressive formats only.
+	 */
+	if (source_fmt.format.field != sink_fmt->field &&
+	    sink_fmt->field != V4L2_FIELD_NONE)
+		return -EINVAL;
+
+	return 0;
+}
+
+static const struct media_entity_operations vimc_cap_mops = {
+	.link_validate		= vimc_cap_link_validate,
+};
+
+static void vimc_cap_destroy(struct vimc_ent_device *ved)
+{
+	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
+						    ved);
+
+	vb2_queue_release(&vcap->queue);
+	media_entity_cleanup(ved->ent);
+	video_unregister_device(&vcap->vd);
+	vimc_pads_cleanup(vcap->ved.pads);
+	kfree(vcap);
+}
+
+static void vimc_cap_process_frame(struct vimc_ent_device *ved,
+				   struct media_pad *sink, const void *frame)
+{
+	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
+						    ved);
+	struct vimc_cap_buffer *vimc_buf;
+	void *vbuf;
+
+	/* If the stream in this node is not active, just return */
+	mutex_lock(&vcap->lock);
+	if (!vb2_is_busy(&vcap->queue)) {
+		mutex_unlock(&vcap->lock);
+		return;
+	}
+	mutex_unlock(&vcap->lock);
+
+	spin_lock(&vcap->qlock);
+
+	/* Get the first entry of the list */
+	vimc_buf = list_first_entry_or_null(&vcap->buf_list,
+					    typeof(*vimc_buf), list);
+	if (!vimc_buf) {
+		spin_unlock(&vcap->qlock);
+		return;
+	}
+
+	/* Remove this entry from the list */
+	list_del(&vimc_buf->list);
+
+	spin_unlock(&vcap->qlock);
+
+	/* Fill the buffer */
+	vimc_buf->vb2.vb2_buf.timestamp = ktime_get_ns();
+	vimc_buf->vb2.sequence = vcap->sequence++;
+	vimc_buf->vb2.field = vcap->format.field;
+
+	vbuf = vb2_plane_vaddr(&vimc_buf->vb2.vb2_buf, 0);
+
+	memcpy(vbuf, frame, vcap->format.sizeimage);
+
+	/* Set it as ready */
+	vb2_set_plane_payload(&vimc_buf->vb2.vb2_buf, 0,
+			      vcap->format.sizeimage);
+	vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag)
+{
+	const struct vimc_pix_map *vpix;
+	struct vimc_cap_device *vcap;
+	struct video_device *vd;
+	struct vb2_queue *q;
+	int ret;
+
+	/*
+	 * Check entity configuration params
+	 * NOTE: we only support a single sink pad
+	 */
+	if (!name || num_pads != 1 || !pads_flag ||
+	    !(pads_flag[0] & MEDIA_PAD_FL_SINK))
+		return ERR_PTR(-EINVAL);
+
+	/* Allocate the vimc_cap_device struct */
+	vcap = kzalloc(sizeof(*vcap), GFP_KERNEL);
+	if (!vcap)
+		return ERR_PTR(-ENOMEM);
+
+	/* Link the vimc_cap_device struct with dev parent */
+	vcap->dev = v4l2_dev->dev;
+
+	/* Allocate the pads */
+	vcap->ved.pads = vimc_pads_init(num_pads, pads_flag);
+	if (IS_ERR(vcap->ved.pads)) {
+		ret = PTR_ERR(vcap->ved.pads);
+		goto err_free_vcap;
+	}
+
+	/* Initialize the media entity */
+	vcap->vd.entity.name = name;
+	vcap->vd.entity.function = MEDIA_ENT_F_IO_V4L;
+	ret = media_entity_pads_init(&vcap->vd.entity,
+				     num_pads, vcap->ved.pads);
+	if (ret)
+		goto err_clean_pads;
+
+	/* Initialize the lock */
+	mutex_init(&vcap->lock);
+
+	/* Initialize the vb2 queue */
+	q = &vcap->queue;
+	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	q->io_modes = VB2_MMAP | VB2_DMABUF;
+	q->drv_priv = vcap;
+	q->buf_struct_size = sizeof(struct vimc_cap_buffer);
+	q->ops = &vimc_cap_qops;
+	q->mem_ops = &vb2_vmalloc_memops;
+	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	q->min_buffers_needed = 2;
+	q->lock = &vcap->lock;
+
+	ret = vb2_queue_init(q);
+	if (ret) {
+		dev_err(vcap->dev,
+			"vb2 queue init failed (err=%d)\n", ret);
+		goto err_clean_m_ent;
+	}
+
+	/* Initialize buffer list and its lock */
+	INIT_LIST_HEAD(&vcap->buf_list);
+	spin_lock_init(&vcap->qlock);
+
+	/* Set the frame format (this is hardcoded for now) */
+	vcap->format.width = 640;
+	vcap->format.height = 480;
+	vcap->format.pixelformat = V4L2_PIX_FMT_RGB24;
+	vcap->format.field = V4L2_FIELD_NONE;
+	vcap->format.colorspace = V4L2_COLORSPACE_SRGB;
+
+	vpix = vimc_pix_map_by_pixelformat(vcap->format.pixelformat);
+
+	vcap->format.bytesperline = vcap->format.width * vpix->bpp;
+	vcap->format.sizeimage = vcap->format.bytesperline *
+				 vcap->format.height;
+
+	/* Fill the vimc_ent_device struct */
+	vcap->ved.destroy = vimc_cap_destroy;
+	vcap->ved.ent = &vcap->vd.entity;
+	vcap->ved.process_frame = vimc_cap_process_frame;
+
+	/* Initialize the video_device struct */
+	vd = &vcap->vd;
+	vd->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	vd->entity.ops = &vimc_cap_mops;
+	vd->release = video_device_release_empty;
+	vd->fops = &vimc_cap_fops;
+	vd->ioctl_ops = &vimc_cap_ioctl_ops;
+	vd->lock = &vcap->lock;
+	vd->queue = q;
+	vd->v4l2_dev = v4l2_dev;
+	vd->vfl_dir = VFL_DIR_RX;
+	strlcpy(vd->name, name, sizeof(vd->name));
+	video_set_drvdata(vd, &vcap->ved);
+
+	/* Register the video_device with the v4l2 and the media framework */
+	ret = video_register_device(vd, VFL_TYPE_GRABBER, -1);
+	if (ret) {
+		dev_err(vcap->dev,
+			"video register failed (err=%d)\n", ret);
+		goto err_release_queue;
+	}
+
+	return &vcap->ved;
+
+err_release_queue:
+	vb2_queue_release(q);
+err_clean_m_ent:
+	media_entity_cleanup(&vcap->vd.entity);
+err_clean_pads:
+	vimc_pads_cleanup(vcap->ved.pads);
+err_free_vcap:
+	kfree(vcap);
+
+	return ERR_PTR(ret);
+}
diff --git a/drivers/media/platform/vimc/vimc-capture.h b/drivers/media/platform/vimc/vimc-capture.h
new file mode 100644
index 0000000..c7e633d
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-capture.h
@@ -0,0 +1,28 @@
+/*
+ * vimc-capture.h Virtual Media Controller Driver
+ *
+ * Copyright (C) 2017 Helen Koike F. <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIMC_CAPTURE_H_
+#define _VIMC_CAPTURE_H_
+
+#include "vimc-core.h"
+
+struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag);
+
+#endif
diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c
new file mode 100644
index 0000000..2dc2ca8
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-core.c
@@ -0,0 +1,696 @@
+/*
+ * vimc-core.c Virtual Media Controller Driver
+ *
+ * Copyright (C) 2017 Helen Koike F. <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <media/media-device.h>
+#include <media/v4l2-device.h>
+
+#include "vimc-capture.h"
+#include "vimc-core.h"
+#include "vimc-sensor.h"
+
+#define VIMC_PDEV_NAME "vimc"
+#define VIMC_MDEV_MODEL_NAME "VIMC MDEV"
+
+#define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) {	\
+	.src_ent = src,						\
+	.src_pad = srcpad,					\
+	.sink_ent = sink,					\
+	.sink_pad = sinkpad,					\
+	.flags = link_flags,					\
+}
+
+struct vimc_device {
+	/*
+	 * The pipeline configuration
+	 * (filled before calling vimc_device_register)
+	 */
+	const struct vimc_pipeline_config *pipe_cfg;
+
+	/* The Associated media_device parent */
+	struct media_device mdev;
+
+	/* Internal v4l2 parent device*/
+	struct v4l2_device v4l2_dev;
+
+	/* Internal topology */
+	struct vimc_ent_device **ved;
+};
+
+/**
+ * enum vimc_ent_node - Select the functionality of a node in the topology
+ * @VIMC_ENT_NODE_SENSOR:	A node of type SENSOR simulates a camera sensor
+ *				generating internal images in bayer format and
+ *				propagating those images through the pipeline
+ * @VIMC_ENT_NODE_CAPTURE:	A node of type CAPTURE is a v4l2 video_device
+ *				that exposes the received image from the
+ *				pipeline to the user space
+ * @VIMC_ENT_NODE_INPUT:	A node of type INPUT is a v4l2 video_device that
+ *				receives images from the user space and
+ *				propagates them through the pipeline
+ * @VIMC_ENT_NODE_DEBAYER:	A node type DEBAYER expects to receive a frame
+ *				in bayer format converts it to RGB
+ * @VIMC_ENT_NODE_SCALER:	A node of type SCALER scales the received image
+ *				by a given multiplier
+ *
+ * This enum is used in the entity configuration struct to allow the definition
+ * of a custom topology specifying the role of each node on it.
+ */
+enum vimc_ent_node {
+	VIMC_ENT_NODE_SENSOR,
+	VIMC_ENT_NODE_CAPTURE,
+	VIMC_ENT_NODE_INPUT,
+	VIMC_ENT_NODE_DEBAYER,
+	VIMC_ENT_NODE_SCALER,
+};
+
+/* Structure which describes individual configuration for each entity */
+struct vimc_ent_config {
+	const char *name;
+	size_t pads_qty;
+	const unsigned long *pads_flag;
+	enum vimc_ent_node node;
+};
+
+/* Structure which describes links between entities */
+struct vimc_ent_link {
+	unsigned int src_ent;
+	u16 src_pad;
+	unsigned int sink_ent;
+	u16 sink_pad;
+	u32 flags;
+};
+
+/* Structure which describes the whole topology */
+struct vimc_pipeline_config {
+	const struct vimc_ent_config *ents;
+	size_t num_ents;
+	const struct vimc_ent_link *links;
+	size_t num_links;
+};
+
+/* --------------------------------------------------------------------------
+ * Topology Configuration
+ */
+
+static const struct vimc_ent_config ent_config[] = {
+	{
+		.name = "Sensor A",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_SENSOR,
+	},
+	{
+		.name = "Sensor B",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_SENSOR,
+	},
+	{
+		.name = "Debayer A",
+		.pads_qty = 2,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
+						     MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_DEBAYER,
+	},
+	{
+		.name = "Debayer B",
+		.pads_qty = 2,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
+						     MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_DEBAYER,
+	},
+	{
+		.name = "Raw Capture 0",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
+		.node = VIMC_ENT_NODE_CAPTURE,
+	},
+	{
+		.name = "Raw Capture 1",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
+		.node = VIMC_ENT_NODE_CAPTURE,
+	},
+	{
+		.name = "RGB/YUV Input",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_INPUT,
+	},
+	{
+		.name = "Scaler",
+		.pads_qty = 2,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
+						     MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_SCALER,
+	},
+	{
+		.name = "RGB/YUV Capture",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
+		.node = VIMC_ENT_NODE_CAPTURE,
+	},
+};
+
+static const struct vimc_ent_link ent_links[] = {
+	/* Link: Sensor A (Pad 0)->(Pad 0) Debayer A */
+	VIMC_ENT_LINK(0, 0, 2, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Sensor A (Pad 0)->(Pad 0) Raw Capture 0 */
+	VIMC_ENT_LINK(0, 0, 4, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Sensor B (Pad 0)->(Pad 0) Debayer B */
+	VIMC_ENT_LINK(1, 0, 3, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Sensor B (Pad 0)->(Pad 0) Raw Capture 1 */
+	VIMC_ENT_LINK(1, 0, 5, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Debayer A (Pad 1)->(Pad 0) Scaler */
+	VIMC_ENT_LINK(2, 1, 7, 0, MEDIA_LNK_FL_ENABLED),
+	/* Link: Debayer B (Pad 1)->(Pad 0) Scaler */
+	VIMC_ENT_LINK(3, 1, 7, 0, 0),
+	/* Link: RGB/YUV Input (Pad 0)->(Pad 0) Scaler */
+	VIMC_ENT_LINK(6, 0, 7, 0, 0),
+	/* Link: Scaler (Pad 1)->(Pad 0) RGB/YUV Capture */
+	VIMC_ENT_LINK(7, 1, 8, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+};
+
+static const struct vimc_pipeline_config pipe_cfg = {
+	.ents		= ent_config,
+	.num_ents	= ARRAY_SIZE(ent_config),
+	.links		= ent_links,
+	.num_links	= ARRAY_SIZE(ent_links)
+};
+
+/* -------------------------------------------------------------------------- */
+
+static const struct vimc_pix_map vimc_pix_map_list[] = {
+	/* TODO: add all missing formats */
+
+	/* RGB formats */
+	{
+		.code = MEDIA_BUS_FMT_BGR888_1X24,
+		.pixelformat = V4L2_PIX_FMT_BGR24,
+		.bpp = 3,
+	},
+	{
+		.code = MEDIA_BUS_FMT_RGB888_1X24,
+		.pixelformat = V4L2_PIX_FMT_RGB24,
+		.bpp = 3,
+	},
+	{
+		.code = MEDIA_BUS_FMT_ARGB8888_1X32,
+		.pixelformat = V4L2_PIX_FMT_ARGB32,
+		.bpp = 4,
+	},
+
+	/* Bayer formats */
+	{
+		.code = MEDIA_BUS_FMT_SBGGR8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SBGGR8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGBRG8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SGBRG8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGRBG8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SGRBG8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SRGGB8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SRGGB8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SBGGR10_1X10,
+		.pixelformat = V4L2_PIX_FMT_SBGGR10,
+		.bpp = 2,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGBRG10_1X10,
+		.pixelformat = V4L2_PIX_FMT_SGBRG10,
+		.bpp = 2,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGRBG10_1X10,
+		.pixelformat = V4L2_PIX_FMT_SGRBG10,
+		.bpp = 2,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SRGGB10_1X10,
+		.pixelformat = V4L2_PIX_FMT_SRGGB10,
+		.bpp = 2,
+	},
+
+	/* 10bit raw bayer a-law compressed to 8 bits */
+	{
+		.code = MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SBGGR10ALAW8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SGBRG10ALAW8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SGRBG10ALAW8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SRGGB10ALAW8,
+		.bpp = 1,
+	},
+
+	/* 10bit raw bayer DPCM compressed to 8 bits */
+	{
+		.code = MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SBGGR10DPCM8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SGBRG10DPCM8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SRGGB10DPCM8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SBGGR12_1X12,
+		.pixelformat = V4L2_PIX_FMT_SBGGR12,
+		.bpp = 2,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGBRG12_1X12,
+		.pixelformat = V4L2_PIX_FMT_SGBRG12,
+		.bpp = 2,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGRBG12_1X12,
+		.pixelformat = V4L2_PIX_FMT_SGRBG12,
+		.bpp = 2,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SRGGB12_1X12,
+		.pixelformat = V4L2_PIX_FMT_SRGGB12,
+		.bpp = 2,
+	},
+};
+
+const struct vimc_pix_map *vimc_pix_map_by_code(u32 code)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(vimc_pix_map_list); i++) {
+		if (vimc_pix_map_list[i].code == code)
+			return &vimc_pix_map_list[i];
+	}
+	return NULL;
+}
+
+const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(vimc_pix_map_list); i++) {
+		if (vimc_pix_map_list[i].pixelformat == pixelformat)
+			return &vimc_pix_map_list[i];
+	}
+	return NULL;
+}
+
+int vimc_propagate_frame(struct device *dev,
+			 struct media_pad *src, const void *frame)
+{
+	struct media_link *link;
+
+	if (!(src->flags & MEDIA_PAD_FL_SOURCE))
+		return -EINVAL;
+
+	/* Send this frame to all sink pads that are direct linked */
+	list_for_each_entry(link, &src->entity->links, list) {
+		if (link->source == src &&
+		    (link->flags & MEDIA_LNK_FL_ENABLED)) {
+			struct vimc_ent_device *ved = NULL;
+			struct media_entity *entity = link->sink->entity;
+
+			if (is_media_entity_v4l2_subdev(entity)) {
+				struct v4l2_subdev *sd =
+					container_of(entity, struct v4l2_subdev,
+						     entity);
+				ved = v4l2_get_subdevdata(sd);
+			} else if (is_media_entity_v4l2_video_device(entity)) {
+				struct video_device *vd =
+					container_of(entity,
+						     struct video_device,
+						     entity);
+				ved = video_get_drvdata(vd);
+			}
+			if (ved && ved->process_frame)
+				ved->process_frame(ved, link->sink, frame);
+		}
+	}
+
+	return 0;
+}
+
+static void vimc_device_unregister(struct vimc_device *vimc)
+{
+	unsigned int i;
+
+	media_device_unregister(&vimc->mdev);
+	/* Cleanup (only initialized) entities */
+	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
+		if (vimc->ved[i] && vimc->ved[i]->destroy)
+			vimc->ved[i]->destroy(vimc->ved[i]);
+
+		vimc->ved[i] = NULL;
+	}
+	v4l2_device_unregister(&vimc->v4l2_dev);
+	media_device_cleanup(&vimc->mdev);
+}
+
+/* Helper function to allocate and initialize pads */
+struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag)
+{
+	struct media_pad *pads;
+	unsigned int i;
+
+	/* Allocate memory for the pads */
+	pads = kcalloc(num_pads, sizeof(*pads), GFP_KERNEL);
+	if (!pads)
+		return ERR_PTR(-ENOMEM);
+
+	/* Initialize the pads */
+	for (i = 0; i < num_pads; i++) {
+		pads[i].index = i;
+		pads[i].flags = pads_flag[i];
+	}
+
+	return pads;
+}
+
+/*
+ * TODO: remove this function when all the
+ * entities specific code are implemented
+ */
+static void vimc_raw_destroy(struct vimc_ent_device *ved)
+{
+	media_device_unregister_entity(ved->ent);
+
+	media_entity_cleanup(ved->ent);
+
+	vimc_pads_cleanup(ved->pads);
+
+	kfree(ved->ent);
+
+	kfree(ved);
+}
+
+/*
+ * TODO: remove this function when all the
+ * entities specific code are implemented
+ */
+static struct vimc_ent_device *vimc_raw_create(struct v4l2_device *v4l2_dev,
+					       const char *const name,
+					       u16 num_pads,
+					       const unsigned long *pads_flag)
+{
+	struct vimc_ent_device *ved;
+	int ret;
+
+	/* Allocate the main ved struct */
+	ved = kzalloc(sizeof(*ved), GFP_KERNEL);
+	if (!ved)
+		return ERR_PTR(-ENOMEM);
+
+	/* Allocate the media entity */
+	ved->ent = kzalloc(sizeof(*ved->ent), GFP_KERNEL);
+	if (!ved->ent) {
+		ret = -ENOMEM;
+		goto err_free_ved;
+	}
+
+	/* Allocate the pads */
+	ved->pads = vimc_pads_init(num_pads, pads_flag);
+	if (IS_ERR(ved->pads)) {
+		ret = PTR_ERR(ved->pads);
+		goto err_free_ent;
+	}
+
+	/* Initialize the media entity */
+	ved->ent->name = name;
+	ved->ent->function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
+	ret = media_entity_pads_init(ved->ent, num_pads, ved->pads);
+	if (ret)
+		goto err_cleanup_pads;
+
+	/* Register the media entity */
+	ret = media_device_register_entity(v4l2_dev->mdev, ved->ent);
+	if (ret)
+		goto err_cleanup_entity;
+
+	/* Fill out the destroy function and return */
+	ved->destroy = vimc_raw_destroy;
+	return ved;
+
+err_cleanup_entity:
+	media_entity_cleanup(ved->ent);
+err_cleanup_pads:
+	vimc_pads_cleanup(ved->pads);
+err_free_ent:
+	kfree(ved->ent);
+err_free_ved:
+	kfree(ved);
+
+	return ERR_PTR(ret);
+}
+
+static int vimc_device_register(struct vimc_device *vimc)
+{
+	unsigned int i;
+	int ret;
+
+	/* Allocate memory for the vimc_ent_devices pointers */
+	vimc->ved = devm_kcalloc(vimc->mdev.dev, vimc->pipe_cfg->num_ents,
+				 sizeof(*vimc->ved), GFP_KERNEL);
+	if (!vimc->ved)
+		return -ENOMEM;
+
+	/* Link the media device within the v4l2_device */
+	vimc->v4l2_dev.mdev = &vimc->mdev;
+
+	/* Register the v4l2 struct */
+	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);
+		return ret;
+	}
+
+	/* Initialize entities */
+	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
+		struct vimc_ent_device *(*create_func)(struct v4l2_device *,
+						       const char *const,
+						       u16,
+						       const unsigned long *);
+
+		/* Register the specific node */
+		switch (vimc->pipe_cfg->ents[i].node) {
+		case VIMC_ENT_NODE_SENSOR:
+			create_func = vimc_sen_create;
+			break;
+
+		case VIMC_ENT_NODE_CAPTURE:
+			create_func = vimc_cap_create;
+			break;
+
+		/* TODO: Instantiate the specific topology node */
+		case VIMC_ENT_NODE_INPUT:
+		case VIMC_ENT_NODE_DEBAYER:
+		case VIMC_ENT_NODE_SCALER:
+		default:
+			/*
+			 * TODO: remove this when all the entities specific
+			 * code are implemented
+			 */
+			create_func = vimc_raw_create;
+			break;
+		}
+
+		vimc->ved[i] = create_func(&vimc->v4l2_dev,
+					   vimc->pipe_cfg->ents[i].name,
+					   vimc->pipe_cfg->ents[i].pads_qty,
+					   vimc->pipe_cfg->ents[i].pads_flag);
+		if (IS_ERR(vimc->ved[i])) {
+			ret = PTR_ERR(vimc->ved[i]);
+			vimc->ved[i] = NULL;
+			goto err;
+		}
+	}
+
+	/* 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];
+
+		ret = media_create_pad_link(vimc->ved[link->src_ent]->ent,
+					    link->src_pad,
+					    vimc->ved[link->sink_ent]->ent,
+					    link->sink_pad,
+					    link->flags);
+		if (ret)
+			goto err;
+	}
+
+	/* Register the media device */
+	ret = media_device_register(&vimc->mdev);
+	if (ret) {
+		dev_err(vimc->mdev.dev,
+			"media device register failed (err=%d)\n", ret);
+		return ret;
+	}
+
+	/* 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);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	/* Destroy the so far created topology */
+	vimc_device_unregister(vimc);
+
+	return ret;
+}
+
+static int vimc_probe(struct platform_device *pdev)
+{
+	struct vimc_device *vimc;
+	int ret;
+
+	/* Prepare the vimc topology structure */
+
+	/* Allocate memory for the vimc structure */
+	vimc = kzalloc(sizeof(*vimc), GFP_KERNEL);
+	if (!vimc)
+		return -ENOMEM;
+
+	/* Set the pipeline configuration struct */
+	vimc->pipe_cfg = &pipe_cfg;
+
+	/* Initialize media device */
+	strlcpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME,
+		sizeof(vimc->mdev.model));
+	vimc->mdev.dev = &pdev->dev;
+	media_device_init(&vimc->mdev);
+
+	/* Create vimc topology */
+	ret = vimc_device_register(vimc);
+	if (ret) {
+		dev_err(vimc->mdev.dev,
+			"vimc device registration failed (err=%d)\n", ret);
+		kfree(vimc);
+		return ret;
+	}
+
+	/* Link the topology object with the platform device object */
+	platform_set_drvdata(pdev, vimc);
+
+	return 0;
+}
+
+static int vimc_remove(struct platform_device *pdev)
+{
+	struct vimc_device *vimc = platform_get_drvdata(pdev);
+
+	/* Destroy all the topology */
+	vimc_device_unregister(vimc);
+	kfree(vimc);
+
+	return 0;
+}
+
+static void vimc_dev_release(struct device *dev)
+{
+}
+
+static struct platform_device vimc_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,
+	},
+};
+
+static int __init vimc_init(void)
+{
+	int ret;
+
+	ret = platform_device_register(&vimc_pdev);
+	if (ret) {
+		dev_err(&vimc_pdev.dev,
+			"platform device registration failed (err=%d)\n", ret);
+		return ret;
+	}
+
+	ret = platform_driver_register(&vimc_pdrv);
+	if (ret) {
+		dev_err(&vimc_pdev.dev,
+			"platform driver registration failed (err=%d)\n", ret);
+
+		platform_device_unregister(&vimc_pdev);
+	}
+
+	return ret;
+}
+
+static void __exit vimc_exit(void)
+{
+	platform_driver_unregister(&vimc_pdrv);
+
+	platform_device_unregister(&vimc_pdev);
+}
+
+module_init(vimc_init);
+module_exit(vimc_exit);
+
+MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC)");
+MODULE_AUTHOR("Helen Fornazier <helen.fornazier@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/vimc/vimc-core.h b/drivers/media/platform/vimc/vimc-core.h
new file mode 100644
index 0000000..1d368e9
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-core.h
@@ -0,0 +1,123 @@
+/*
+ * vimc-core.h Virtual Media Controller Driver
+ *
+ * Copyright (C) 2017 Helen Koike F. <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIMC_CORE_H_
+#define _VIMC_CORE_H_
+
+#include <linux/slab.h>
+#include <media/v4l2-device.h>
+
+/**
+ * struct vimc_pix_map - maps media bus code with v4l2 pixel format
+ *
+ * @code:		media bus format code defined by MEDIA_BUS_FMT_* macros
+ * @bbp:		number of bytes each pixel occupies
+ * @pixelformat:	pixel format devined by V4L2_PIX_FMT_* macros
+ *
+ * Struct which matches the MEDIA_BUS_FMT_* codes with the corresponding
+ * V4L2_PIX_FMT_* fourcc pixelformat and its bytes per pixel (bpp)
+ */
+struct vimc_pix_map {
+	unsigned int code;
+	unsigned int bpp;
+	u32 pixelformat;
+};
+
+/**
+ * struct vimc_ent_device - core struct that represents a node in the topology
+ *
+ * @ent:		the pointer to struct media_entity for the node
+ * @pads:		the list of pads of the node
+ * @destroy:		callback to destroy the node
+ * @process_frame:	callback send a frame to that node
+ * @obj:		the object containing the struct media_entity. It can be
+ *			one of two different types: struct v4l2_subdev
+ *			struct video_device depending of the type of the node.
+ *			The struct vimc_ent_device must be embedded in the obj
+ *			by v4l2_set_subdevdata() or by video_set_drvdata()
+ *
+ * Each node of the topology must create a vimc_ent_device struct. Depending on
+ * the node it will be of an instance of v4l2_subdev or video_device struct
+ * where both contains a struct media_entity.
+ * Those structures should embedded the vimc_ent_device struct through
+ * v4l2_set_subdevdata() and video_set_drvdata() respectivaly, allowing the
+ * vimc_ent_device struct to be retrieved from the corresponding struct
+ * media_entity
+ */
+struct vimc_ent_device {
+	struct media_entity *ent;
+	struct media_pad *pads;
+	void (*destroy)(struct vimc_ent_device *);
+	void (*process_frame)(struct vimc_ent_device *ved,
+			      struct media_pad *sink, const void *frame);
+	union {
+		struct v4l2_subdev sd;
+		struct video_device vd;
+	} obj;
+};
+
+/**
+ * vimc_propagate_frame - propagate a frame through the topology
+ *
+ * @dev:	pointer to struct &device
+ * @src:	the source pad where the frame is being originated
+ * @frame:	the frame to be propagated
+ *
+ * This function will call the process_frame callback from the vimc_ent_device
+ * struct of the nodes directly connected to the @src pad
+ */
+int vimc_propagate_frame(struct device *dev,
+			 struct media_pad *src, const void *frame);
+
+/**
+ * vimc_pads_init - initialize pads
+ *
+ * @num_pads:	number of pads to initialize
+ * @pads_flags:	flags to use in each pad
+ *
+ * Helper functions to allocate/initialize pads
+ */
+struct media_pad *vimc_pads_init(u16 num_pads,
+				 const unsigned long *pads_flag);
+
+/**
+ * vimc_pads_cleanup - free pads
+ *
+ * @pads: pointer to the pads
+ *
+ * Helper function to free the pads initialized with vimc_pads_init
+ */
+static inline void vimc_pads_cleanup(struct media_pad *pads)
+{
+	kfree(pads);
+}
+
+/**
+ * vimc_pix_map_by_code - retrieve the vimc_pix_map struct by media code
+ *
+ * @code:		media bus format code defined by MEDIA_BUS_FMT_* macros
+ */
+const struct vimc_pix_map *vimc_pix_map_by_code(u32 code);
+
+/**
+ * vimc_pix_map_by_pixelformat - retrieve the vimc_pix_map struct by v4l2 pixel format
+ *
+ * @pixelformat:	pixel format devined by V4L2_PIX_FMT_* macros
+ */
+const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat);
+
+#endif
diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c
new file mode 100644
index 0000000..45892d0
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-sensor.c
@@ -0,0 +1,279 @@
+/*
+ * vimc-sensor.c Virtual Media Controller Driver
+ *
+ * Copyright (C) 2017 Helen Koike F. <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/freezer.h>
+#include <linux/kthread.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/vmalloc.h>
+#include <media/v4l2-subdev.h>
+
+#include "vimc-sensor.h"
+
+struct vimc_sen_device {
+	struct vimc_ent_device ved;
+	struct v4l2_subdev sd;
+	struct v4l2_device *v4l2_dev;
+	struct device *dev;
+	struct task_struct *kthread_sen;
+	u8 *frame;
+	/* The active format */
+	struct v4l2_mbus_framefmt mbus_format;
+	int frame_size;
+};
+
+static int vimc_sen_enum_mbus_code(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_pad_config *cfg,
+				   struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct vimc_sen_device *vsen =
+				container_of(sd, struct vimc_sen_device, sd);
+
+	code->code = vsen->mbus_format.code;
+
+	return 0;
+}
+
+static int vimc_sen_enum_frame_size(struct v4l2_subdev *sd,
+				    struct v4l2_subdev_pad_config *cfg,
+				    struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct vimc_sen_device *vsen =
+				container_of(sd, struct vimc_sen_device, sd);
+
+	/* TODO: Add support to other formats */
+	if (fse->index)
+		return -EINVAL;
+
+	/* TODO: Add support for other codes */
+	if (fse->code != vsen->mbus_format.code)
+		return -EINVAL;
+
+	fse->min_width = vsen->mbus_format.width;
+	fse->max_width = vsen->mbus_format.width;
+	fse->min_height = vsen->mbus_format.height;
+	fse->max_height = vsen->mbus_format.height;
+
+	return 0;
+}
+
+static int vimc_sen_get_fmt(struct v4l2_subdev *sd,
+			    struct v4l2_subdev_pad_config *cfg,
+			    struct v4l2_subdev_format *format)
+{
+	struct vimc_sen_device *vsen =
+				container_of(sd, struct vimc_sen_device, sd);
+
+	format->format = vsen->mbus_format;
+
+	return 0;
+}
+
+static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = {
+	.enum_mbus_code		= vimc_sen_enum_mbus_code,
+	.enum_frame_size	= vimc_sen_enum_frame_size,
+	.get_fmt		= vimc_sen_get_fmt,
+	/* TODO: Add support to other formats */
+	.set_fmt		= vimc_sen_get_fmt,
+};
+
+/* media operations */
+static const struct media_entity_operations vimc_sen_mops = {
+	.link_validate = v4l2_subdev_link_validate,
+};
+
+static int vimc_thread_sen(void *data)
+{
+	struct vimc_sen_device *vsen = data;
+	unsigned int i;
+
+	set_freezable();
+	set_current_state(TASK_UNINTERRUPTIBLE);
+
+	for (;;) {
+		try_to_freeze();
+		if (kthread_should_stop())
+			break;
+
+		memset(vsen->frame, 100, vsen->frame_size);
+
+		/* Send the frame to all source pads */
+		for (i = 0; i < vsen->sd.entity.num_pads; i++)
+			vimc_propagate_frame(vsen->dev,
+					     &vsen->sd.entity.pads[i],
+					     vsen->frame);
+
+		/* 60 frames per second */
+		schedule_timeout(HZ/60);
+	}
+
+	return 0;
+}
+
+static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct vimc_sen_device *vsen =
+				container_of(sd, struct vimc_sen_device, sd);
+	int ret;
+
+	if (enable) {
+		const struct vimc_pix_map *vpix;
+
+		if (vsen->kthread_sen)
+			return -EINVAL;
+
+		/* Calculate the frame size */
+		vpix = vimc_pix_map_by_code(vsen->mbus_format.code);
+		vsen->frame_size = vsen->mbus_format.width * vpix->bpp *
+				   vsen->mbus_format.height;
+
+		/*
+		 * Allocate the frame buffer. Use vmalloc to be able to
+		 * allocate a large amount of memory
+		 */
+		vsen->frame = vmalloc(vsen->frame_size);
+		if (!vsen->frame)
+			return -ENOMEM;
+
+		/* Initialize the image generator thread */
+		vsen->kthread_sen = kthread_run(vimc_thread_sen, vsen,
+						"%s-sen", vsen->v4l2_dev->name);
+		if (IS_ERR(vsen->kthread_sen)) {
+			dev_err(vsen->dev, "kernel_thread() failed\n");
+			vfree(vsen->frame);
+			vsen->frame = NULL;
+			return PTR_ERR(vsen->kthread_sen);
+		}
+	} else {
+		if (!vsen->kthread_sen)
+			return -EINVAL;
+
+		/* Stop image generator */
+		ret = kthread_stop(vsen->kthread_sen);
+		vsen->kthread_sen = NULL;
+
+		vfree(vsen->frame);
+		vsen->frame = NULL;
+		return ret;
+	}
+
+	return 0;
+}
+
+struct v4l2_subdev_video_ops vimc_sen_video_ops = {
+	.s_stream = vimc_sen_s_stream,
+};
+
+static const struct v4l2_subdev_ops vimc_sen_ops = {
+	.pad = &vimc_sen_pad_ops,
+	.video = &vimc_sen_video_ops,
+};
+
+static void vimc_sen_destroy(struct vimc_ent_device *ved)
+{
+	struct vimc_sen_device *vsen =
+				container_of(ved, struct vimc_sen_device, ved);
+
+	media_entity_cleanup(ved->ent);
+	v4l2_device_unregister_subdev(&vsen->sd);
+	kfree(vsen);
+}
+
+struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag)
+{
+	struct vimc_sen_device *vsen;
+	unsigned int i;
+	int ret;
+
+	/* NOTE: a sensor node may be created with more then one pad */
+	if (!name || !num_pads || !pads_flag)
+		return ERR_PTR(-EINVAL);
+
+	/* check if all pads are sources */
+	for (i = 0; i < num_pads; i++)
+		if (!(pads_flag[i] & MEDIA_PAD_FL_SOURCE))
+			return ERR_PTR(-EINVAL);
+
+	/* Allocate the vsen struct */
+	vsen = kzalloc(sizeof(*vsen), GFP_KERNEL);
+	if (!vsen)
+		return ERR_PTR(-ENOMEM);
+
+	/* Link the vimc_sen_device struct with the v4l2 parent */
+	vsen->v4l2_dev = v4l2_dev;
+	/* Link the vimc_sen_device struct with the dev parent */
+	vsen->dev = v4l2_dev->dev;
+
+	/* Allocate the pads */
+	vsen->ved.pads = vimc_pads_init(num_pads, pads_flag);
+	if (IS_ERR(vsen->ved.pads)) {
+		ret = PTR_ERR(vsen->ved.pads);
+		goto err_free_vsen;
+	}
+
+	/* Initialize the media entity */
+	vsen->sd.entity.name = name;
+	ret = media_entity_pads_init(&vsen->sd.entity,
+				     num_pads, vsen->ved.pads);
+	if (ret)
+		goto err_clean_pads;
+
+	/* Set the active frame format (this is hardcoded for now) */
+	vsen->mbus_format.width = 640;
+	vsen->mbus_format.height = 480;
+	vsen->mbus_format.code = MEDIA_BUS_FMT_RGB888_1X24;
+	vsen->mbus_format.field = V4L2_FIELD_NONE;
+	vsen->mbus_format.colorspace = V4L2_COLORSPACE_SRGB;
+	vsen->mbus_format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
+	vsen->mbus_format.xfer_func = V4L2_XFER_FUNC_SRGB;
+
+	/* Fill the vimc_ent_device struct */
+	vsen->ved.destroy = vimc_sen_destroy;
+	vsen->ved.ent = &vsen->sd.entity;
+
+	/* Initialize the subdev */
+	v4l2_subdev_init(&vsen->sd, &vimc_sen_ops);
+	vsen->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+	vsen->sd.entity.ops = &vimc_sen_mops;
+	vsen->sd.owner = THIS_MODULE;
+	strlcpy(vsen->sd.name, name, sizeof(vsen->sd.name));
+	v4l2_set_subdevdata(&vsen->sd, &vsen->ved);
+
+	/* Expose this subdev to user space */
+	vsen->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	/* Register the subdev with the v4l2 and the media framework */
+	ret = v4l2_device_register_subdev(vsen->v4l2_dev, &vsen->sd);
+	if (ret) {
+		dev_err(vsen->dev,
+			"subdev register failed (err=%d)\n", ret);
+		goto err_clean_m_ent;
+	}
+
+	return &vsen->ved;
+
+err_clean_m_ent:
+	media_entity_cleanup(&vsen->sd.entity);
+err_clean_pads:
+	vimc_pads_cleanup(vsen->ved.pads);
+err_free_vsen:
+	kfree(vsen);
+
+	return ERR_PTR(ret);
+}
diff --git a/drivers/media/platform/vimc/vimc-sensor.h b/drivers/media/platform/vimc/vimc-sensor.h
new file mode 100644
index 0000000..ef48362
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-sensor.h
@@ -0,0 +1,28 @@
+/*
+ * vimc-sensor.h Virtual Media Controller Driver
+ *
+ * Copyright (C) 2017 Helen Koike F. <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIMC_SENSOR_H_
+#define _VIMC_SENSOR_H_
+
+#include "vimc-core.h"
+
+struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag);
+
+#endif
-- 
2.7.4

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

* Re: [PATCH v7] [media] vimc: Virtual Media Controller core, capture and sensor
  2017-03-25 17:11                 ` [PATCH v7] " Helen Koike
@ 2017-03-25 23:04                   ` Helen Koike
  2017-03-26 13:31                   ` Sakari Ailus
  2017-03-27  9:00                   ` Hans Verkuil
  2 siblings, 0 replies; 41+ messages in thread
From: Helen Koike @ 2017-03-25 23:04 UTC (permalink / raw)
  To: Helen Koike
  Cc: linux-media, jgebben, mchehab, Helen Fornazier, Sakari Ailus,
	Laurent Pinchart, Hans Verkuil


On 2017-03-25 02:11 PM, Helen Koike wrote:
> First version of the Virtual Media Controller.
> Add a simple version of the core of the driver, the capture and
> sensor nodes in the topology, generating a grey image in a hardcoded
> format.
>
> Signed-off-by: Helen Koike <helen.koike@collabora.com>
>
> ---
>
> Patch based in media/master tree, and available here:
> https://github.com/helen-fornazier/opw-staging/tree/vimc/devel/v7
>
> Changes since v6:
> - add kernel-docs in vimc-core.h
> - reorder list_del call in vimc_cap_return_all_buffers()
> - call media_pipeline_stop in vimc_cap_start_streaming when fail
> - remove DMA comment (left over from the sample driver)
> - remove vb2_set_plane_payload call in vimc_cap_buffer_prepare
> - remove format verification in vimc_cap_link_validate
> - set vimc_pix_map_list as static
> - use MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN in vimc_raw_create()
> - register media device after creating the topology and unregister it
> before destroying the topology
> - replace devm_kzalloc by kzalloc for allocating struct vimc_device
> - uset TASK_UNINTERRUPTIBLE for vimc_thread_sen
> - do not allow the creation of a sensor with no pads
> - add more verbose description in Kconfig
> - change copyright to 2017
> - arrange includes: number before letters
> - remove v4l2_dev pointer from vimc_cap_device structure
> - coding style adjustments
> - remove entity variable in vimc_cap_pipeline_s_stream
> - declare and assign variables in vimc_cap_link_validate
> - declare vimc_dev_release() and vimc_pdev closer to vimc_init()
> - remove pad check in vimc_sen_enum_{mbus_code,frame_size} that is
> already performed by v4l2-subdev.c
> - fix multiline comments to start with /*
> - reorder variable declaration in functions
> - remove pad and subdevice type check in vimc_cap_pipeline_s_stream
> - add a note that sensor nodes can can have more then one source pad
> - remove the use of entity->use_count in the core and attach the ved
> structure in sd or vd, use
>   ent->obj_type to know which structure (sd or vd) to use
> - rename vdev (video_device) to vd to be similar to sd (subdev)
>
> Changes since v5:
> - Fix message "Entity type for entity Sensor A was not initialized!"
>   by initializing the sensor entity.function after the calling
>   v4l2_subded_init
> - populate device_caps in vimc_cap_create instead of in
>   vimc_cap_querycap
> - Fix typo in vimc-core.c s/de/the
>
> Changes since v4:
> - coding style fixes
> - remove BUG_ON
> - change copyright to 2016
> - depens on VIDEO_V4L2_SUBDEV_API instead of select
> - remove assignement of V4L2_CAP_DEVICE_CAPS
> - s/vimc_cap_g_fmt_vid_cap/vimc_cap_fmt_vid_cap
> - fix vimc_cap_queue_setup declaration type
> - remove wrong buffer size check and add it in vimc_cap_buffer_prepare
> - vimc_cap_create: remove unecessary check if v4l2_dev or
> v4l2_dev->dev is null
> - vimc_cap_create: only allow a single pad
> - vimc_sen_create: only allow source pads, remove unecessary source pads
> checks in vimc_thread_sen
>
> Changes since v3: fix rmmod crash and built-in compile
> - Re-order unregister calls in vimc_device_unregister function (remove
> rmmod issue)
> - Call media_device_unregister_entity in vimc_raw_destroy
> - Add depends on VIDEO_DEV && VIDEO_V4L2 and select VIDEOBUF2_VMALLOC
> - Check *nplanes in queue_setup (this remove v4l2-compliance fail)
> - Include <linux/kthread.h> in vimc-sensor.c
> - Move include of <linux/slab.h> from vimc-core.c to vimc-core.h
> - Generate 60 frames per sec instead of 1 in the sensor
>
> Changes since v2: update with current media master tree
> - Add struct media_pipeline in vimc_cap_device
> - Use vb2_v4l2_buffer instead of vb2_buffer
> - Typos
> - Remove usage of entity->type and use entity->function instead
> - Remove fmt argument from queue setup
> - Use ktime_get_ns instead of v4l2_get_timestamp
> - Iterate over link's list using list_for_each_entry
> - Use media_device_{init, cleanup}
> - Use entity->use_count to keep track of entities instead of the old
> entity->id
> - Replace media_entity_init by media_entity_pads_init
> ---
>  drivers/media/platform/Kconfig             |   2 +
>  drivers/media/platform/Makefile            |   1 +
>  drivers/media/platform/vimc/Kconfig        |  14 +
>  drivers/media/platform/vimc/Makefile       |   3 +
>  drivers/media/platform/vimc/vimc-capture.c | 536 ++++++++++++++++++++++
>  drivers/media/platform/vimc/vimc-capture.h |  28 ++
>  drivers/media/platform/vimc/vimc-core.c    | 696
> +++++++++++++++++++++++++++++
>  drivers/media/platform/vimc/vimc-core.h    | 123 +++++
>  drivers/media/platform/vimc/vimc-sensor.c  | 279 ++++++++++++
>  drivers/media/platform/vimc/vimc-sensor.h  |  28 ++
>  10 files changed, 1710 insertions(+)
>  create mode 100644 drivers/media/platform/vimc/Kconfig
>  create mode 100644 drivers/media/platform/vimc/Makefile
>  create mode 100644 drivers/media/platform/vimc/vimc-capture.c
>  create mode 100644 drivers/media/platform/vimc/vimc-capture.h
>  create mode 100644 drivers/media/platform/vimc/vimc-core.c
>  create mode 100644 drivers/media/platform/vimc/vimc-core.h
>  create mode 100644 drivers/media/platform/vimc/vimc-sensor.c
>  create mode 100644 drivers/media/platform/vimc/vimc-sensor.h
>
> diff --git a/drivers/media/platform/Kconfig
> b/drivers/media/platform/Kconfig
> index 53f6f12..6dab7e6 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -466,6 +466,8 @@ menuconfig V4L_TEST_DRIVERS
>
>  if V4L_TEST_DRIVERS
>
> +source "drivers/media/platform/vimc/Kconfig"
> +
>  source "drivers/media/platform/vivid/Kconfig"
>
>  config VIDEO_VIM2M
> diff --git a/drivers/media/platform/Makefile
> b/drivers/media/platform/Makefile
> index 8959f6e..4af4cce 100644
> --- a/drivers/media/platform/Makefile
> +++ b/drivers/media/platform/Makefile
> @@ -13,6 +13,7 @@ obj-$(CONFIG_VIDEO_PXA27x)    += pxa_camera.o
>
>  obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o
>
> +obj-$(CONFIG_VIDEO_VIMC)        += vimc/
>  obj-$(CONFIG_VIDEO_VIVID)        += vivid/
>  obj-$(CONFIG_VIDEO_VIM2M)        += vim2m.o
>
> diff --git a/drivers/media/platform/vimc/Kconfig
> b/drivers/media/platform/vimc/Kconfig
> new file mode 100644
> index 0000000..dd285fa
> --- /dev/null
> +++ b/drivers/media/platform/vimc/Kconfig
> @@ -0,0 +1,14 @@
> +config VIDEO_VIMC
> +    tristate "Virtual Media Controller Driver (VIMC)"
> +    depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
> +    select VIDEOBUF2_VMALLOC
> +    default n
> +    ---help---
> +      Skeleton driver for Virtual Media Controller
> +
> +      This drivers can be compared to the Vivid driver for emulating
> +      a media node that exposes a complex media topology. The topology
> +      is hard coded for now but is meant to be highly configurable in
> +      the future.
> +
> +      When in doubt, say N.
> diff --git a/drivers/media/platform/vimc/Makefile
> b/drivers/media/platform/vimc/Makefile
> new file mode 100644
> index 0000000..c45195e
> --- /dev/null
> +++ b/drivers/media/platform/vimc/Makefile
> @@ -0,0 +1,3 @@
> +vimc-objs := vimc-core.o vimc-capture.o vimc-sensor.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
> new file mode 100644
> index 0000000..8f77fc8
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-capture.c
> @@ -0,0 +1,536 @@
> +/*
> + * vimc-capture.c Virtual Media Controller Driver
> + *
> + * Copyright (C) 2017 Helen Koike F. <helen.fornazier@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <media/v4l2-ioctl.h>
> +#include <media/videobuf2-core.h>
> +#include <media/videobuf2-vmalloc.h>
> +
> +#include "vimc-capture.h"
> +
> +struct vimc_cap_device {
> +    struct vimc_ent_device ved;
> +    struct video_device vd;
> +    struct device *dev;
> +    struct v4l2_pix_format format;
> +    struct vb2_queue queue;
> +    struct list_head buf_list;
> +    /*
> +     * NOTE: in a real driver, a spin lock must be used to access the
> +     * queue because the frames are generated from a hardware
> interruption
> +     * and the isr is not allowed to sleep.
> +     * Even if it is not necessary a spinlock in the vimc driver, we
> +     * use it here as a code reference
> +     */
> +    spinlock_t qlock;
> +    struct mutex lock;
> +    u32 sequence;
> +    struct media_pipeline pipe;
> +};
> +
> +struct vimc_cap_buffer {
> +    /*
> +     * struct vb2_v4l2_buffer must be the first element
> +     * the videobuf2 framework will allocate this struct based on
> +     * buf_struct_size and use the first sizeof(struct vb2_buffer)
> bytes of
> +     * memory as a vb2_buffer
> +     */
> +    struct vb2_v4l2_buffer vb2;
> +    struct list_head list;
> +};
> +
> +static int vimc_cap_querycap(struct file *file, void *priv,
> +                 struct v4l2_capability *cap)
> +{
> +    struct vimc_cap_device *vcap = video_drvdata(file);
> +
> +    strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
> +    strlcpy(cap->card, KBUILD_MODNAME, sizeof(cap->card));
> +    snprintf(cap->bus_info, sizeof(cap->bus_info),
> +         "platform:%s", vcap->vd.v4l2_dev->name);
> +
> +    return 0;
> +}
> +
> +static int vimc_cap_enum_input(struct file *file, void *priv,
> +                   struct v4l2_input *i)
> +{
> +    /* We only have one input */
> +    if (i->index > 0)
> +        return -EINVAL;
> +
> +    i->type = V4L2_INPUT_TYPE_CAMERA;
> +    strlcpy(i->name, "VIMC capture", sizeof(i->name));
> +
> +    return 0;
> +}
> +
> +static int vimc_cap_g_input(struct file *file, void *priv, unsigned
> int *i)
> +{
> +    /* We only have one input */
> +    *i = 0;
> +    return 0;
> +}
> +
> +static int vimc_cap_s_input(struct file *file, void *priv, unsigned
> int i)
> +{
> +    /* We only have one input */
> +    return i ? -EINVAL : 0;
> +}
> +
> +static int vimc_cap_fmt_vid_cap(struct file *file, void *priv,
> +                  struct v4l2_format *f)
> +{
> +    struct vimc_cap_device *vcap = video_drvdata(file);
> +
> +    f->fmt.pix = vcap->format;
> +
> +    return 0;
> +}
> +
> +static int vimc_cap_enum_fmt_vid_cap(struct file *file, void *priv,
> +                     struct v4l2_fmtdesc *f)
> +{
> +    struct vimc_cap_device *vcap = video_drvdata(file);
> +
> +    if (f->index > 0)
> +        return -EINVAL;
> +
> +    /* We only support one format for now */
> +    f->pixelformat = vcap->format.pixelformat;
> +
> +    return 0;
> +}
> +
> +static const struct v4l2_file_operations vimc_cap_fops = {
> +    .owner        = THIS_MODULE,
> +    .open        = v4l2_fh_open,
> +    .release    = vb2_fop_release,
> +    .read           = vb2_fop_read,
> +    .poll        = vb2_fop_poll,
> +    .unlocked_ioctl = video_ioctl2,
> +    .mmap           = vb2_fop_mmap,
> +};
> +
> +static const struct v4l2_ioctl_ops vimc_cap_ioctl_ops = {
> +    .vidioc_querycap = vimc_cap_querycap,
> +
> +    .vidioc_enum_input = vimc_cap_enum_input,
> +    .vidioc_g_input = vimc_cap_g_input,
> +    .vidioc_s_input = vimc_cap_s_input,
> +
> +    .vidioc_g_fmt_vid_cap = vimc_cap_fmt_vid_cap,
> +    .vidioc_s_fmt_vid_cap = vimc_cap_fmt_vid_cap,
> +    .vidioc_try_fmt_vid_cap = vimc_cap_fmt_vid_cap,
> +    .vidioc_enum_fmt_vid_cap = vimc_cap_enum_fmt_vid_cap,
> +
> +    .vidioc_reqbufs = vb2_ioctl_reqbufs,
> +    .vidioc_create_bufs = vb2_ioctl_create_bufs,
> +    .vidioc_querybuf = vb2_ioctl_querybuf,
> +    .vidioc_qbuf = vb2_ioctl_qbuf,
> +    .vidioc_dqbuf = vb2_ioctl_dqbuf,
> +    .vidioc_expbuf = vb2_ioctl_expbuf,
> +    .vidioc_streamon = vb2_ioctl_streamon,
> +    .vidioc_streamoff = vb2_ioctl_streamoff,
> +};
> +
> +static void vimc_cap_return_all_buffers(struct vimc_cap_device *vcap,
> +                    enum vb2_buffer_state state)
> +{
> +    struct vimc_cap_buffer *vbuf, *node;
> +
> +    spin_lock(&vcap->qlock);
> +
> +    list_for_each_entry_safe(vbuf, node, &vcap->buf_list, list) {
> +        list_del(&vbuf->list);
> +        vb2_buffer_done(&vbuf->vb2.vb2_buf, state);
> +    }
> +
> +    spin_unlock(&vcap->qlock);
> +}
> +
> +static int vimc_cap_pipeline_s_stream(struct vimc_cap_device *vcap,
> int enable)
> +{
> +    struct v4l2_subdev *sd;
> +    struct media_pad *pad;
> +    int ret;
> +
> +    /* Start the stream in the subdevice direct connected */
> +    pad = media_entity_remote_pad(&vcap->vd.entity.pads[0]);
> +
> +    /*
> +     * if it is a raw node from vimc-core, there is nothing to activate
> +     * TODO: remove this when there are no more raw nodes in the
> +     * core and return error instead
> +     */
> +    if (pad->entity->obj_type == MEDIA_ENTITY_TYPE_BASE)
> +        return 0;
> +
> +    sd = media_entity_to_v4l2_subdev(pad->entity);
> +    ret = v4l2_subdev_call(sd, video, s_stream, enable);
> +    if (ret && ret != -ENOIOCTLCMD)
> +        return ret;
> +
> +    return 0;
> +}
> +
> +static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned
> int count)
> +{
> +    struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
> +    struct media_entity *entity = &vcap->vd.entity;
> +    int ret;
> +
> +    vcap->sequence = 0;
> +
> +    /* Start the media pipeline */
> +    ret = media_pipeline_start(entity, &vcap->pipe);
> +    if (ret) {
> +        vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
> +        return ret;
> +    }
> +
> +    /* Enable streaming from the pipe */
> +    ret = vimc_cap_pipeline_s_stream(vcap, 1);
> +    if (ret) {
> +        media_pipeline_stop(entity);
> +        vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
> +        return ret;
> +    }
> +
> +    return 0;
> +}
> +
> +/*
> + * Stop the stream engine. Any remaining buffers in the stream queue are
> + * dequeued and passed on to the vb2 framework marked as STATE_ERROR.
> + */
> +static void vimc_cap_stop_streaming(struct vb2_queue *vq)
> +{
> +    struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
> +
> +    /* Disable streaming from the pipe */
> +    vimc_cap_pipeline_s_stream(vcap, 0);
> +
> +    /* Stop the media pipeline */
> +    media_pipeline_stop(&vcap->vd.entity);
> +
> +    /* Release all active buffers */
> +    vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_ERROR);
> +}
> +
> +static void vimc_cap_buf_queue(struct vb2_buffer *vb2_buf)
> +{
> +    struct vimc_cap_device *vcap = vb2_get_drv_priv(vb2_buf->vb2_queue);
> +    struct vimc_cap_buffer *buf = container_of(vb2_buf,
> +                           struct vimc_cap_buffer,
> +                           vb2.vb2_buf);
> +
> +    spin_lock(&vcap->qlock);
> +    list_add_tail(&buf->list, &vcap->buf_list);
> +    spin_unlock(&vcap->qlock);
> +}
> +
> +static int vimc_cap_queue_setup(struct vb2_queue *vq, unsigned int
> *nbuffers,
> +                unsigned int *nplanes, unsigned int sizes[],
> +                struct device *alloc_devs[])
> +{
> +    struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
> +
> +    if (*nplanes)
> +        return sizes[0] < vcap->format.sizeimage ? -EINVAL : 0;
> +    /* We don't support multiplanes for now */
> +    *nplanes = 1;
> +    sizes[0] = vcap->format.sizeimage;
> +
> +    return 0;
> +}
> +
> +static int vimc_cap_buffer_prepare(struct vb2_buffer *vb)
> +{
> +    struct vimc_cap_device *vcap = vb2_get_drv_priv(vb->vb2_queue);
> +    unsigned long size = vcap->format.sizeimage;
> +
> +    if (vb2_plane_size(vb, 0) < size) {
> +        dev_err(vcap->dev, "buffer too small (%lu < %lu)\n",
> +            vb2_plane_size(vb, 0), size);
> +        return -EINVAL;
> +    }
> +    return 0;
> +}
> +
> +static const struct vb2_ops vimc_cap_qops = {
> +    .start_streaming    = vimc_cap_start_streaming,
> +    .stop_streaming        = vimc_cap_stop_streaming,
> +    .buf_queue        = vimc_cap_buf_queue,
> +    .queue_setup        = vimc_cap_queue_setup,
> +    .buf_prepare        = vimc_cap_buffer_prepare,
> +    /*
> +     * Since q->lock is set we can use the standard
> +     * vb2_ops_wait_prepare/finish helper functions.
> +     */
> +    .wait_prepare        = vb2_ops_wait_prepare,
> +    .wait_finish        = vb2_ops_wait_finish,
> +};
> +
> +/*
> + * NOTE: this function is a copy of v4l2_subdev_link_validate_get_format
> + * maybe the v4l2 function should be public
> + */
> +static int vimc_cap_v4l2_subdev_link_validate_get_format(struct
> media_pad *pad,
> +                        struct v4l2_subdev_format *fmt)
> +{
> +    struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity);
> +
> +    fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +    fmt->pad = pad->index;
> +
> +    return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt);
> +}
> +
> +static int vimc_cap_link_validate(struct media_link *link)
> +{
> +    struct v4l2_subdev_format source_fmt;
> +    const struct vimc_pix_map *vpix;
> +    struct vimc_cap_device *vcap = container_of(link->sink->entity,
> +                            struct vimc_cap_device,
> +                            vd.entity);
> +    struct v4l2_pix_format *sink_fmt = &vcap->format;
> +    int ret;
> +
> +    /*
> +     * if it is a raw node from vimc-core, ignore the link for now
> +     * TODO: remove this when there are no more raw nodes in the
> +     * core and return error instead
> +     */
> +    if (link->source->entity->obj_type == MEDIA_ENTITY_TYPE_BASE)
> +        return 0;
> +
> +    /* Get the the format of the subdev */
> +    ret = vimc_cap_v4l2_subdev_link_validate_get_format(link->source,
> +                                &source_fmt);
> +    if (ret)
> +        return ret;
> +
> +    dev_dbg(vcap->dev,
> +        "cap: %s: link validate formats src:%dx%d %d sink:%dx%d %d\n",
> +        vcap->vd.name,
> +        source_fmt.format.width, source_fmt.format.height,
> +        source_fmt.format.code,
> +        sink_fmt->width, sink_fmt->height,
> +        sink_fmt->pixelformat);
> +
> +    /* The width, height and code must match. */
> +    vpix = vimc_pix_map_by_pixelformat(sink_fmt->pixelformat);
> +    if (source_fmt.format.width != sink_fmt->width
> +        || source_fmt.format.height != sink_fmt->height
> +        || vpix->code != source_fmt.format.code)
> +        return -EINVAL;
> +
> +    /*
> +     * The field order must match, or the sink field order must be NONE
> +     * to support interlaced hardware connected to bridges that support
> +     * progressive formats only.
> +     */
> +    if (source_fmt.format.field != sink_fmt->field &&
> +        sink_fmt->field != V4L2_FIELD_NONE)
> +        return -EINVAL;
> +
> +    return 0;
> +}
> +
> +static const struct media_entity_operations vimc_cap_mops = {
> +    .link_validate        = vimc_cap_link_validate,
> +};
> +
> +static void vimc_cap_destroy(struct vimc_ent_device *ved)
> +{
> +    struct vimc_cap_device *vcap = container_of(ved, struct
> vimc_cap_device,
> +                            ved);
> +
> +    vb2_queue_release(&vcap->queue);
> +    media_entity_cleanup(ved->ent);
> +    video_unregister_device(&vcap->vd);
> +    vimc_pads_cleanup(vcap->ved.pads);
> +    kfree(vcap);
> +}
> +
> +static void vimc_cap_process_frame(struct vimc_ent_device *ved,
> +                   struct media_pad *sink, const void *frame)
> +{
> +    struct vimc_cap_device *vcap = container_of(ved, struct
> vimc_cap_device,
> +                            ved);
> +    struct vimc_cap_buffer *vimc_buf;
> +    void *vbuf;
> +
> +    /* If the stream in this node is not active, just return */
> +    mutex_lock(&vcap->lock);
> +    if (!vb2_is_busy(&vcap->queue)) {
> +        mutex_unlock(&vcap->lock);
> +        return;
> +    }
> +    mutex_unlock(&vcap->lock);
> +
> +    spin_lock(&vcap->qlock);
> +
> +    /* Get the first entry of the list */
> +    vimc_buf = list_first_entry_or_null(&vcap->buf_list,
> +                        typeof(*vimc_buf), list);
> +    if (!vimc_buf) {
> +        spin_unlock(&vcap->qlock);
> +        return;
> +    }
> +
> +    /* Remove this entry from the list */
> +    list_del(&vimc_buf->list);
> +
> +    spin_unlock(&vcap->qlock);
> +
> +    /* Fill the buffer */
> +    vimc_buf->vb2.vb2_buf.timestamp = ktime_get_ns();
> +    vimc_buf->vb2.sequence = vcap->sequence++;
> +    vimc_buf->vb2.field = vcap->format.field;
> +
> +    vbuf = vb2_plane_vaddr(&vimc_buf->vb2.vb2_buf, 0);
> +
> +    memcpy(vbuf, frame, vcap->format.sizeimage);
> +
> +    /* Set it as ready */
> +    vb2_set_plane_payload(&vimc_buf->vb2.vb2_buf, 0,
> +                  vcap->format.sizeimage);
> +    vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE);
> +}
> +
> +struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
> +                    const char *const name,
> +                    u16 num_pads,
> +                    const unsigned long *pads_flag)
> +{
> +    const struct vimc_pix_map *vpix;
> +    struct vimc_cap_device *vcap;
> +    struct video_device *vd;
> +    struct vb2_queue *q;
> +    int ret;
> +
> +    /*
> +     * Check entity configuration params
> +     * NOTE: we only support a single sink pad
> +     */
> +    if (!name || num_pads != 1 || !pads_flag ||
> +        !(pads_flag[0] & MEDIA_PAD_FL_SINK))
> +        return ERR_PTR(-EINVAL);
> +
> +    /* Allocate the vimc_cap_device struct */
> +    vcap = kzalloc(sizeof(*vcap), GFP_KERNEL);
> +    if (!vcap)
> +        return ERR_PTR(-ENOMEM);
> +
> +    /* Link the vimc_cap_device struct with dev parent */
> +    vcap->dev = v4l2_dev->dev;
> +
> +    /* Allocate the pads */
> +    vcap->ved.pads = vimc_pads_init(num_pads, pads_flag);
> +    if (IS_ERR(vcap->ved.pads)) {
> +        ret = PTR_ERR(vcap->ved.pads);
> +        goto err_free_vcap;
> +    }
> +
> +    /* Initialize the media entity */
> +    vcap->vd.entity.name = name;
> +    vcap->vd.entity.function = MEDIA_ENT_F_IO_V4L;
> +    ret = media_entity_pads_init(&vcap->vd.entity,
> +                     num_pads, vcap->ved.pads);
> +    if (ret)
> +        goto err_clean_pads;
> +
> +    /* Initialize the lock */
> +    mutex_init(&vcap->lock);
> +
> +    /* Initialize the vb2 queue */
> +    q = &vcap->queue;
> +    q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +    q->io_modes = VB2_MMAP | VB2_DMABUF;
> +    q->drv_priv = vcap;
> +    q->buf_struct_size = sizeof(struct vimc_cap_buffer);
> +    q->ops = &vimc_cap_qops;
> +    q->mem_ops = &vb2_vmalloc_memops;
> +    q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> +    q->min_buffers_needed = 2;
> +    q->lock = &vcap->lock;
> +
> +    ret = vb2_queue_init(q);
> +    if (ret) {
> +        dev_err(vcap->dev,
> +            "vb2 queue init failed (err=%d)\n", ret);
> +        goto err_clean_m_ent;
> +    }
> +
> +    /* Initialize buffer list and its lock */
> +    INIT_LIST_HEAD(&vcap->buf_list);
> +    spin_lock_init(&vcap->qlock);
> +
> +    /* Set the frame format (this is hardcoded for now) */
> +    vcap->format.width = 640;
> +    vcap->format.height = 480;
> +    vcap->format.pixelformat = V4L2_PIX_FMT_RGB24;
> +    vcap->format.field = V4L2_FIELD_NONE;
> +    vcap->format.colorspace = V4L2_COLORSPACE_SRGB;
> +
> +    vpix = vimc_pix_map_by_pixelformat(vcap->format.pixelformat);
> +
> +    vcap->format.bytesperline = vcap->format.width * vpix->bpp;
> +    vcap->format.sizeimage = vcap->format.bytesperline *
> +                 vcap->format.height;
> +
> +    /* Fill the vimc_ent_device struct */
> +    vcap->ved.destroy = vimc_cap_destroy;
> +    vcap->ved.ent = &vcap->vd.entity;
> +    vcap->ved.process_frame = vimc_cap_process_frame;
> +
> +    /* Initialize the video_device struct */
> +    vd = &vcap->vd;
> +    vd->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
> +    vd->entity.ops = &vimc_cap_mops;
> +    vd->release = video_device_release_empty;
> +    vd->fops = &vimc_cap_fops;
> +    vd->ioctl_ops = &vimc_cap_ioctl_ops;
> +    vd->lock = &vcap->lock;
> +    vd->queue = q;
> +    vd->v4l2_dev = v4l2_dev;
> +    vd->vfl_dir = VFL_DIR_RX;
> +    strlcpy(vd->name, name, sizeof(vd->name));
> +    video_set_drvdata(vd, &vcap->ved);
> +
> +    /* Register the video_device with the v4l2 and the media framework */
> +    ret = video_register_device(vd, VFL_TYPE_GRABBER, -1);
> +    if (ret) {
> +        dev_err(vcap->dev,
> +            "video register failed (err=%d)\n", ret);
> +        goto err_release_queue;
> +    }
> +
> +    return &vcap->ved;
> +
> +err_release_queue:
> +    vb2_queue_release(q);
> +err_clean_m_ent:
> +    media_entity_cleanup(&vcap->vd.entity);
> +err_clean_pads:
> +    vimc_pads_cleanup(vcap->ved.pads);
> +err_free_vcap:
> +    kfree(vcap);
> +
> +    return ERR_PTR(ret);
> +}
> diff --git a/drivers/media/platform/vimc/vimc-capture.h
> b/drivers/media/platform/vimc/vimc-capture.h
> new file mode 100644
> index 0000000..c7e633d
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-capture.h
> @@ -0,0 +1,28 @@
> +/*
> + * vimc-capture.h Virtual Media Controller Driver
> + *
> + * Copyright (C) 2017 Helen Koike F. <helen.fornazier@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef _VIMC_CAPTURE_H_
> +#define _VIMC_CAPTURE_H_
> +
> +#include "vimc-core.h"
> +
> +struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
> +                    const char *const name,
> +                    u16 num_pads,
> +                    const unsigned long *pads_flag);
> +
> +#endif
> diff --git a/drivers/media/platform/vimc/vimc-core.c
> b/drivers/media/platform/vimc/vimc-core.c
> new file mode 100644
> index 0000000..2dc2ca8
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-core.c
> @@ -0,0 +1,696 @@
> +/*
> + * vimc-core.c Virtual Media Controller Driver
> + *
> + * Copyright (C) 2017 Helen Koike F. <helen.fornazier@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <media/media-device.h>
> +#include <media/v4l2-device.h>
> +
> +#include "vimc-capture.h"
> +#include "vimc-core.h"
> +#include "vimc-sensor.h"
> +
> +#define VIMC_PDEV_NAME "vimc"
> +#define VIMC_MDEV_MODEL_NAME "VIMC MDEV"
> +
> +#define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) {    \
> +    .src_ent = src,                        \
> +    .src_pad = srcpad,                    \
> +    .sink_ent = sink,                    \
> +    .sink_pad = sinkpad,                    \
> +    .flags = link_flags,                    \
> +}
> +
> +struct vimc_device {
> +    /*
> +     * The pipeline configuration
> +     * (filled before calling vimc_device_register)
> +     */
> +    const struct vimc_pipeline_config *pipe_cfg;
> +
> +    /* The Associated media_device parent */
> +    struct media_device mdev;
> +
> +    /* Internal v4l2 parent device*/
> +    struct v4l2_device v4l2_dev;
> +
> +    /* Internal topology */
> +    struct vimc_ent_device **ved;
> +};
> +
> +/**
> + * enum vimc_ent_node - Select the functionality of a node in the
> topology
> + * @VIMC_ENT_NODE_SENSOR:    A node of type SENSOR simulates a camera
> sensor
> + *                generating internal images in bayer format and
> + *                propagating those images through the pipeline
> + * @VIMC_ENT_NODE_CAPTURE:    A node of type CAPTURE is a v4l2
> video_device
> + *                that exposes the received image from the
> + *                pipeline to the user space
> + * @VIMC_ENT_NODE_INPUT:    A node of type INPUT is a v4l2
> video_device that
> + *                receives images from the user space and
> + *                propagates them through the pipeline
> + * @VIMC_ENT_NODE_DEBAYER:    A node type DEBAYER expects to receive
> a frame
> + *                in bayer format converts it to RGB
> + * @VIMC_ENT_NODE_SCALER:    A node of type SCALER scales the
> received image
> + *                by a given multiplier
> + *
> + * This enum is used in the entity configuration struct to allow the
> definition
> + * of a custom topology specifying the role of each node on it.
> + */
> +enum vimc_ent_node {
> +    VIMC_ENT_NODE_SENSOR,
> +    VIMC_ENT_NODE_CAPTURE,
> +    VIMC_ENT_NODE_INPUT,
> +    VIMC_ENT_NODE_DEBAYER,
> +    VIMC_ENT_NODE_SCALER,
> +};
> +
> +/* Structure which describes individual configuration for each entity */
> +struct vimc_ent_config {
> +    const char *name;
> +    size_t pads_qty;
> +    const unsigned long *pads_flag;
> +    enum vimc_ent_node node;
> +};
> +
> +/* Structure which describes links between entities */
> +struct vimc_ent_link {
> +    unsigned int src_ent;
> +    u16 src_pad;
> +    unsigned int sink_ent;
> +    u16 sink_pad;
> +    u32 flags;
> +};
> +
> +/* Structure which describes the whole topology */
> +struct vimc_pipeline_config {
> +    const struct vimc_ent_config *ents;
> +    size_t num_ents;
> +    const struct vimc_ent_link *links;
> +    size_t num_links;
> +};
> +
> +/*
> --------------------------------------------------------------------------
> + * Topology Configuration
> + */
> +
> +static const struct vimc_ent_config ent_config[] = {
> +    {
> +        .name = "Sensor A",
> +        .pads_qty = 1,
> +        .pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
> +        .node = VIMC_ENT_NODE_SENSOR,
> +    },
> +    {
> +        .name = "Sensor B",
> +        .pads_qty = 1,
> +        .pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
> +        .node = VIMC_ENT_NODE_SENSOR,
> +    },
> +    {
> +        .name = "Debayer A",
> +        .pads_qty = 2,
> +        .pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
> +                             MEDIA_PAD_FL_SOURCE},
> +        .node = VIMC_ENT_NODE_DEBAYER,
> +    },
> +    {
> +        .name = "Debayer B",
> +        .pads_qty = 2,
> +        .pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
> +                             MEDIA_PAD_FL_SOURCE},
> +        .node = VIMC_ENT_NODE_DEBAYER,
> +    },
> +    {
> +        .name = "Raw Capture 0",
> +        .pads_qty = 1,
> +        .pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
> +        .node = VIMC_ENT_NODE_CAPTURE,
> +    },
> +    {
> +        .name = "Raw Capture 1",
> +        .pads_qty = 1,
> +        .pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
> +        .node = VIMC_ENT_NODE_CAPTURE,
> +    },
> +    {
> +        .name = "RGB/YUV Input",
> +        .pads_qty = 1,
> +        .pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
> +        .node = VIMC_ENT_NODE_INPUT,
> +    },
> +    {
> +        .name = "Scaler",
> +        .pads_qty = 2,
> +        .pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
> +                             MEDIA_PAD_FL_SOURCE},
> +        .node = VIMC_ENT_NODE_SCALER,
> +    },
> +    {
> +        .name = "RGB/YUV Capture",
> +        .pads_qty = 1,
> +        .pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
> +        .node = VIMC_ENT_NODE_CAPTURE,
> +    },
> +};
> +
> +static const struct vimc_ent_link ent_links[] = {
> +    /* Link: Sensor A (Pad 0)->(Pad 0) Debayer A */
> +    VIMC_ENT_LINK(0, 0, 2, 0, MEDIA_LNK_FL_ENABLED |
> MEDIA_LNK_FL_IMMUTABLE),
> +    /* Link: Sensor A (Pad 0)->(Pad 0) Raw Capture 0 */
> +    VIMC_ENT_LINK(0, 0, 4, 0, MEDIA_LNK_FL_ENABLED |
> MEDIA_LNK_FL_IMMUTABLE),
> +    /* Link: Sensor B (Pad 0)->(Pad 0) Debayer B */
> +    VIMC_ENT_LINK(1, 0, 3, 0, MEDIA_LNK_FL_ENABLED |
> MEDIA_LNK_FL_IMMUTABLE),
> +    /* Link: Sensor B (Pad 0)->(Pad 0) Raw Capture 1 */
> +    VIMC_ENT_LINK(1, 0, 5, 0, MEDIA_LNK_FL_ENABLED |
> MEDIA_LNK_FL_IMMUTABLE),
> +    /* Link: Debayer A (Pad 1)->(Pad 0) Scaler */
> +    VIMC_ENT_LINK(2, 1, 7, 0, MEDIA_LNK_FL_ENABLED),
> +    /* Link: Debayer B (Pad 1)->(Pad 0) Scaler */
> +    VIMC_ENT_LINK(3, 1, 7, 0, 0),
> +    /* Link: RGB/YUV Input (Pad 0)->(Pad 0) Scaler */
> +    VIMC_ENT_LINK(6, 0, 7, 0, 0),
> +    /* Link: Scaler (Pad 1)->(Pad 0) RGB/YUV Capture */
> +    VIMC_ENT_LINK(7, 1, 8, 0, MEDIA_LNK_FL_ENABLED |
> MEDIA_LNK_FL_IMMUTABLE),
> +};
> +
> +static const struct vimc_pipeline_config pipe_cfg = {
> +    .ents        = ent_config,
> +    .num_ents    = ARRAY_SIZE(ent_config),
> +    .links        = ent_links,
> +    .num_links    = ARRAY_SIZE(ent_links)
> +};
> +
> +/*
> --------------------------------------------------------------------------
> */
> +
> +static const struct vimc_pix_map vimc_pix_map_list[] = {
> +    /* TODO: add all missing formats */
> +
> +    /* RGB formats */
> +    {
> +        .code = MEDIA_BUS_FMT_BGR888_1X24,
> +        .pixelformat = V4L2_PIX_FMT_BGR24,
> +        .bpp = 3,
> +    },
> +    {
> +        .code = MEDIA_BUS_FMT_RGB888_1X24,
> +        .pixelformat = V4L2_PIX_FMT_RGB24,
> +        .bpp = 3,
> +    },
> +    {
> +        .code = MEDIA_BUS_FMT_ARGB8888_1X32,
> +        .pixelformat = V4L2_PIX_FMT_ARGB32,
> +        .bpp = 4,
> +    },
> +
> +    /* Bayer formats */
> +    {
> +        .code = MEDIA_BUS_FMT_SBGGR8_1X8,
> +        .pixelformat = V4L2_PIX_FMT_SBGGR8,
> +        .bpp = 1,
> +    },
> +    {
> +        .code = MEDIA_BUS_FMT_SGBRG8_1X8,
> +        .pixelformat = V4L2_PIX_FMT_SGBRG8,
> +        .bpp = 1,
> +    },
> +    {
> +        .code = MEDIA_BUS_FMT_SGRBG8_1X8,
> +        .pixelformat = V4L2_PIX_FMT_SGRBG8,
> +        .bpp = 1,
> +    },
> +    {
> +        .code = MEDIA_BUS_FMT_SRGGB8_1X8,
> +        .pixelformat = V4L2_PIX_FMT_SRGGB8,
> +        .bpp = 1,
> +    },
> +    {
> +        .code = MEDIA_BUS_FMT_SBGGR10_1X10,
> +        .pixelformat = V4L2_PIX_FMT_SBGGR10,
> +        .bpp = 2,
> +    },
> +    {
> +        .code = MEDIA_BUS_FMT_SGBRG10_1X10,
> +        .pixelformat = V4L2_PIX_FMT_SGBRG10,
> +        .bpp = 2,
> +    },
> +    {
> +        .code = MEDIA_BUS_FMT_SGRBG10_1X10,
> +        .pixelformat = V4L2_PIX_FMT_SGRBG10,
> +        .bpp = 2,
> +    },
> +    {
> +        .code = MEDIA_BUS_FMT_SRGGB10_1X10,
> +        .pixelformat = V4L2_PIX_FMT_SRGGB10,
> +        .bpp = 2,
> +    },
> +
> +    /* 10bit raw bayer a-law compressed to 8 bits */
> +    {
> +        .code = MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8,
> +        .pixelformat = V4L2_PIX_FMT_SBGGR10ALAW8,
> +        .bpp = 1,
> +    },
> +    {
> +        .code = MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8,
> +        .pixelformat = V4L2_PIX_FMT_SGBRG10ALAW8,
> +        .bpp = 1,
> +    },
> +    {
> +        .code = MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8,
> +        .pixelformat = V4L2_PIX_FMT_SGRBG10ALAW8,
> +        .bpp = 1,
> +    },
> +    {
> +        .code = MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8,
> +        .pixelformat = V4L2_PIX_FMT_SRGGB10ALAW8,
> +        .bpp = 1,
> +    },
> +
> +    /* 10bit raw bayer DPCM compressed to 8 bits */
> +    {
> +        .code = MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8,
> +        .pixelformat = V4L2_PIX_FMT_SBGGR10DPCM8,
> +        .bpp = 1,
> +    },
> +    {
> +        .code = MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8,
> +        .pixelformat = V4L2_PIX_FMT_SGBRG10DPCM8,
> +        .bpp = 1,
> +    },
> +    {
> +        .code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8,
> +        .pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8,
> +        .bpp = 1,
> +    },
> +    {
> +        .code = MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8,
> +        .pixelformat = V4L2_PIX_FMT_SRGGB10DPCM8,
> +        .bpp = 1,
> +    },
> +    {
> +        .code = MEDIA_BUS_FMT_SBGGR12_1X12,
> +        .pixelformat = V4L2_PIX_FMT_SBGGR12,
> +        .bpp = 2,
> +    },
> +    {
> +        .code = MEDIA_BUS_FMT_SGBRG12_1X12,
> +        .pixelformat = V4L2_PIX_FMT_SGBRG12,
> +        .bpp = 2,
> +    },
> +    {
> +        .code = MEDIA_BUS_FMT_SGRBG12_1X12,
> +        .pixelformat = V4L2_PIX_FMT_SGRBG12,
> +        .bpp = 2,
> +    },
> +    {
> +        .code = MEDIA_BUS_FMT_SRGGB12_1X12,
> +        .pixelformat = V4L2_PIX_FMT_SRGGB12,
> +        .bpp = 2,
> +    },
> +};
> +
> +const struct vimc_pix_map *vimc_pix_map_by_code(u32 code)
> +{
> +    unsigned int i;
> +
> +    for (i = 0; i < ARRAY_SIZE(vimc_pix_map_list); i++) {
> +        if (vimc_pix_map_list[i].code == code)
> +            return &vimc_pix_map_list[i];
> +    }
> +    return NULL;
> +}
> +
> +const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat)
> +{
> +    unsigned int i;
> +
> +    for (i = 0; i < ARRAY_SIZE(vimc_pix_map_list); i++) {
> +        if (vimc_pix_map_list[i].pixelformat == pixelformat)
> +            return &vimc_pix_map_list[i];
> +    }
> +    return NULL;
> +}
> +
> +int vimc_propagate_frame(struct device *dev,
> +             struct media_pad *src, const void *frame)
> +{
> +    struct media_link *link;
> +
> +    if (!(src->flags & MEDIA_PAD_FL_SOURCE))
> +        return -EINVAL;
> +
> +    /* Send this frame to all sink pads that are direct linked */
> +    list_for_each_entry(link, &src->entity->links, list) {
> +        if (link->source == src &&
> +            (link->flags & MEDIA_LNK_FL_ENABLED)) {
> +            struct vimc_ent_device *ved = NULL;
> +            struct media_entity *entity = link->sink->entity;
> +
> +            if (is_media_entity_v4l2_subdev(entity)) {
> +                struct v4l2_subdev *sd =
> +                    container_of(entity, struct v4l2_subdev,
> +                             entity);
> +                ved = v4l2_get_subdevdata(sd);
> +            } else if (is_media_entity_v4l2_video_device(entity)) {
> +                struct video_device *vd =
> +                    container_of(entity,
> +                             struct video_device,
> +                             entity);
> +                ved = video_get_drvdata(vd);
> +            }
> +            if (ved && ved->process_frame)
> +                ved->process_frame(ved, link->sink, frame);
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +static void vimc_device_unregister(struct vimc_device *vimc)
> +{
> +    unsigned int i;
> +
> +    media_device_unregister(&vimc->mdev);
> +    /* Cleanup (only initialized) entities */
> +    for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
> +        if (vimc->ved[i] && vimc->ved[i]->destroy)
> +            vimc->ved[i]->destroy(vimc->ved[i]);
> +
> +        vimc->ved[i] = NULL;
> +    }
> +    v4l2_device_unregister(&vimc->v4l2_dev);
> +    media_device_cleanup(&vimc->mdev);
> +}
> +
> +/* Helper function to allocate and initialize pads */
> +struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long
> *pads_flag)
> +{
> +    struct media_pad *pads;
> +    unsigned int i;
> +
> +    /* Allocate memory for the pads */
> +    pads = kcalloc(num_pads, sizeof(*pads), GFP_KERNEL);
> +    if (!pads)
> +        return ERR_PTR(-ENOMEM);
> +
> +    /* Initialize the pads */
> +    for (i = 0; i < num_pads; i++) {
> +        pads[i].index = i;
> +        pads[i].flags = pads_flag[i];
> +    }
> +
> +    return pads;
> +}
> +
> +/*
> + * TODO: remove this function when all the
> + * entities specific code are implemented
> + */
> +static void vimc_raw_destroy(struct vimc_ent_device *ved)
> +{
> +    media_device_unregister_entity(ved->ent);
> +
> +    media_entity_cleanup(ved->ent);
> +
> +    vimc_pads_cleanup(ved->pads);
> +
> +    kfree(ved->ent);
> +
> +    kfree(ved);
> +}
> +
> +/*
> + * TODO: remove this function when all the
> + * entities specific code are implemented
> + */
> +static struct vimc_ent_device *vimc_raw_create(struct v4l2_device
> *v4l2_dev,
> +                           const char *const name,
> +                           u16 num_pads,
> +                           const unsigned long *pads_flag)
> +{
> +    struct vimc_ent_device *ved;
> +    int ret;
> +
> +    /* Allocate the main ved struct */
> +    ved = kzalloc(sizeof(*ved), GFP_KERNEL);
> +    if (!ved)
> +        return ERR_PTR(-ENOMEM);
> +
> +    /* Allocate the media entity */
> +    ved->ent = kzalloc(sizeof(*ved->ent), GFP_KERNEL);
> +    if (!ved->ent) {
> +        ret = -ENOMEM;
> +        goto err_free_ved;
> +    }
> +
> +    /* Allocate the pads */
> +    ved->pads = vimc_pads_init(num_pads, pads_flag);
> +    if (IS_ERR(ved->pads)) {
> +        ret = PTR_ERR(ved->pads);
> +        goto err_free_ent;
> +    }
> +
> +    /* Initialize the media entity */
> +    ved->ent->name = name;
> +    ved->ent->function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
> +    ret = media_entity_pads_init(ved->ent, num_pads, ved->pads);
> +    if (ret)
> +        goto err_cleanup_pads;
> +
> +    /* Register the media entity */
> +    ret = media_device_register_entity(v4l2_dev->mdev, ved->ent);
> +    if (ret)
> +        goto err_cleanup_entity;
> +
> +    /* Fill out the destroy function and return */
> +    ved->destroy = vimc_raw_destroy;
> +    return ved;
> +
> +err_cleanup_entity:
> +    media_entity_cleanup(ved->ent);
> +err_cleanup_pads:
> +    vimc_pads_cleanup(ved->pads);
> +err_free_ent:
> +    kfree(ved->ent);
> +err_free_ved:
> +    kfree(ved);
> +
> +    return ERR_PTR(ret);
> +}
> +
> +static int vimc_device_register(struct vimc_device *vimc)
> +{
> +    unsigned int i;
> +    int ret;
> +
> +    /* Allocate memory for the vimc_ent_devices pointers */
> +    vimc->ved = devm_kcalloc(vimc->mdev.dev, vimc->pipe_cfg->num_ents,
> +                 sizeof(*vimc->ved), GFP_KERNEL);
> +    if (!vimc->ved)
> +        return -ENOMEM;
> +
> +    /* Link the media device within the v4l2_device */
> +    vimc->v4l2_dev.mdev = &vimc->mdev;
> +
> +    /* Register the v4l2 struct */
> +    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);
> +        return ret;
> +    }
> +
> +    /* Initialize entities */
> +    for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
> +        struct vimc_ent_device *(*create_func)(struct v4l2_device *,
> +                               const char *const,
> +                               u16,
> +                               const unsigned long *);
> +
> +        /* Register the specific node */
> +        switch (vimc->pipe_cfg->ents[i].node) {
> +        case VIMC_ENT_NODE_SENSOR:
> +            create_func = vimc_sen_create;
> +            break;
> +
> +        case VIMC_ENT_NODE_CAPTURE:
> +            create_func = vimc_cap_create;
> +            break;
> +
> +        /* TODO: Instantiate the specific topology node */
> +        case VIMC_ENT_NODE_INPUT:
> +        case VIMC_ENT_NODE_DEBAYER:
> +        case VIMC_ENT_NODE_SCALER:
> +        default:
> +            /*
> +             * TODO: remove this when all the entities specific
> +             * code are implemented
> +             */
> +            create_func = vimc_raw_create;
> +            break;
> +        }
> +
> +        vimc->ved[i] = create_func(&vimc->v4l2_dev,
> +                       vimc->pipe_cfg->ents[i].name,
> +                       vimc->pipe_cfg->ents[i].pads_qty,
> +                       vimc->pipe_cfg->ents[i].pads_flag);
> +        if (IS_ERR(vimc->ved[i])) {
> +            ret = PTR_ERR(vimc->ved[i]);
> +            vimc->ved[i] = NULL;
> +            goto err;
> +        }
> +    }
> +
> +    /* 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];
> +
> +        ret = media_create_pad_link(vimc->ved[link->src_ent]->ent,
> +                        link->src_pad,
> +                        vimc->ved[link->sink_ent]->ent,
> +                        link->sink_pad,
> +                        link->flags);
> +        if (ret)
> +            goto err;
> +    }
> +
> +    /* Register the media device */
> +    ret = media_device_register(&vimc->mdev);
> +    if (ret) {
> +        dev_err(vimc->mdev.dev,
> +            "media device register failed (err=%d)\n", ret);
> +        return ret;
> +    }
> +
> +    /* 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);
> +        goto err;
> +    }
> +
> +    return 0;
> +
> +err:
> +    /* Destroy the so far created topology */
> +    vimc_device_unregister(vimc);
> +
> +    return ret;
> +}
> +
> +static int vimc_probe(struct platform_device *pdev)
> +{
> +    struct vimc_device *vimc;
> +    int ret;
> +
> +    /* Prepare the vimc topology structure */
> +
> +    /* Allocate memory for the vimc structure */
> +    vimc = kzalloc(sizeof(*vimc), GFP_KERNEL);
> +    if (!vimc)
> +        return -ENOMEM;
> +
> +    /* Set the pipeline configuration struct */
> +    vimc->pipe_cfg = &pipe_cfg;
> +
> +    /* Initialize media device */
> +    strlcpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME,
> +        sizeof(vimc->mdev.model));
> +    vimc->mdev.dev = &pdev->dev;
> +    media_device_init(&vimc->mdev);
> +
> +    /* Create vimc topology */
> +    ret = vimc_device_register(vimc);
> +    if (ret) {
> +        dev_err(vimc->mdev.dev,
> +            "vimc device registration failed (err=%d)\n", ret);
> +        kfree(vimc);
> +        return ret;
> +    }
> +
> +    /* Link the topology object with the platform device object */
> +    platform_set_drvdata(pdev, vimc);
> +
> +    return 0;
> +}
> +
> +static int vimc_remove(struct platform_device *pdev)
> +{
> +    struct vimc_device *vimc = platform_get_drvdata(pdev);
> +
> +    /* Destroy all the topology */
> +    vimc_device_unregister(vimc);
> +    kfree(vimc);
> +
> +    return 0;
> +}
> +
> +static void vimc_dev_release(struct device *dev)
> +{
> +}
> +
> +static struct platform_device vimc_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,
> +    },
> +};
> +
> +static int __init vimc_init(void)
> +{
> +    int ret;
> +
> +    ret = platform_device_register(&vimc_pdev);
> +    if (ret) {
> +        dev_err(&vimc_pdev.dev,
> +            "platform device registration failed (err=%d)\n", ret);
> +        return ret;
> +    }
> +
> +    ret = platform_driver_register(&vimc_pdrv);
> +    if (ret) {
> +        dev_err(&vimc_pdev.dev,
> +            "platform driver registration failed (err=%d)\n", ret);
> +
> +        platform_device_unregister(&vimc_pdev);
> +    }
> +
> +    return ret;
> +}
> +
> +static void __exit vimc_exit(void)
> +{
> +    platform_driver_unregister(&vimc_pdrv);
> +
> +    platform_device_unregister(&vimc_pdev);
> +}
> +
> +module_init(vimc_init);
> +module_exit(vimc_exit);
> +
> +MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC)");
> +MODULE_AUTHOR("Helen Fornazier <helen.fornazier@gmail.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/media/platform/vimc/vimc-core.h
> b/drivers/media/platform/vimc/vimc-core.h
> new file mode 100644
> index 0000000..1d368e9
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-core.h
> @@ -0,0 +1,123 @@
> +/*
> + * vimc-core.h Virtual Media Controller Driver
> + *
> + * Copyright (C) 2017 Helen Koike F. <helen.fornazier@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef _VIMC_CORE_H_
> +#define _VIMC_CORE_H_
> +
> +#include <linux/slab.h>
> +#include <media/v4l2-device.h>
> +
> +/**
> + * struct vimc_pix_map - maps media bus code with v4l2 pixel format
> + *
> + * @code:        media bus format code defined by MEDIA_BUS_FMT_* macros
> + * @bbp:        number of bytes each pixel occupies
> + * @pixelformat:    pixel format devined by V4L2_PIX_FMT_* macros
> + *
> + * Struct which matches the MEDIA_BUS_FMT_* codes with the corresponding
> + * V4L2_PIX_FMT_* fourcc pixelformat and its bytes per pixel (bpp)
> + */
> +struct vimc_pix_map {
> +    unsigned int code;
> +    unsigned int bpp;
> +    u32 pixelformat;
> +};
> +
> +/**
> + * struct vimc_ent_device - core struct that represents a node in the
> topology
> + *
> + * @ent:        the pointer to struct media_entity for the node
> + * @pads:        the list of pads of the node
> + * @destroy:        callback to destroy the node
> + * @process_frame:    callback send a frame to that node
> + * @obj:        the object containing the struct media_entity. It can be
> + *            one of two different types: struct v4l2_subdev
> + *            struct video_device depending of the type of the node.
> + *            The struct vimc_ent_device must be embedded in the obj
> + *            by v4l2_set_subdevdata() or by video_set_drvdata()
> + *
> + * Each node of the topology must create a vimc_ent_device struct.
> Depending on
> + * the node it will be of an instance of v4l2_subdev or video_device
> struct
> + * where both contains a struct media_entity.
> + * Those structures should embedded the vimc_ent_device struct through
> + * v4l2_set_subdevdata() and video_set_drvdata() respectivaly,
> allowing the
> + * vimc_ent_device struct to be retrieved from the corresponding struct
> + * media_entity
> + */
> +struct vimc_ent_device {
> +    struct media_entity *ent;
> +    struct media_pad *pads;
> +    void (*destroy)(struct vimc_ent_device *);
> +    void (*process_frame)(struct vimc_ent_device *ved,
> +                  struct media_pad *sink, const void *frame);
> +    union {
> +        struct v4l2_subdev sd;
> +        struct video_device vd;
> +    } obj;
> +};


This union is a left over from a previous implementation test, please 
disconsider it, I'll
update this in the next version after your comments

diff --git a/drivers/media/platform/vimc/vimc-core.h 
b/drivers/media/platform/vimc/vimc-core.h
index 1d368e9..4719021 100644
--- a/drivers/media/platform/vimc/vimc-core.h
+++ b/drivers/media/platform/vimc/vimc-core.h
@@ -44,11 +44,6 @@ struct vimc_pix_map {
   * @pads:              the list of pads of the node
   * @destroy:           callback to destroy the node
   * @process_frame:     callback send a frame to that node
- * @obj:               the object containing the struct media_entity. 
It can be
- *                     one of two different types: struct v4l2_subdev
- *                     struct video_device depending of the type of the 
node.
- *                     The struct vimc_ent_device must be embedded in 
the obj
- *                     by v4l2_set_subdevdata() or by video_set_drvdata()
   *
   * Each node of the topology must create a vimc_ent_device struct. 
Depending on
   * the node it will be of an instance of v4l2_subdev or video_device 
struct
@@ -64,10 +59,6 @@ struct vimc_ent_device {
         void (*destroy)(struct vimc_ent_device *);
         void (*process_frame)(struct vimc_ent_device *ved,
                               struct media_pad *sink, const void *frame);
-       union {
-               struct v4l2_subdev sd;
-               struct video_device vd;
-       } obj;
  };

  /**


> +
> +/**
> + * vimc_propagate_frame - propagate a frame through the topology
> + *
> + * @dev:    pointer to struct &device
> + * @src:    the source pad where the frame is being originated
> + * @frame:    the frame to be propagated
> + *
> + * This function will call the process_frame callback from the
> vimc_ent_device
> + * struct of the nodes directly connected to the @src pad
> + */
> +int vimc_propagate_frame(struct device *dev,
> +             struct media_pad *src, const void *frame);
> +
> +/**
> + * vimc_pads_init - initialize pads
> + *
> + * @num_pads:    number of pads to initialize
> + * @pads_flags:    flags to use in each pad
> + *
> + * Helper functions to allocate/initialize pads
> + */
> +struct media_pad *vimc_pads_init(u16 num_pads,
> +                 const unsigned long *pads_flag);
> +
> +/**
> + * vimc_pads_cleanup - free pads
> + *
> + * @pads: pointer to the pads
> + *
> + * Helper function to free the pads initialized with vimc_pads_init
> + */
> +static inline void vimc_pads_cleanup(struct media_pad *pads)
> +{
> +    kfree(pads);
> +}
> +
> +/**
> + * vimc_pix_map_by_code - retrieve the vimc_pix_map struct by media code
> + *
> + * @code:        media bus format code defined by MEDIA_BUS_FMT_* macros
> + */
> +const struct vimc_pix_map *vimc_pix_map_by_code(u32 code);
> +
> +/**
> + * vimc_pix_map_by_pixelformat - retrieve the vimc_pix_map struct by
> v4l2 pixel format
> + *
> + * @pixelformat:    pixel format devined by V4L2_PIX_FMT_* macros
> + */
> +const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat);
> +
> +#endif
> diff --git a/drivers/media/platform/vimc/vimc-sensor.c
> b/drivers/media/platform/vimc/vimc-sensor.c
> new file mode 100644
> index 0000000..45892d0
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-sensor.c
> @@ -0,0 +1,279 @@
> +/*
> + * vimc-sensor.c Virtual Media Controller Driver
> + *
> + * Copyright (C) 2017 Helen Koike F. <helen.fornazier@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/freezer.h>
> +#include <linux/kthread.h>
> +#include <linux/v4l2-mediabus.h>
> +#include <linux/vmalloc.h>
> +#include <media/v4l2-subdev.h>
> +
> +#include "vimc-sensor.h"
> +
> +struct vimc_sen_device {
> +    struct vimc_ent_device ved;
> +    struct v4l2_subdev sd;
> +    struct v4l2_device *v4l2_dev;
> +    struct device *dev;
> +    struct task_struct *kthread_sen;
> +    u8 *frame;
> +    /* The active format */
> +    struct v4l2_mbus_framefmt mbus_format;
> +    int frame_size;
> +};
> +
> +static int vimc_sen_enum_mbus_code(struct v4l2_subdev *sd,
> +                   struct v4l2_subdev_pad_config *cfg,
> +                   struct v4l2_subdev_mbus_code_enum *code)
> +{
> +    struct vimc_sen_device *vsen =
> +                container_of(sd, struct vimc_sen_device, sd);
> +
> +    code->code = vsen->mbus_format.code;
> +
> +    return 0;
> +}
> +
> +static int vimc_sen_enum_frame_size(struct v4l2_subdev *sd,
> +                    struct v4l2_subdev_pad_config *cfg,
> +                    struct v4l2_subdev_frame_size_enum *fse)
> +{
> +    struct vimc_sen_device *vsen =
> +                container_of(sd, struct vimc_sen_device, sd);
> +
> +    /* TODO: Add support to other formats */
> +    if (fse->index)
> +        return -EINVAL;
> +
> +    /* TODO: Add support for other codes */
> +    if (fse->code != vsen->mbus_format.code)
> +        return -EINVAL;
> +
> +    fse->min_width = vsen->mbus_format.width;
> +    fse->max_width = vsen->mbus_format.width;
> +    fse->min_height = vsen->mbus_format.height;
> +    fse->max_height = vsen->mbus_format.height;
> +
> +    return 0;
> +}
> +
> +static int vimc_sen_get_fmt(struct v4l2_subdev *sd,
> +                struct v4l2_subdev_pad_config *cfg,
> +                struct v4l2_subdev_format *format)
> +{
> +    struct vimc_sen_device *vsen =
> +                container_of(sd, struct vimc_sen_device, sd);
> +
> +    format->format = vsen->mbus_format;
> +
> +    return 0;
> +}
> +
> +static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = {
> +    .enum_mbus_code        = vimc_sen_enum_mbus_code,
> +    .enum_frame_size    = vimc_sen_enum_frame_size,
> +    .get_fmt        = vimc_sen_get_fmt,
> +    /* TODO: Add support to other formats */
> +    .set_fmt        = vimc_sen_get_fmt,
> +};
> +
> +/* media operations */
> +static const struct media_entity_operations vimc_sen_mops = {
> +    .link_validate = v4l2_subdev_link_validate,
> +};
> +
> +static int vimc_thread_sen(void *data)
> +{
> +    struct vimc_sen_device *vsen = data;
> +    unsigned int i;
> +
> +    set_freezable();
> +    set_current_state(TASK_UNINTERRUPTIBLE);
> +
> +    for (;;) {
> +        try_to_freeze();
> +        if (kthread_should_stop())
> +            break;
> +
> +        memset(vsen->frame, 100, vsen->frame_size);
> +
> +        /* Send the frame to all source pads */
> +        for (i = 0; i < vsen->sd.entity.num_pads; i++)
> +            vimc_propagate_frame(vsen->dev,
> +                         &vsen->sd.entity.pads[i],
> +                         vsen->frame);
> +
> +        /* 60 frames per second */
> +        schedule_timeout(HZ/60);
> +    }
> +
> +    return 0;
> +}
> +
> +static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> +    struct vimc_sen_device *vsen =
> +                container_of(sd, struct vimc_sen_device, sd);
> +    int ret;
> +
> +    if (enable) {
> +        const struct vimc_pix_map *vpix;
> +
> +        if (vsen->kthread_sen)
> +            return -EINVAL;
> +
> +        /* Calculate the frame size */
> +        vpix = vimc_pix_map_by_code(vsen->mbus_format.code);
> +        vsen->frame_size = vsen->mbus_format.width * vpix->bpp *
> +                   vsen->mbus_format.height;
> +
> +        /*
> +         * Allocate the frame buffer. Use vmalloc to be able to
> +         * allocate a large amount of memory
> +         */
> +        vsen->frame = vmalloc(vsen->frame_size);
> +        if (!vsen->frame)
> +            return -ENOMEM;
> +
> +        /* Initialize the image generator thread */
> +        vsen->kthread_sen = kthread_run(vimc_thread_sen, vsen,
> +                        "%s-sen", vsen->v4l2_dev->name);
> +        if (IS_ERR(vsen->kthread_sen)) {
> +            dev_err(vsen->dev, "kernel_thread() failed\n");
> +            vfree(vsen->frame);
> +            vsen->frame = NULL;
> +            return PTR_ERR(vsen->kthread_sen);
> +        }
> +    } else {
> +        if (!vsen->kthread_sen)
> +            return -EINVAL;
> +
> +        /* Stop image generator */
> +        ret = kthread_stop(vsen->kthread_sen);
> +        vsen->kthread_sen = NULL;
> +
> +        vfree(vsen->frame);
> +        vsen->frame = NULL;
> +        return ret;
> +    }
> +
> +    return 0;
> +}
> +
> +struct v4l2_subdev_video_ops vimc_sen_video_ops = {
> +    .s_stream = vimc_sen_s_stream,
> +};
> +
> +static const struct v4l2_subdev_ops vimc_sen_ops = {
> +    .pad = &vimc_sen_pad_ops,
> +    .video = &vimc_sen_video_ops,
> +};
> +
> +static void vimc_sen_destroy(struct vimc_ent_device *ved)
> +{
> +    struct vimc_sen_device *vsen =
> +                container_of(ved, struct vimc_sen_device, ved);
> +
> +    media_entity_cleanup(ved->ent);
> +    v4l2_device_unregister_subdev(&vsen->sd);
> +    kfree(vsen);
> +}
> +
> +struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
> +                    const char *const name,
> +                    u16 num_pads,
> +                    const unsigned long *pads_flag)
> +{
> +    struct vimc_sen_device *vsen;
> +    unsigned int i;
> +    int ret;
> +
> +    /* NOTE: a sensor node may be created with more then one pad */
> +    if (!name || !num_pads || !pads_flag)
> +        return ERR_PTR(-EINVAL);
> +
> +    /* check if all pads are sources */
> +    for (i = 0; i < num_pads; i++)
> +        if (!(pads_flag[i] & MEDIA_PAD_FL_SOURCE))
> +            return ERR_PTR(-EINVAL);
> +
> +    /* Allocate the vsen struct */
> +    vsen = kzalloc(sizeof(*vsen), GFP_KERNEL);
> +    if (!vsen)
> +        return ERR_PTR(-ENOMEM);
> +
> +    /* Link the vimc_sen_device struct with the v4l2 parent */
> +    vsen->v4l2_dev = v4l2_dev;
> +    /* Link the vimc_sen_device struct with the dev parent */
> +    vsen->dev = v4l2_dev->dev;
> +
> +    /* Allocate the pads */
> +    vsen->ved.pads = vimc_pads_init(num_pads, pads_flag);
> +    if (IS_ERR(vsen->ved.pads)) {
> +        ret = PTR_ERR(vsen->ved.pads);
> +        goto err_free_vsen;
> +    }
> +
> +    /* Initialize the media entity */
> +    vsen->sd.entity.name = name;
> +    ret = media_entity_pads_init(&vsen->sd.entity,
> +                     num_pads, vsen->ved.pads);
> +    if (ret)
> +        goto err_clean_pads;
> +
> +    /* Set the active frame format (this is hardcoded for now) */
> +    vsen->mbus_format.width = 640;
> +    vsen->mbus_format.height = 480;
> +    vsen->mbus_format.code = MEDIA_BUS_FMT_RGB888_1X24;
> +    vsen->mbus_format.field = V4L2_FIELD_NONE;
> +    vsen->mbus_format.colorspace = V4L2_COLORSPACE_SRGB;
> +    vsen->mbus_format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
> +    vsen->mbus_format.xfer_func = V4L2_XFER_FUNC_SRGB;
> +
> +    /* Fill the vimc_ent_device struct */
> +    vsen->ved.destroy = vimc_sen_destroy;
> +    vsen->ved.ent = &vsen->sd.entity;
> +
> +    /* Initialize the subdev */
> +    v4l2_subdev_init(&vsen->sd, &vimc_sen_ops);
> +    vsen->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
> +    vsen->sd.entity.ops = &vimc_sen_mops;
> +    vsen->sd.owner = THIS_MODULE;
> +    strlcpy(vsen->sd.name, name, sizeof(vsen->sd.name));
> +    v4l2_set_subdevdata(&vsen->sd, &vsen->ved);
> +
> +    /* Expose this subdev to user space */
> +    vsen->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
> +
> +    /* Register the subdev with the v4l2 and the media framework */
> +    ret = v4l2_device_register_subdev(vsen->v4l2_dev, &vsen->sd);
> +    if (ret) {
> +        dev_err(vsen->dev,
> +            "subdev register failed (err=%d)\n", ret);
> +        goto err_clean_m_ent;
> +    }
> +
> +    return &vsen->ved;
> +
> +err_clean_m_ent:
> +    media_entity_cleanup(&vsen->sd.entity);
> +err_clean_pads:
> +    vimc_pads_cleanup(vsen->ved.pads);
> +err_free_vsen:
> +    kfree(vsen);
> +
> +    return ERR_PTR(ret);
> +}
> diff --git a/drivers/media/platform/vimc/vimc-sensor.h
> b/drivers/media/platform/vimc/vimc-sensor.h
> new file mode 100644
> index 0000000..ef48362
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-sensor.h
> @@ -0,0 +1,28 @@
> +/*
> + * vimc-sensor.h Virtual Media Controller Driver
> + *
> + * Copyright (C) 2017 Helen Koike F. <helen.fornazier@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef _VIMC_SENSOR_H_
> +#define _VIMC_SENSOR_H_
> +
> +#include "vimc-core.h"
> +
> +struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
> +                    const char *const name,
> +                    u16 num_pads,
> +                    const unsigned long *pads_flag);
> +
> +#endif
>

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

* Re: [PATCH v6] [media] vimc: Virtual Media Controller core, capture and sensor
  2017-03-24 22:11           ` Helen Koike
@ 2017-03-26 13:25             ` Sakari Ailus
  0 siblings, 0 replies; 41+ messages in thread
From: Sakari Ailus @ 2017-03-26 13:25 UTC (permalink / raw)
  To: Helen Koike, Helen Koike
  Cc: Hans Verkuil, linux-media, laurent.pinchart, jgebben, mchehab,
	Helen Fornazier

Hi Helen,

Please see my comments below.

Helen Koike wrote:
> On 2017-01-25 11:03 AM, Sakari Ailus wrote:
...
>>> +     * the videobuf2 framework will allocate this struct based on
>>> +     * buf_struct_size and use the first sizeof(struct vb2_buffer)
>>> bytes of
>>> +     * memory as a vb2_buffer
>>> +     */
>>> +    struct vb2_v4l2_buffer vb2;
>>> +    struct list_head list;
>>> +};
>>> +
>>> +static int vimc_cap_querycap(struct file *file, void *priv,
>>> +                 struct v4l2_capability *cap)
>>> +{
>>> +    struct vimc_cap_device *vcap = video_drvdata(file);
>>> +
>>> +    strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
>>> +    strlcpy(cap->card, KBUILD_MODNAME, sizeof(cap->card));
>>> +    snprintf(cap->bus_info, sizeof(cap->bus_info),
>>> +         "platform:%s", vcap->v4l2_dev->name);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int vimc_cap_enum_input(struct file *file, void *priv,
>>> +                   struct v4l2_input *i)
>>> +{
>>> +    /* We only have one input */
>>> +    if (i->index > 0)
>>> +        return -EINVAL;
>>> +
>>> +    i->type = V4L2_INPUT_TYPE_CAMERA;
>>> +    strlcpy(i->name, "VIMC capture", sizeof(i->name));
>>
>> Isn't this (*INPUT IOCTLs) something that should be handled in a
>> sub-device
>> driver, such as a TV tuner?
> 
> 
> Can the ioctl VIDIOC_ENUMINPUT enumerate no inputs at all? Can I just
> return -EINVAL here in G_INPUT and S_INPUT as well?
> I thought I had to enumerate at least one input, and between
> V4L2_INPUT_TYPE_TUNER and V4L2_INPUT_TYPE_CAMERA, this last
> one seems more appropriated

I don't think other drivers that provide MC interface do this on video
nodes either. The VIMC driver could know what's connected to it, but
generally that's not the case.

> 
> 
>>
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int vimc_cap_g_input(struct file *file, void *priv, unsigned
>>> int *i)
>>> +{
>>> +    /* We only have one input */
>>> +    *i = 0;
>>> +    return 0;
>>> +}
>>> +
>>> +static int vimc_cap_s_input(struct file *file, void *priv, unsigned
>>> int i)
>>> +{
>>> +    /* We only have one input */
>>> +    return i ? -EINVAL : 0;
>>> +}
>>> +
>>> +static int vimc_cap_fmt_vid_cap(struct file *file, void *priv,
>>> +                  struct v4l2_format *f)
>>> +{
>>> +    struct vimc_cap_device *vcap = video_drvdata(file);
>>> +
>>> +    f->fmt.pix = vcap->format;
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int vimc_cap_enum_fmt_vid_cap(struct file *file, void *priv,
>>> +                     struct v4l2_fmtdesc *f)
>>> +{
>>> +    struct vimc_cap_device *vcap = video_drvdata(file);
>>> +
>>> +    if (f->index > 0)
>>> +        return -EINVAL;
>>> +
>>> +    /* We only support one format for now */
>>> +    f->pixelformat = vcap->format.pixelformat;
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static const struct v4l2_file_operations vimc_cap_fops = {
>>> +    .owner        = THIS_MODULE,
>>> +    .open        = v4l2_fh_open,
>>> +    .release    = vb2_fop_release,
>>> +    .read           = vb2_fop_read,
>>> +    .poll        = vb2_fop_poll,
>>> +    .unlocked_ioctl = video_ioctl2,
>>> +    .mmap           = vb2_fop_mmap,
>>> +};
>>> +
>>> +static const struct v4l2_ioctl_ops vimc_cap_ioctl_ops = {
>>> +    .vidioc_querycap = vimc_cap_querycap,
>>> +
>>> +    .vidioc_enum_input = vimc_cap_enum_input,
>>> +    .vidioc_g_input = vimc_cap_g_input,
>>> +    .vidioc_s_input = vimc_cap_s_input,
>>> +
>>> +    .vidioc_g_fmt_vid_cap = vimc_cap_fmt_vid_cap,
>>> +    .vidioc_s_fmt_vid_cap = vimc_cap_fmt_vid_cap,
>>> +    .vidioc_try_fmt_vid_cap = vimc_cap_fmt_vid_cap,
>>> +    .vidioc_enum_fmt_vid_cap = vimc_cap_enum_fmt_vid_cap,
>>> +
>>> +    .vidioc_reqbufs = vb2_ioctl_reqbufs,
>>> +    .vidioc_create_bufs = vb2_ioctl_create_bufs,
>>> +    .vidioc_querybuf = vb2_ioctl_querybuf,
>>> +    .vidioc_qbuf = vb2_ioctl_qbuf,
>>> +    .vidioc_dqbuf = vb2_ioctl_dqbuf,
>>> +    .vidioc_expbuf = vb2_ioctl_expbuf,
>>> +    .vidioc_streamon = vb2_ioctl_streamon,
>>> +    .vidioc_streamoff = vb2_ioctl_streamoff,
>>> +};
>>> +
>>> +static void vimc_cap_return_all_buffers(struct vimc_cap_device *vcap,
>>> +                    enum vb2_buffer_state state)
>>> +{
>>> +    struct vimc_cap_buffer *vbuf, *node;
>>> +
>>> +    spin_lock(&vcap->qlock);
>>> +
>>> +    list_for_each_entry_safe(vbuf, node, &vcap->buf_list, list) {
>>> +        vb2_buffer_done(&vbuf->vb2.vb2_buf, state);
>>> +        list_del(&vbuf->list);
>>> +    }
>>> +
>>> +    spin_unlock(&vcap->qlock);
>>> +}
>>> +
>>> +static int vimc_cap_pipeline_s_stream(struct vimc_cap_device *vcap,
>>> int enable)
>>> +{
>>> +    int ret;
>>> +    struct media_pad *pad;
>>> +    struct media_entity *entity;
>>> +    struct v4l2_subdev *sd;
>>> +
>>> +    /* Start the stream in the subdevice direct connected */
>>> +    entity = &vcap->vdev.entity;
>>> +    pad = media_entity_remote_pad(&entity->pads[0]);
>>
>> You could use vcap->vdev.entity.pads here, without assigning to
>> entity. Then
>> entity would only be used to refer to the remove entity at the other
>> end of
>> the link. Up to you.
>>
>>> +
>>> +    /* If we are not connected to any subdev node, it means there is
>>> nothing
>>
>> /*
>>  * Multi line
>>  * comment.
>>  */
>>
>>> +     * to activate on the pipe (e.g. we can be connected with an input
>>> +     * device or we are not connected at all)
>>> +     */
>>> +    if (pad == NULL || !is_media_entity_v4l2_subdev(pad->entity))
>>> +        return 0;
>>> +
>>> +    entity = pad->entity;
>>> +    sd = media_entity_to_v4l2_subdev(entity);
>>
>> And if you used pad->entity here, you could remove the entity variable
>> altogether.
>>
>>> +
>>> +    ret = v4l2_subdev_call(sd, video, s_stream, enable);
>>> +    if (ret && ret != -ENOIOCTLCMD)
>>> +        return ret;
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned
>>> int count)
>>> +{
>>> +    struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
>>> +    struct media_entity *entity;
>>> +    int ret;
>>> +
>>> +    vcap->sequence = 0;
>>> +
>>> +    /* Start the media pipeline */
>>> +    entity = &vcap->vdev.entity;
>>> +    ret = media_entity_pipeline_start(entity, &vcap->pipe);
>>> +    if (ret) {
>>> +        vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
>>> +        return ret;
>>> +    }
>>> +
>>> +    /* Enable streaming from the pipe */
>>> +    ret = vimc_cap_pipeline_s_stream(vcap, 1);
>>> +    if (ret) {
>>> +        vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
>>> +        return ret;
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +/*
>>> + * Stop the stream engine. Any remaining buffers in the stream queue
>>> are
>>> + * dequeued and passed on to the vb2 framework marked as STATE_ERROR.
>>> + */
>>> +static void vimc_cap_stop_streaming(struct vb2_queue *vq)
>>> +{
>>> +    struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
>>> +
>>> +    /* Disable streaming from the pipe */
>>> +    vimc_cap_pipeline_s_stream(vcap, 0);
>>> +
>>> +    /* Stop the media pipeline */
>>> +    media_entity_pipeline_stop(&vcap->vdev.entity);
>>> +
>>> +    /* Release all active buffers */
>>> +    vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_ERROR);
>>> +}
>>> +
>>> +static void vimc_cap_buf_queue(struct vb2_buffer *vb2_buf)
>>> +{
>>> +    struct vimc_cap_device *vcap =
>>> vb2_get_drv_priv(vb2_buf->vb2_queue);
>>> +    struct vimc_cap_buffer *buf = container_of(vb2_buf,
>>> +                           struct vimc_cap_buffer,
>>> +                           vb2.vb2_buf);
>>> +
>>> +    spin_lock(&vcap->qlock);
>>> +    list_add_tail(&buf->list, &vcap->buf_list);
>>> +    spin_unlock(&vcap->qlock);
>>> +}
>>> +
>>> +static int vimc_cap_queue_setup(struct vb2_queue *vq, unsigned int
>>> *nbuffers,
>>> +                unsigned int *nplanes, unsigned int sizes[],
>>> +                struct device *alloc_devs[])
>>> +{
>>> +    struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
>>> +
>>> +    if (*nplanes)
>>> +        return sizes[0] < vcap->format.sizeimage ? -EINVAL : 0;
>>
>> Why? The user could later reconfigure the device to use with a buffer of
>> this size. This might not be a concern for vimc, but the code from
>> example
>> drivers tends to get copied around.
> 
> 
> This first version only support a fixed sizeimage, this will be changed
> in the next patch series where
> I add support for multiple sizes

Sounds good to me. Thanks.

-- 
Kind regards,

Sakari Ailus
sakari.ailus@iki.fi

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

* Re: [PATCH v7] [media] vimc: Virtual Media Controller core, capture and sensor
  2017-03-25 17:11                 ` [PATCH v7] " Helen Koike
  2017-03-25 23:04                   ` Helen Koike
@ 2017-03-26 13:31                   ` Sakari Ailus
  2017-03-27 15:19                     ` Helen Koike
  2017-03-27  9:00                   ` Hans Verkuil
  2 siblings, 1 reply; 41+ messages in thread
From: Sakari Ailus @ 2017-03-26 13:31 UTC (permalink / raw)
  To: Helen Koike, Hans Verkuil
  Cc: linux-media, jgebben, mchehab, Helen Fornazier, Laurent Pinchart

Hi Helen,

...
> +static int vimc_cap_enum_input(struct file *file, void *priv,
> +			       struct v4l2_input *i)
> +{
> +	/* We only have one input */
> +	if (i->index > 0)
> +		return -EINVAL;
> +
> +	i->type = V4L2_INPUT_TYPE_CAMERA;
> +	strlcpy(i->name, "VIMC capture", sizeof(i->name));
> +
> +	return 0;
> +}
> +
> +static int vimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
> +{
> +	/* We only have one input */
> +	*i = 0;
> +	return 0;
> +}
> +
> +static int vimc_cap_s_input(struct file *file, void *priv, unsigned int i)
> +{
> +	/* We only have one input */
> +	return i ? -EINVAL : 0;
> +}

You can drop the input IOCTLs altogether here. If you had e.g. a TV
tuner, it'd be the TV tuner driver's responsibility to implement them.

-- 
Regards,

Sakari Ailus
sakari.ailus@iki.fi

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

* Re: [PATCH v7] [media] vimc: Virtual Media Controller core, capture and sensor
  2017-03-25 17:11                 ` [PATCH v7] " Helen Koike
  2017-03-25 23:04                   ` Helen Koike
  2017-03-26 13:31                   ` Sakari Ailus
@ 2017-03-27  9:00                   ` Hans Verkuil
  2017-03-27 13:33                     ` [PATCH v8] " Helen Koike
  2 siblings, 1 reply; 41+ messages in thread
From: Hans Verkuil @ 2017-03-27  9:00 UTC (permalink / raw)
  To: Helen Koike
  Cc: linux-media, jgebben, mchehab, Helen Fornazier, Sakari Ailus,
	Laurent Pinchart

Two small comments:

On 25/03/17 18:11, Helen Koike wrote:
> First version of the Virtual Media Controller.
> Add a simple version of the core of the driver, the capture and
> sensor nodes in the topology, generating a grey image in a hardcoded
> format.
> 
> Signed-off-by: Helen Koike <helen.koike@collabora.com>
> 
> ---
> 
> Patch based in media/master tree, and available here:
> https://github.com/helen-fornazier/opw-staging/tree/vimc/devel/v7
> 
> Changes since v6:
> - add kernel-docs in vimc-core.h
> - reorder list_del call in vimc_cap_return_all_buffers()
> - call media_pipeline_stop in vimc_cap_start_streaming when fail
> - remove DMA comment (left over from the sample driver)
> - remove vb2_set_plane_payload call in vimc_cap_buffer_prepare
> - remove format verification in vimc_cap_link_validate
> - set vimc_pix_map_list as static
> - use MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN in vimc_raw_create()
> - register media device after creating the topology and unregister it before destroying the topology
> - replace devm_kzalloc by kzalloc for allocating struct vimc_device
> - uset TASK_UNINTERRUPTIBLE for vimc_thread_sen
> - do not allow the creation of a sensor with no pads
> - add more verbose description in Kconfig
> - change copyright to 2017
> - arrange includes: number before letters
> - remove v4l2_dev pointer from vimc_cap_device structure
> - coding style adjustments
> - remove entity variable in vimc_cap_pipeline_s_stream
> - declare and assign variables in vimc_cap_link_validate
> - declare vimc_dev_release() and vimc_pdev closer to vimc_init()
> - remove pad check in vimc_sen_enum_{mbus_code,frame_size} that is already performed by v4l2-subdev.c
> - fix multiline comments to start with /*
> - reorder variable declaration in functions
> - remove pad and subdevice type check in vimc_cap_pipeline_s_stream
> - add a note that sensor nodes can can have more then one source pad
> - remove the use of entity->use_count in the core and attach the ved structure in sd or vd, use
>   ent->obj_type to know which structure (sd or vd) to use
> - rename vdev (video_device) to vd to be similar to sd (subdev)

Hmm, vdev is what everyone uses, so I would prefer that this change is reverted.
Did someone comment on this in a code review?

> 
> Changes since v5:
> - Fix message "Entity type for entity Sensor A was not initialized!"
>   by initializing the sensor entity.function after the calling
>   v4l2_subded_init
> - populate device_caps in vimc_cap_create instead of in
>   vimc_cap_querycap
> - Fix typo in vimc-core.c s/de/the
> 
> Changes since v4:
> - coding style fixes
> - remove BUG_ON
> - change copyright to 2016
> - depens on VIDEO_V4L2_SUBDEV_API instead of select
> - remove assignement of V4L2_CAP_DEVICE_CAPS
> - s/vimc_cap_g_fmt_vid_cap/vimc_cap_fmt_vid_cap
> - fix vimc_cap_queue_setup declaration type
> - remove wrong buffer size check and add it in vimc_cap_buffer_prepare
> - vimc_cap_create: remove unecessary check if v4l2_dev or v4l2_dev->dev is null
> - vimc_cap_create: only allow a single pad
> - vimc_sen_create: only allow source pads, remove unecessary source pads
> checks in vimc_thread_sen
> 
> Changes since v3: fix rmmod crash and built-in compile
> - Re-order unregister calls in vimc_device_unregister function (remove
> rmmod issue)
> - Call media_device_unregister_entity in vimc_raw_destroy
> - Add depends on VIDEO_DEV && VIDEO_V4L2 and select VIDEOBUF2_VMALLOC
> - Check *nplanes in queue_setup (this remove v4l2-compliance fail)
> - Include <linux/kthread.h> in vimc-sensor.c
> - Move include of <linux/slab.h> from vimc-core.c to vimc-core.h
> - Generate 60 frames per sec instead of 1 in the sensor
> 
> Changes since v2: update with current media master tree
> - Add struct media_pipeline in vimc_cap_device
> - Use vb2_v4l2_buffer instead of vb2_buffer
> - Typos
> - Remove usage of entity->type and use entity->function instead
> - Remove fmt argument from queue setup
> - Use ktime_get_ns instead of v4l2_get_timestamp
> - Iterate over link's list using list_for_each_entry
> - Use media_device_{init, cleanup}
> - Use entity->use_count to keep track of entities instead of the old
> entity->id
> - Replace media_entity_init by media_entity_pads_init
> ---
>  drivers/media/platform/Kconfig             |   2 +
>  drivers/media/platform/Makefile            |   1 +
>  drivers/media/platform/vimc/Kconfig        |  14 +
>  drivers/media/platform/vimc/Makefile       |   3 +
>  drivers/media/platform/vimc/vimc-capture.c | 536 ++++++++++++++++++++++
>  drivers/media/platform/vimc/vimc-capture.h |  28 ++
>  drivers/media/platform/vimc/vimc-core.c    | 696 +++++++++++++++++++++++++++++
>  drivers/media/platform/vimc/vimc-core.h    | 123 +++++
>  drivers/media/platform/vimc/vimc-sensor.c  | 279 ++++++++++++
>  drivers/media/platform/vimc/vimc-sensor.h  |  28 ++
>  10 files changed, 1710 insertions(+)
>  create mode 100644 drivers/media/platform/vimc/Kconfig
>  create mode 100644 drivers/media/platform/vimc/Makefile
>  create mode 100644 drivers/media/platform/vimc/vimc-capture.c
>  create mode 100644 drivers/media/platform/vimc/vimc-capture.h
>  create mode 100644 drivers/media/platform/vimc/vimc-core.c
>  create mode 100644 drivers/media/platform/vimc/vimc-core.h
>  create mode 100644 drivers/media/platform/vimc/vimc-sensor.c
>  create mode 100644 drivers/media/platform/vimc/vimc-sensor.h
> 
> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> index 53f6f12..6dab7e6 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -466,6 +466,8 @@ menuconfig V4L_TEST_DRIVERS
>  
>  if V4L_TEST_DRIVERS
>  
> +source "drivers/media/platform/vimc/Kconfig"
> +
>  source "drivers/media/platform/vivid/Kconfig"
>  
>  config VIDEO_VIM2M
> diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> index 8959f6e..4af4cce 100644
> --- a/drivers/media/platform/Makefile
> +++ b/drivers/media/platform/Makefile
> @@ -13,6 +13,7 @@ obj-$(CONFIG_VIDEO_PXA27x)	+= pxa_camera.o
>  
>  obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o
>  
> +obj-$(CONFIG_VIDEO_VIMC)		+= vimc/
>  obj-$(CONFIG_VIDEO_VIVID)		+= vivid/
>  obj-$(CONFIG_VIDEO_VIM2M)		+= vim2m.o
>  
> diff --git a/drivers/media/platform/vimc/Kconfig b/drivers/media/platform/vimc/Kconfig
> new file mode 100644
> index 0000000..dd285fa
> --- /dev/null
> +++ b/drivers/media/platform/vimc/Kconfig
> @@ -0,0 +1,14 @@
> +config VIDEO_VIMC
> +	tristate "Virtual Media Controller Driver (VIMC)"
> +	depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
> +	select VIDEOBUF2_VMALLOC
> +	default n
> +	---help---
> +	  Skeleton driver for Virtual Media Controller
> +
> +	  This drivers can be compared to the Vivid driver for emulating
> +	  a media node that exposes a complex media topology. The topology
> +	  is hard coded for now but is meant to be highly configurable in
> +	  the future.
> +
> +	  When in doubt, say N.
> diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile
> new file mode 100644
> index 0000000..c45195e
> --- /dev/null
> +++ b/drivers/media/platform/vimc/Makefile
> @@ -0,0 +1,3 @@
> +vimc-objs := vimc-core.o vimc-capture.o vimc-sensor.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
> new file mode 100644
> index 0000000..8f77fc8
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-capture.c
> @@ -0,0 +1,536 @@
> +/*
> + * vimc-capture.c Virtual Media Controller Driver
> + *
> + * Copyright (C) 2017 Helen Koike F. <helen.fornazier@gmail.com>

As Mauro mentioned: since you started development in 2016 (or even 2015?) use a range:
2016-2017.

> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */

Regards,

	Hans

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

* [PATCH v8] [media] vimc: Virtual Media Controller core, capture and sensor
  2017-03-27  9:00                   ` Hans Verkuil
@ 2017-03-27 13:33                     ` Helen Koike
  2017-04-03 22:16                       ` [PATCH v9] " Helen Koike
  0 siblings, 1 reply; 41+ messages in thread
From: Helen Koike @ 2017-03-27 13:33 UTC (permalink / raw)
  To: Hans Verkuil, Helen Koike
  Cc: linux-media, jgebben, mchehab, Helen Fornazier, Sakari Ailus,
	Laurent Pinchart

First version of the Virtual Media Controller.
Add a simple version of the core of the driver, the capture and
sensor nodes in the topology, generating a grey image in a hardcoded
format.

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

---

Patch based in media/master tree, and available here:
https://github.com/helen-fornazier/opw-staging/tree/vimc/devel/v8

Changes since v7:
- remove left over union in struct vimc_ent_device
- remove v4l2_dev pointer from vimc_sen_device structure
- remove unused dev parameter from vimc_propagate_frame()
- remove struct device *dev from struct vimc_cap_device and vimc_sen_device
- in vimc_sen_create: call media_entity_pads_init() after v4l2_subdev_init()
to avoid double initialization of vsen->sd.entity.name
- in vimc_sen_destroy: move media_entity_cleanup to be after v4l2_device_unregister_subdev
- rename video_device back to vdev instead of vd
- adjust copyright with range 2015-2017
- add node names in dev_err prints for vimc-capture.c and vimc-sensor.c
- remove prefix "cap" in dev_dbg as it already uses the name of the node

Changes since v6:
- add kernel-docs in vimc-core.h
- reorder list_del call in vimc_cap_return_all_buffers()
- call media_pipeline_stop in vimc_cap_start_streaming when fail
- remove DMA comment (left over from the sample driver)
- remove vb2_set_plane_payload call in vimc_cap_buffer_prepare
- remove format verification in vimc_cap_link_validate
- set vimc_pix_map_list as static
- use MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN in vimc_raw_create()
- register media device after creating the topology and unregister it before destroying the topology
- replace devm_kzalloc by kzalloc for allocating struct vimc_device
- uset TASK_UNINTERRUPTIBLE for vimc_thread_sen
- do not allow the creation of a sensor with no pads
- add more verbose description in Kconfig
- change copyright to 2017
- arrange includes: number before letters
- remove v4l2_dev pointer from vimc_cap_device structure
- coding style adjustments
- remove entity variable in vimc_cap_pipeline_s_stream
- declare and assign variables in vimc_cap_link_validate
- declare vimc_dev_release() and vimc_pdev closer to vimc_init()
- remove pad check in vimc_sen_enum_{mbus_code,frame_size} that is already performed by v4l2-subdev.c
- fix multiline comments to start with /*
- reorder variable declaration in functions
- remove pad and subdevice type check in vimc_cap_pipeline_s_stream
- add a note that sensor nodes can can have more then one source pad
- remove the use of entity->use_count in the core and attach the ved structure in sd or vd, use
  ent->obj_type to know which structure (sd or vd) to use
- rename vdev (video_device) to vd to be similar to sd (subdev)

Changes since v5:
- Fix message "Entity type for entity Sensor A was not initialized!"
  by initializing the sensor entity.function after the calling
  v4l2_subded_init
- populate device_caps in vimc_cap_create instead of in
  vimc_cap_querycap
- Fix typo in vimc-core.c s/de/the

Changes since v4:
- coding style fixes
- remove BUG_ON
- change copyright to 2016
- depens on VIDEO_V4L2_SUBDEV_API instead of select
- remove assignement of V4L2_CAP_DEVICE_CAPS
- s/vimc_cap_g_fmt_vid_cap/vimc_cap_fmt_vid_cap
- fix vimc_cap_queue_setup declaration type
- remove wrong buffer size check and add it in vimc_cap_buffer_prepare
- vimc_cap_create: remove unecessary check if v4l2_dev or v4l2_dev->dev is null
- vimc_cap_create: only allow a single pad
- vimc_sen_create: only allow source pads, remove unecessary source pads
checks in vimc_thread_sen

Changes since v3: fix rmmod crash and built-in compile
- Re-order unregister calls in vimc_device_unregister function (remove
rmmod issue)
- Call media_device_unregister_entity in vimc_raw_destroy
- Add depends on VIDEO_DEV && VIDEO_V4L2 and select VIDEOBUF2_VMALLOC
- Check *nplanes in queue_setup (this remove v4l2-compliance fail)
- Include <linux/kthread.h> in vimc-sensor.c
- Move include of <linux/slab.h> from vimc-core.c to vimc-core.h
- Generate 60 frames per sec instead of 1 in the sensor

Changes since v2: update with current media master tree
- Add struct media_pipeline in vimc_cap_device
- Use vb2_v4l2_buffer instead of vb2_buffer
- Typos
- Remove usage of entity->type and use entity->function instead
- Remove fmt argument from queue setup
- Use ktime_get_ns instead of v4l2_get_timestamp
- Iterate over link's list using list_for_each_entry
- Use media_device_{init, cleanup}
- Use entity->use_count to keep track of entities instead of the old
entity->id
- Replace media_entity_init by media_entity_pads_init
---
 drivers/media/platform/Kconfig             |   2 +
 drivers/media/platform/Makefile            |   1 +
 drivers/media/platform/vimc/Kconfig        |  14 +
 drivers/media/platform/vimc/Makefile       |   3 +
 drivers/media/platform/vimc/vimc-capture.c | 535 ++++++++++++++++++++++
 drivers/media/platform/vimc/vimc-capture.h |  28 ++
 drivers/media/platform/vimc/vimc-core.c    | 695 +++++++++++++++++++++++++++++
 drivers/media/platform/vimc/vimc-core.h    | 112 +++++
 drivers/media/platform/vimc/vimc-sensor.c  | 272 +++++++++++
 drivers/media/platform/vimc/vimc-sensor.h  |  28 ++
 10 files changed, 1690 insertions(+)
 create mode 100644 drivers/media/platform/vimc/Kconfig
 create mode 100644 drivers/media/platform/vimc/Makefile
 create mode 100644 drivers/media/platform/vimc/vimc-capture.c
 create mode 100644 drivers/media/platform/vimc/vimc-capture.h
 create mode 100644 drivers/media/platform/vimc/vimc-core.c
 create mode 100644 drivers/media/platform/vimc/vimc-core.h
 create mode 100644 drivers/media/platform/vimc/vimc-sensor.c
 create mode 100644 drivers/media/platform/vimc/vimc-sensor.h

diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 53f6f12..6dab7e6 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -466,6 +466,8 @@ menuconfig V4L_TEST_DRIVERS
 
 if V4L_TEST_DRIVERS
 
+source "drivers/media/platform/vimc/Kconfig"
+
 source "drivers/media/platform/vivid/Kconfig"
 
 config VIDEO_VIM2M
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 8959f6e..4af4cce 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_VIDEO_PXA27x)	+= pxa_camera.o
 
 obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o
 
+obj-$(CONFIG_VIDEO_VIMC)		+= vimc/
 obj-$(CONFIG_VIDEO_VIVID)		+= vivid/
 obj-$(CONFIG_VIDEO_VIM2M)		+= vim2m.o
 
diff --git a/drivers/media/platform/vimc/Kconfig b/drivers/media/platform/vimc/Kconfig
new file mode 100644
index 0000000..dd285fa
--- /dev/null
+++ b/drivers/media/platform/vimc/Kconfig
@@ -0,0 +1,14 @@
+config VIDEO_VIMC
+	tristate "Virtual Media Controller Driver (VIMC)"
+	depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+	select VIDEOBUF2_VMALLOC
+	default n
+	---help---
+	  Skeleton driver for Virtual Media Controller
+
+	  This drivers can be compared to the Vivid driver for emulating
+	  a media node that exposes a complex media topology. The topology
+	  is hard coded for now but is meant to be highly configurable in
+	  the future.
+
+	  When in doubt, say N.
diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile
new file mode 100644
index 0000000..c45195e
--- /dev/null
+++ b/drivers/media/platform/vimc/Makefile
@@ -0,0 +1,3 @@
+vimc-objs := vimc-core.o vimc-capture.o vimc-sensor.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
new file mode 100644
index 0000000..6a43551
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-capture.c
@@ -0,0 +1,535 @@
+/*
+ * vimc-capture.c Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-vmalloc.h>
+
+#include "vimc-capture.h"
+
+struct vimc_cap_device {
+	struct vimc_ent_device ved;
+	struct video_device vdev;
+	struct v4l2_pix_format format;
+	struct vb2_queue queue;
+	struct list_head buf_list;
+	/*
+	 * NOTE: in a real driver, a spin lock must be used to access the
+	 * queue because the frames are generated from a hardware interruption
+	 * and the isr is not allowed to sleep.
+	 * Even if it is not necessary a spinlock in the vimc driver, we
+	 * use it here as a code reference
+	 */
+	spinlock_t qlock;
+	struct mutex lock;
+	u32 sequence;
+	struct media_pipeline pipe;
+};
+
+struct vimc_cap_buffer {
+	/*
+	 * struct vb2_v4l2_buffer must be the first element
+	 * the videobuf2 framework will allocate this struct based on
+	 * buf_struct_size and use the first sizeof(struct vb2_buffer) bytes of
+	 * memory as a vb2_buffer
+	 */
+	struct vb2_v4l2_buffer vb2;
+	struct list_head list;
+};
+
+static int vimc_cap_querycap(struct file *file, void *priv,
+			     struct v4l2_capability *cap)
+{
+	struct vimc_cap_device *vcap = video_drvdata(file);
+
+	strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
+	strlcpy(cap->card, KBUILD_MODNAME, sizeof(cap->card));
+	snprintf(cap->bus_info, sizeof(cap->bus_info),
+		 "platform:%s", vcap->vdev.v4l2_dev->name);
+
+	return 0;
+}
+
+static int vimc_cap_enum_input(struct file *file, void *priv,
+			       struct v4l2_input *i)
+{
+	/* We only have one input */
+	if (i->index > 0)
+		return -EINVAL;
+
+	i->type = V4L2_INPUT_TYPE_CAMERA;
+	strlcpy(i->name, "VIMC capture", sizeof(i->name));
+
+	return 0;
+}
+
+static int vimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
+{
+	/* We only have one input */
+	*i = 0;
+	return 0;
+}
+
+static int vimc_cap_s_input(struct file *file, void *priv, unsigned int i)
+{
+	/* We only have one input */
+	return i ? -EINVAL : 0;
+}
+
+static int vimc_cap_fmt_vid_cap(struct file *file, void *priv,
+				  struct v4l2_format *f)
+{
+	struct vimc_cap_device *vcap = video_drvdata(file);
+
+	f->fmt.pix = vcap->format;
+
+	return 0;
+}
+
+static int vimc_cap_enum_fmt_vid_cap(struct file *file, void *priv,
+				     struct v4l2_fmtdesc *f)
+{
+	struct vimc_cap_device *vcap = video_drvdata(file);
+
+	if (f->index > 0)
+		return -EINVAL;
+
+	/* We only support one format for now */
+	f->pixelformat = vcap->format.pixelformat;
+
+	return 0;
+}
+
+static const struct v4l2_file_operations vimc_cap_fops = {
+	.owner		= THIS_MODULE,
+	.open		= v4l2_fh_open,
+	.release	= vb2_fop_release,
+	.read           = vb2_fop_read,
+	.poll		= vb2_fop_poll,
+	.unlocked_ioctl = video_ioctl2,
+	.mmap           = vb2_fop_mmap,
+};
+
+static const struct v4l2_ioctl_ops vimc_cap_ioctl_ops = {
+	.vidioc_querycap = vimc_cap_querycap,
+
+	.vidioc_enum_input = vimc_cap_enum_input,
+	.vidioc_g_input = vimc_cap_g_input,
+	.vidioc_s_input = vimc_cap_s_input,
+
+	.vidioc_g_fmt_vid_cap = vimc_cap_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap = vimc_cap_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap = vimc_cap_fmt_vid_cap,
+	.vidioc_enum_fmt_vid_cap = vimc_cap_enum_fmt_vid_cap,
+
+	.vidioc_reqbufs = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs = vb2_ioctl_create_bufs,
+	.vidioc_querybuf = vb2_ioctl_querybuf,
+	.vidioc_qbuf = vb2_ioctl_qbuf,
+	.vidioc_dqbuf = vb2_ioctl_dqbuf,
+	.vidioc_expbuf = vb2_ioctl_expbuf,
+	.vidioc_streamon = vb2_ioctl_streamon,
+	.vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+static void vimc_cap_return_all_buffers(struct vimc_cap_device *vcap,
+					enum vb2_buffer_state state)
+{
+	struct vimc_cap_buffer *vbuf, *node;
+
+	spin_lock(&vcap->qlock);
+
+	list_for_each_entry_safe(vbuf, node, &vcap->buf_list, list) {
+		list_del(&vbuf->list);
+		vb2_buffer_done(&vbuf->vb2.vb2_buf, state);
+	}
+
+	spin_unlock(&vcap->qlock);
+}
+
+static int vimc_cap_pipeline_s_stream(struct vimc_cap_device *vcap, int enable)
+{
+	struct v4l2_subdev *sd;
+	struct media_pad *pad;
+	int ret;
+
+	/* Start the stream in the subdevice direct connected */
+	pad = media_entity_remote_pad(&vcap->vdev.entity.pads[0]);
+
+	/*
+	 * if it is a raw node from vimc-core, there is nothing to activate
+	 * TODO: remove this when there are no more raw nodes in the
+	 * core and return error instead
+	 */
+	if (pad->entity->obj_type == MEDIA_ENTITY_TYPE_BASE)
+		return 0;
+
+	sd = media_entity_to_v4l2_subdev(pad->entity);
+	ret = v4l2_subdev_call(sd, video, s_stream, enable);
+	if (ret && ret != -ENOIOCTLCMD)
+		return ret;
+
+	return 0;
+}
+
+static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
+	struct media_entity *entity = &vcap->vdev.entity;
+	int ret;
+
+	vcap->sequence = 0;
+
+	/* Start the media pipeline */
+	ret = media_pipeline_start(entity, &vcap->pipe);
+	if (ret) {
+		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
+		return ret;
+	}
+
+	/* Enable streaming from the pipe */
+	ret = vimc_cap_pipeline_s_stream(vcap, 1);
+	if (ret) {
+		media_pipeline_stop(entity);
+		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Stop the stream engine. Any remaining buffers in the stream queue are
+ * dequeued and passed on to the vb2 framework marked as STATE_ERROR.
+ */
+static void vimc_cap_stop_streaming(struct vb2_queue *vq)
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
+
+	/* Disable streaming from the pipe */
+	vimc_cap_pipeline_s_stream(vcap, 0);
+
+	/* Stop the media pipeline */
+	media_pipeline_stop(&vcap->vdev.entity);
+
+	/* Release all active buffers */
+	vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_ERROR);
+}
+
+static void vimc_cap_buf_queue(struct vb2_buffer *vb2_buf)
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb2_buf->vb2_queue);
+	struct vimc_cap_buffer *buf = container_of(vb2_buf,
+						   struct vimc_cap_buffer,
+						   vb2.vb2_buf);
+
+	spin_lock(&vcap->qlock);
+	list_add_tail(&buf->list, &vcap->buf_list);
+	spin_unlock(&vcap->qlock);
+}
+
+static int vimc_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
+				unsigned int *nplanes, unsigned int sizes[],
+				struct device *alloc_devs[])
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
+
+	if (*nplanes)
+		return sizes[0] < vcap->format.sizeimage ? -EINVAL : 0;
+	/* We don't support multiplanes for now */
+	*nplanes = 1;
+	sizes[0] = vcap->format.sizeimage;
+
+	return 0;
+}
+
+static int vimc_cap_buffer_prepare(struct vb2_buffer *vb)
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb->vb2_queue);
+	unsigned long size = vcap->format.sizeimage;
+
+	if (vb2_plane_size(vb, 0) < size) {
+		dev_err(vcap->vdev.v4l2_dev->dev,
+			"%s: buffer too small (%lu < %lu)\n",
+			vcap->vdev.name, vb2_plane_size(vb, 0), size);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static const struct vb2_ops vimc_cap_qops = {
+	.start_streaming	= vimc_cap_start_streaming,
+	.stop_streaming		= vimc_cap_stop_streaming,
+	.buf_queue		= vimc_cap_buf_queue,
+	.queue_setup		= vimc_cap_queue_setup,
+	.buf_prepare		= vimc_cap_buffer_prepare,
+	/*
+	 * Since q->lock is set we can use the standard
+	 * vb2_ops_wait_prepare/finish helper functions.
+	 */
+	.wait_prepare		= vb2_ops_wait_prepare,
+	.wait_finish		= vb2_ops_wait_finish,
+};
+
+/*
+ * NOTE: this function is a copy of v4l2_subdev_link_validate_get_format
+ * maybe the v4l2 function should be public
+ */
+static int vimc_cap_v4l2_subdev_link_validate_get_format(struct media_pad *pad,
+						struct v4l2_subdev_format *fmt)
+{
+	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity);
+
+	fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	fmt->pad = pad->index;
+
+	return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt);
+}
+
+static int vimc_cap_link_validate(struct media_link *link)
+{
+	struct v4l2_subdev_format source_fmt;
+	const struct vimc_pix_map *vpix;
+	struct vimc_cap_device *vcap = container_of(link->sink->entity,
+						    struct vimc_cap_device,
+						    vdev.entity);
+	struct v4l2_pix_format *sink_fmt = &vcap->format;
+	int ret;
+
+	/*
+	 * if it is a raw node from vimc-core, ignore the link for now
+	 * TODO: remove this when there are no more raw nodes in the
+	 * core and return error instead
+	 */
+	if (link->source->entity->obj_type == MEDIA_ENTITY_TYPE_BASE)
+		return 0;
+
+	/* Get the the format of the subdev */
+	ret = vimc_cap_v4l2_subdev_link_validate_get_format(link->source,
+							    &source_fmt);
+	if (ret)
+		return ret;
+
+	dev_dbg(vcap->vdev.v4l2_dev->dev,
+		"%s: link validate formats src:%dx%d %d sink:%dx%d %d\n",
+		vcap->vdev.name,
+		source_fmt.format.width, source_fmt.format.height,
+		source_fmt.format.code,
+		sink_fmt->width, sink_fmt->height,
+		sink_fmt->pixelformat);
+
+	/* The width, height and code must match. */
+	vpix = vimc_pix_map_by_pixelformat(sink_fmt->pixelformat);
+	if (source_fmt.format.width != sink_fmt->width
+	    || source_fmt.format.height != sink_fmt->height
+	    || vpix->code != source_fmt.format.code)
+		return -EINVAL;
+
+	/*
+	 * The field order must match, or the sink field order must be NONE
+	 * to support interlaced hardware connected to bridges that support
+	 * progressive formats only.
+	 */
+	if (source_fmt.format.field != sink_fmt->field &&
+	    sink_fmt->field != V4L2_FIELD_NONE)
+		return -EINVAL;
+
+	return 0;
+}
+
+static const struct media_entity_operations vimc_cap_mops = {
+	.link_validate		= vimc_cap_link_validate,
+};
+
+static void vimc_cap_destroy(struct vimc_ent_device *ved)
+{
+	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
+						    ved);
+
+	vb2_queue_release(&vcap->queue);
+	media_entity_cleanup(ved->ent);
+	video_unregister_device(&vcap->vdev);
+	vimc_pads_cleanup(vcap->ved.pads);
+	kfree(vcap);
+}
+
+static void vimc_cap_process_frame(struct vimc_ent_device *ved,
+				   struct media_pad *sink, const void *frame)
+{
+	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
+						    ved);
+	struct vimc_cap_buffer *vimc_buf;
+	void *vbuf;
+
+	/* If the stream in this node is not active, just return */
+	mutex_lock(&vcap->lock);
+	if (!vb2_is_busy(&vcap->queue)) {
+		mutex_unlock(&vcap->lock);
+		return;
+	}
+	mutex_unlock(&vcap->lock);
+
+	spin_lock(&vcap->qlock);
+
+	/* Get the first entry of the list */
+	vimc_buf = list_first_entry_or_null(&vcap->buf_list,
+					    typeof(*vimc_buf), list);
+	if (!vimc_buf) {
+		spin_unlock(&vcap->qlock);
+		return;
+	}
+
+	/* Remove this entry from the list */
+	list_del(&vimc_buf->list);
+
+	spin_unlock(&vcap->qlock);
+
+	/* Fill the buffer */
+	vimc_buf->vb2.vb2_buf.timestamp = ktime_get_ns();
+	vimc_buf->vb2.sequence = vcap->sequence++;
+	vimc_buf->vb2.field = vcap->format.field;
+
+	vbuf = vb2_plane_vaddr(&vimc_buf->vb2.vb2_buf, 0);
+
+	memcpy(vbuf, frame, vcap->format.sizeimage);
+
+	/* Set it as ready */
+	vb2_set_plane_payload(&vimc_buf->vb2.vb2_buf, 0,
+			      vcap->format.sizeimage);
+	vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag)
+{
+	const struct vimc_pix_map *vpix;
+	struct vimc_cap_device *vcap;
+	struct video_device *vdev;
+	struct vb2_queue *q;
+	int ret;
+
+	/*
+	 * Check entity configuration params
+	 * NOTE: we only support a single sink pad
+	 */
+	if (!name || num_pads != 1 || !pads_flag ||
+	    !(pads_flag[0] & MEDIA_PAD_FL_SINK))
+		return ERR_PTR(-EINVAL);
+
+	/* Allocate the vimc_cap_device struct */
+	vcap = kzalloc(sizeof(*vcap), GFP_KERNEL);
+	if (!vcap)
+		return ERR_PTR(-ENOMEM);
+
+	/* Allocate the pads */
+	vcap->ved.pads = vimc_pads_init(num_pads, pads_flag);
+	if (IS_ERR(vcap->ved.pads)) {
+		ret = PTR_ERR(vcap->ved.pads);
+		goto err_free_vcap;
+	}
+
+	/* Initialize the media entity */
+	vcap->vdev.entity.name = name;
+	vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L;
+	ret = media_entity_pads_init(&vcap->vdev.entity,
+				     num_pads, vcap->ved.pads);
+	if (ret)
+		goto err_clean_pads;
+
+	/* Initialize the lock */
+	mutex_init(&vcap->lock);
+
+	/* Initialize the vb2 queue */
+	q = &vcap->queue;
+	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	q->io_modes = VB2_MMAP | VB2_DMABUF;
+	q->drv_priv = vcap;
+	q->buf_struct_size = sizeof(struct vimc_cap_buffer);
+	q->ops = &vimc_cap_qops;
+	q->mem_ops = &vb2_vmalloc_memops;
+	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	q->min_buffers_needed = 2;
+	q->lock = &vcap->lock;
+
+	ret = vb2_queue_init(q);
+	if (ret) {
+		dev_err(vcap->vdev.v4l2_dev->dev,
+			"%s: vb2 queue init failed (err=%d)\n",
+			vcap->vdev.name, ret);
+		goto err_clean_m_ent;
+	}
+
+	/* Initialize buffer list and its lock */
+	INIT_LIST_HEAD(&vcap->buf_list);
+	spin_lock_init(&vcap->qlock);
+
+	/* Set the frame format (this is hardcoded for now) */
+	vcap->format.width = 640;
+	vcap->format.height = 480;
+	vcap->format.pixelformat = V4L2_PIX_FMT_RGB24;
+	vcap->format.field = V4L2_FIELD_NONE;
+	vcap->format.colorspace = V4L2_COLORSPACE_SRGB;
+
+	vpix = vimc_pix_map_by_pixelformat(vcap->format.pixelformat);
+
+	vcap->format.bytesperline = vcap->format.width * vpix->bpp;
+	vcap->format.sizeimage = vcap->format.bytesperline *
+				 vcap->format.height;
+
+	/* Fill the vimc_ent_device struct */
+	vcap->ved.destroy = vimc_cap_destroy;
+	vcap->ved.ent = &vcap->vdev.entity;
+	vcap->ved.process_frame = vimc_cap_process_frame;
+
+	/* Initialize the video_device struct */
+	vdev = &vcap->vdev;
+	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	vdev->entity.ops = &vimc_cap_mops;
+	vdev->release = video_device_release_empty;
+	vdev->fops = &vimc_cap_fops;
+	vdev->ioctl_ops = &vimc_cap_ioctl_ops;
+	vdev->lock = &vcap->lock;
+	vdev->queue = q;
+	vdev->v4l2_dev = v4l2_dev;
+	vdev->vfl_dir = VFL_DIR_RX;
+	strlcpy(vdev->name, name, sizeof(vdev->name));
+	video_set_drvdata(vdev, &vcap->ved);
+
+	/* Register the video_device with the v4l2 and the media framework */
+	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+	if (ret) {
+		dev_err(vcap->vdev.v4l2_dev->dev,
+			"%s: video register failed (err=%d)\n",
+			vcap->vdev.name, ret);
+		goto err_release_queue;
+	}
+
+	return &vcap->ved;
+
+err_release_queue:
+	vb2_queue_release(q);
+err_clean_m_ent:
+	media_entity_cleanup(&vcap->vdev.entity);
+err_clean_pads:
+	vimc_pads_cleanup(vcap->ved.pads);
+err_free_vcap:
+	kfree(vcap);
+
+	return ERR_PTR(ret);
+}
diff --git a/drivers/media/platform/vimc/vimc-capture.h b/drivers/media/platform/vimc/vimc-capture.h
new file mode 100644
index 0000000..581a813
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-capture.h
@@ -0,0 +1,28 @@
+/*
+ * vimc-capture.h Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIMC_CAPTURE_H_
+#define _VIMC_CAPTURE_H_
+
+#include "vimc-core.h"
+
+struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag);
+
+#endif
diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c
new file mode 100644
index 0000000..bc107da
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-core.c
@@ -0,0 +1,695 @@
+/*
+ * vimc-core.c Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <media/media-device.h>
+#include <media/v4l2-device.h>
+
+#include "vimc-capture.h"
+#include "vimc-core.h"
+#include "vimc-sensor.h"
+
+#define VIMC_PDEV_NAME "vimc"
+#define VIMC_MDEV_MODEL_NAME "VIMC MDEV"
+
+#define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) {	\
+	.src_ent = src,						\
+	.src_pad = srcpad,					\
+	.sink_ent = sink,					\
+	.sink_pad = sinkpad,					\
+	.flags = link_flags,					\
+}
+
+struct vimc_device {
+	/*
+	 * The pipeline configuration
+	 * (filled before calling vimc_device_register)
+	 */
+	const struct vimc_pipeline_config *pipe_cfg;
+
+	/* The Associated media_device parent */
+	struct media_device mdev;
+
+	/* Internal v4l2 parent device*/
+	struct v4l2_device v4l2_dev;
+
+	/* Internal topology */
+	struct vimc_ent_device **ved;
+};
+
+/**
+ * enum vimc_ent_node - Select the functionality of a node in the topology
+ * @VIMC_ENT_NODE_SENSOR:	A node of type SENSOR simulates a camera sensor
+ *				generating internal images in bayer format and
+ *				propagating those images through the pipeline
+ * @VIMC_ENT_NODE_CAPTURE:	A node of type CAPTURE is a v4l2 video_device
+ *				that exposes the received image from the
+ *				pipeline to the user space
+ * @VIMC_ENT_NODE_INPUT:	A node of type INPUT is a v4l2 video_device that
+ *				receives images from the user space and
+ *				propagates them through the pipeline
+ * @VIMC_ENT_NODE_DEBAYER:	A node type DEBAYER expects to receive a frame
+ *				in bayer format converts it to RGB
+ * @VIMC_ENT_NODE_SCALER:	A node of type SCALER scales the received image
+ *				by a given multiplier
+ *
+ * This enum is used in the entity configuration struct to allow the definition
+ * of a custom topology specifying the role of each node on it.
+ */
+enum vimc_ent_node {
+	VIMC_ENT_NODE_SENSOR,
+	VIMC_ENT_NODE_CAPTURE,
+	VIMC_ENT_NODE_INPUT,
+	VIMC_ENT_NODE_DEBAYER,
+	VIMC_ENT_NODE_SCALER,
+};
+
+/* Structure which describes individual configuration for each entity */
+struct vimc_ent_config {
+	const char *name;
+	size_t pads_qty;
+	const unsigned long *pads_flag;
+	enum vimc_ent_node node;
+};
+
+/* Structure which describes links between entities */
+struct vimc_ent_link {
+	unsigned int src_ent;
+	u16 src_pad;
+	unsigned int sink_ent;
+	u16 sink_pad;
+	u32 flags;
+};
+
+/* Structure which describes the whole topology */
+struct vimc_pipeline_config {
+	const struct vimc_ent_config *ents;
+	size_t num_ents;
+	const struct vimc_ent_link *links;
+	size_t num_links;
+};
+
+/* --------------------------------------------------------------------------
+ * Topology Configuration
+ */
+
+static const struct vimc_ent_config ent_config[] = {
+	{
+		.name = "Sensor A",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_SENSOR,
+	},
+	{
+		.name = "Sensor B",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_SENSOR,
+	},
+	{
+		.name = "Debayer A",
+		.pads_qty = 2,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
+						     MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_DEBAYER,
+	},
+	{
+		.name = "Debayer B",
+		.pads_qty = 2,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
+						     MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_DEBAYER,
+	},
+	{
+		.name = "Raw Capture 0",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
+		.node = VIMC_ENT_NODE_CAPTURE,
+	},
+	{
+		.name = "Raw Capture 1",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
+		.node = VIMC_ENT_NODE_CAPTURE,
+	},
+	{
+		.name = "RGB/YUV Input",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_INPUT,
+	},
+	{
+		.name = "Scaler",
+		.pads_qty = 2,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
+						     MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_SCALER,
+	},
+	{
+		.name = "RGB/YUV Capture",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
+		.node = VIMC_ENT_NODE_CAPTURE,
+	},
+};
+
+static const struct vimc_ent_link ent_links[] = {
+	/* Link: Sensor A (Pad 0)->(Pad 0) Debayer A */
+	VIMC_ENT_LINK(0, 0, 2, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Sensor A (Pad 0)->(Pad 0) Raw Capture 0 */
+	VIMC_ENT_LINK(0, 0, 4, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Sensor B (Pad 0)->(Pad 0) Debayer B */
+	VIMC_ENT_LINK(1, 0, 3, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Sensor B (Pad 0)->(Pad 0) Raw Capture 1 */
+	VIMC_ENT_LINK(1, 0, 5, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Debayer A (Pad 1)->(Pad 0) Scaler */
+	VIMC_ENT_LINK(2, 1, 7, 0, MEDIA_LNK_FL_ENABLED),
+	/* Link: Debayer B (Pad 1)->(Pad 0) Scaler */
+	VIMC_ENT_LINK(3, 1, 7, 0, 0),
+	/* Link: RGB/YUV Input (Pad 0)->(Pad 0) Scaler */
+	VIMC_ENT_LINK(6, 0, 7, 0, 0),
+	/* Link: Scaler (Pad 1)->(Pad 0) RGB/YUV Capture */
+	VIMC_ENT_LINK(7, 1, 8, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+};
+
+static const struct vimc_pipeline_config pipe_cfg = {
+	.ents		= ent_config,
+	.num_ents	= ARRAY_SIZE(ent_config),
+	.links		= ent_links,
+	.num_links	= ARRAY_SIZE(ent_links)
+};
+
+/* -------------------------------------------------------------------------- */
+
+static const struct vimc_pix_map vimc_pix_map_list[] = {
+	/* TODO: add all missing formats */
+
+	/* RGB formats */
+	{
+		.code = MEDIA_BUS_FMT_BGR888_1X24,
+		.pixelformat = V4L2_PIX_FMT_BGR24,
+		.bpp = 3,
+	},
+	{
+		.code = MEDIA_BUS_FMT_RGB888_1X24,
+		.pixelformat = V4L2_PIX_FMT_RGB24,
+		.bpp = 3,
+	},
+	{
+		.code = MEDIA_BUS_FMT_ARGB8888_1X32,
+		.pixelformat = V4L2_PIX_FMT_ARGB32,
+		.bpp = 4,
+	},
+
+	/* Bayer formats */
+	{
+		.code = MEDIA_BUS_FMT_SBGGR8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SBGGR8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGBRG8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SGBRG8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGRBG8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SGRBG8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SRGGB8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SRGGB8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SBGGR10_1X10,
+		.pixelformat = V4L2_PIX_FMT_SBGGR10,
+		.bpp = 2,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGBRG10_1X10,
+		.pixelformat = V4L2_PIX_FMT_SGBRG10,
+		.bpp = 2,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGRBG10_1X10,
+		.pixelformat = V4L2_PIX_FMT_SGRBG10,
+		.bpp = 2,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SRGGB10_1X10,
+		.pixelformat = V4L2_PIX_FMT_SRGGB10,
+		.bpp = 2,
+	},
+
+	/* 10bit raw bayer a-law compressed to 8 bits */
+	{
+		.code = MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SBGGR10ALAW8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SGBRG10ALAW8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SGRBG10ALAW8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SRGGB10ALAW8,
+		.bpp = 1,
+	},
+
+	/* 10bit raw bayer DPCM compressed to 8 bits */
+	{
+		.code = MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SBGGR10DPCM8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SGBRG10DPCM8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SRGGB10DPCM8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SBGGR12_1X12,
+		.pixelformat = V4L2_PIX_FMT_SBGGR12,
+		.bpp = 2,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGBRG12_1X12,
+		.pixelformat = V4L2_PIX_FMT_SGBRG12,
+		.bpp = 2,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGRBG12_1X12,
+		.pixelformat = V4L2_PIX_FMT_SGRBG12,
+		.bpp = 2,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SRGGB12_1X12,
+		.pixelformat = V4L2_PIX_FMT_SRGGB12,
+		.bpp = 2,
+	},
+};
+
+const struct vimc_pix_map *vimc_pix_map_by_code(u32 code)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(vimc_pix_map_list); i++) {
+		if (vimc_pix_map_list[i].code == code)
+			return &vimc_pix_map_list[i];
+	}
+	return NULL;
+}
+
+const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(vimc_pix_map_list); i++) {
+		if (vimc_pix_map_list[i].pixelformat == pixelformat)
+			return &vimc_pix_map_list[i];
+	}
+	return NULL;
+}
+
+int vimc_propagate_frame(struct media_pad *src, const void *frame)
+{
+	struct media_link *link;
+
+	if (!(src->flags & MEDIA_PAD_FL_SOURCE))
+		return -EINVAL;
+
+	/* Send this frame to all sink pads that are direct linked */
+	list_for_each_entry(link, &src->entity->links, list) {
+		if (link->source == src &&
+		    (link->flags & MEDIA_LNK_FL_ENABLED)) {
+			struct vimc_ent_device *ved = NULL;
+			struct media_entity *entity = link->sink->entity;
+
+			if (is_media_entity_v4l2_subdev(entity)) {
+				struct v4l2_subdev *sd =
+					container_of(entity, struct v4l2_subdev,
+						     entity);
+				ved = v4l2_get_subdevdata(sd);
+			} else if (is_media_entity_v4l2_video_device(entity)) {
+				struct video_device *vdev =
+					container_of(entity,
+						     struct video_device,
+						     entity);
+				ved = video_get_drvdata(vdev);
+			}
+			if (ved && ved->process_frame)
+				ved->process_frame(ved, link->sink, frame);
+		}
+	}
+
+	return 0;
+}
+
+static void vimc_device_unregister(struct vimc_device *vimc)
+{
+	unsigned int i;
+
+	media_device_unregister(&vimc->mdev);
+	/* Cleanup (only initialized) entities */
+	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
+		if (vimc->ved[i] && vimc->ved[i]->destroy)
+			vimc->ved[i]->destroy(vimc->ved[i]);
+
+		vimc->ved[i] = NULL;
+	}
+	v4l2_device_unregister(&vimc->v4l2_dev);
+	media_device_cleanup(&vimc->mdev);
+}
+
+/* Helper function to allocate and initialize pads */
+struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag)
+{
+	struct media_pad *pads;
+	unsigned int i;
+
+	/* Allocate memory for the pads */
+	pads = kcalloc(num_pads, sizeof(*pads), GFP_KERNEL);
+	if (!pads)
+		return ERR_PTR(-ENOMEM);
+
+	/* Initialize the pads */
+	for (i = 0; i < num_pads; i++) {
+		pads[i].index = i;
+		pads[i].flags = pads_flag[i];
+	}
+
+	return pads;
+}
+
+/*
+ * TODO: remove this function when all the
+ * entities specific code are implemented
+ */
+static void vimc_raw_destroy(struct vimc_ent_device *ved)
+{
+	media_device_unregister_entity(ved->ent);
+
+	media_entity_cleanup(ved->ent);
+
+	vimc_pads_cleanup(ved->pads);
+
+	kfree(ved->ent);
+
+	kfree(ved);
+}
+
+/*
+ * TODO: remove this function when all the
+ * entities specific code are implemented
+ */
+static struct vimc_ent_device *vimc_raw_create(struct v4l2_device *v4l2_dev,
+					       const char *const name,
+					       u16 num_pads,
+					       const unsigned long *pads_flag)
+{
+	struct vimc_ent_device *ved;
+	int ret;
+
+	/* Allocate the main ved struct */
+	ved = kzalloc(sizeof(*ved), GFP_KERNEL);
+	if (!ved)
+		return ERR_PTR(-ENOMEM);
+
+	/* Allocate the media entity */
+	ved->ent = kzalloc(sizeof(*ved->ent), GFP_KERNEL);
+	if (!ved->ent) {
+		ret = -ENOMEM;
+		goto err_free_ved;
+	}
+
+	/* Allocate the pads */
+	ved->pads = vimc_pads_init(num_pads, pads_flag);
+	if (IS_ERR(ved->pads)) {
+		ret = PTR_ERR(ved->pads);
+		goto err_free_ent;
+	}
+
+	/* Initialize the media entity */
+	ved->ent->name = name;
+	ved->ent->function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
+	ret = media_entity_pads_init(ved->ent, num_pads, ved->pads);
+	if (ret)
+		goto err_cleanup_pads;
+
+	/* Register the media entity */
+	ret = media_device_register_entity(v4l2_dev->mdev, ved->ent);
+	if (ret)
+		goto err_cleanup_entity;
+
+	/* Fill out the destroy function and return */
+	ved->destroy = vimc_raw_destroy;
+	return ved;
+
+err_cleanup_entity:
+	media_entity_cleanup(ved->ent);
+err_cleanup_pads:
+	vimc_pads_cleanup(ved->pads);
+err_free_ent:
+	kfree(ved->ent);
+err_free_ved:
+	kfree(ved);
+
+	return ERR_PTR(ret);
+}
+
+static int vimc_device_register(struct vimc_device *vimc)
+{
+	unsigned int i;
+	int ret;
+
+	/* Allocate memory for the vimc_ent_devices pointers */
+	vimc->ved = devm_kcalloc(vimc->mdev.dev, vimc->pipe_cfg->num_ents,
+				 sizeof(*vimc->ved), GFP_KERNEL);
+	if (!vimc->ved)
+		return -ENOMEM;
+
+	/* Link the media device within the v4l2_device */
+	vimc->v4l2_dev.mdev = &vimc->mdev;
+
+	/* Register the v4l2 struct */
+	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);
+		return ret;
+	}
+
+	/* Initialize entities */
+	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
+		struct vimc_ent_device *(*create_func)(struct v4l2_device *,
+						       const char *const,
+						       u16,
+						       const unsigned long *);
+
+		/* Register the specific node */
+		switch (vimc->pipe_cfg->ents[i].node) {
+		case VIMC_ENT_NODE_SENSOR:
+			create_func = vimc_sen_create;
+			break;
+
+		case VIMC_ENT_NODE_CAPTURE:
+			create_func = vimc_cap_create;
+			break;
+
+		/* TODO: Instantiate the specific topology node */
+		case VIMC_ENT_NODE_INPUT:
+		case VIMC_ENT_NODE_DEBAYER:
+		case VIMC_ENT_NODE_SCALER:
+		default:
+			/*
+			 * TODO: remove this when all the entities specific
+			 * code are implemented
+			 */
+			create_func = vimc_raw_create;
+			break;
+		}
+
+		vimc->ved[i] = create_func(&vimc->v4l2_dev,
+					   vimc->pipe_cfg->ents[i].name,
+					   vimc->pipe_cfg->ents[i].pads_qty,
+					   vimc->pipe_cfg->ents[i].pads_flag);
+		if (IS_ERR(vimc->ved[i])) {
+			ret = PTR_ERR(vimc->ved[i]);
+			vimc->ved[i] = NULL;
+			goto err;
+		}
+	}
+
+	/* 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];
+
+		ret = media_create_pad_link(vimc->ved[link->src_ent]->ent,
+					    link->src_pad,
+					    vimc->ved[link->sink_ent]->ent,
+					    link->sink_pad,
+					    link->flags);
+		if (ret)
+			goto err;
+	}
+
+	/* Register the media device */
+	ret = media_device_register(&vimc->mdev);
+	if (ret) {
+		dev_err(vimc->mdev.dev,
+			"media device register failed (err=%d)\n", ret);
+		return ret;
+	}
+
+	/* 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);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	/* Destroy the so far created topology */
+	vimc_device_unregister(vimc);
+
+	return ret;
+}
+
+static int vimc_probe(struct platform_device *pdev)
+{
+	struct vimc_device *vimc;
+	int ret;
+
+	/* Prepare the vimc topology structure */
+
+	/* Allocate memory for the vimc structure */
+	vimc = kzalloc(sizeof(*vimc), GFP_KERNEL);
+	if (!vimc)
+		return -ENOMEM;
+
+	/* Set the pipeline configuration struct */
+	vimc->pipe_cfg = &pipe_cfg;
+
+	/* Initialize media device */
+	strlcpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME,
+		sizeof(vimc->mdev.model));
+	vimc->mdev.dev = &pdev->dev;
+	media_device_init(&vimc->mdev);
+
+	/* Create vimc topology */
+	ret = vimc_device_register(vimc);
+	if (ret) {
+		dev_err(vimc->mdev.dev,
+			"vimc device registration failed (err=%d)\n", ret);
+		kfree(vimc);
+		return ret;
+	}
+
+	/* Link the topology object with the platform device object */
+	platform_set_drvdata(pdev, vimc);
+
+	return 0;
+}
+
+static int vimc_remove(struct platform_device *pdev)
+{
+	struct vimc_device *vimc = platform_get_drvdata(pdev);
+
+	/* Destroy all the topology */
+	vimc_device_unregister(vimc);
+	kfree(vimc);
+
+	return 0;
+}
+
+static void vimc_dev_release(struct device *dev)
+{
+}
+
+static struct platform_device vimc_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,
+	},
+};
+
+static int __init vimc_init(void)
+{
+	int ret;
+
+	ret = platform_device_register(&vimc_pdev);
+	if (ret) {
+		dev_err(&vimc_pdev.dev,
+			"platform device registration failed (err=%d)\n", ret);
+		return ret;
+	}
+
+	ret = platform_driver_register(&vimc_pdrv);
+	if (ret) {
+		dev_err(&vimc_pdev.dev,
+			"platform driver registration failed (err=%d)\n", ret);
+
+		platform_device_unregister(&vimc_pdev);
+	}
+
+	return ret;
+}
+
+static void __exit vimc_exit(void)
+{
+	platform_driver_unregister(&vimc_pdrv);
+
+	platform_device_unregister(&vimc_pdev);
+}
+
+module_init(vimc_init);
+module_exit(vimc_exit);
+
+MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC)");
+MODULE_AUTHOR("Helen Fornazier <helen.fornazier@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/vimc/vimc-core.h b/drivers/media/platform/vimc/vimc-core.h
new file mode 100644
index 0000000..2beb7f0
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-core.h
@@ -0,0 +1,112 @@
+/*
+ * vimc-core.h Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIMC_CORE_H_
+#define _VIMC_CORE_H_
+
+#include <linux/slab.h>
+#include <media/v4l2-device.h>
+
+/**
+ * struct vimc_pix_map - maps media bus code with v4l2 pixel format
+ *
+ * @code:		media bus format code defined by MEDIA_BUS_FMT_* macros
+ * @bbp:		number of bytes each pixel occupies
+ * @pixelformat:	pixel format devined by V4L2_PIX_FMT_* macros
+ *
+ * Struct which matches the MEDIA_BUS_FMT_* codes with the corresponding
+ * V4L2_PIX_FMT_* fourcc pixelformat and its bytes per pixel (bpp)
+ */
+struct vimc_pix_map {
+	unsigned int code;
+	unsigned int bpp;
+	u32 pixelformat;
+};
+
+/**
+ * struct vimc_ent_device - core struct that represents a node in the topology
+ *
+ * @ent:		the pointer to struct media_entity for the node
+ * @pads:		the list of pads of the node
+ * @destroy:		callback to destroy the node
+ * @process_frame:	callback send a frame to that node
+ *
+ * Each node of the topology must create a vimc_ent_device struct. Depending on
+ * the node it will be of an instance of v4l2_subdev or video_device struct
+ * where both contains a struct media_entity.
+ * Those structures should embedded the vimc_ent_device struct through
+ * v4l2_set_subdevdata() and video_set_drvdata() respectivaly, allowing the
+ * vimc_ent_device struct to be retrieved from the corresponding struct
+ * media_entity
+ */
+struct vimc_ent_device {
+	struct media_entity *ent;
+	struct media_pad *pads;
+	void (*destroy)(struct vimc_ent_device *);
+	void (*process_frame)(struct vimc_ent_device *ved,
+			      struct media_pad *sink, const void *frame);
+};
+
+/**
+ * vimc_propagate_frame - propagate a frame through the topology
+ *
+ * @src:	the source pad where the frame is being originated
+ * @frame:	the frame to be propagated
+ *
+ * This function will call the process_frame callback from the vimc_ent_device
+ * struct of the nodes directly connected to the @src pad
+ */
+int vimc_propagate_frame(struct media_pad *src, const void *frame);
+
+/**
+ * vimc_pads_init - initialize pads
+ *
+ * @num_pads:	number of pads to initialize
+ * @pads_flags:	flags to use in each pad
+ *
+ * Helper functions to allocate/initialize pads
+ */
+struct media_pad *vimc_pads_init(u16 num_pads,
+				 const unsigned long *pads_flag);
+
+/**
+ * vimc_pads_cleanup - free pads
+ *
+ * @pads: pointer to the pads
+ *
+ * Helper function to free the pads initialized with vimc_pads_init
+ */
+static inline void vimc_pads_cleanup(struct media_pad *pads)
+{
+	kfree(pads);
+}
+
+/**
+ * vimc_pix_map_by_code - retrieve the vimc_pix_map struct by media code
+ *
+ * @code:		media bus format code defined by MEDIA_BUS_FMT_* macros
+ */
+const struct vimc_pix_map *vimc_pix_map_by_code(u32 code);
+
+/**
+ * vimc_pix_map_by_pixelformat - retrieve the vimc_pix_map struct by v4l2 pixel format
+ *
+ * @pixelformat:	pixel format devined by V4L2_PIX_FMT_* macros
+ */
+const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat);
+
+#endif
diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c
new file mode 100644
index 0000000..4cd4ee6
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-sensor.c
@@ -0,0 +1,272 @@
+/*
+ * vimc-sensor.c Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/freezer.h>
+#include <linux/kthread.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/vmalloc.h>
+#include <media/v4l2-subdev.h>
+
+#include "vimc-sensor.h"
+
+struct vimc_sen_device {
+	struct vimc_ent_device ved;
+	struct v4l2_subdev sd;
+	struct task_struct *kthread_sen;
+	u8 *frame;
+	/* The active format */
+	struct v4l2_mbus_framefmt mbus_format;
+	int frame_size;
+};
+
+static int vimc_sen_enum_mbus_code(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_pad_config *cfg,
+				   struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct vimc_sen_device *vsen =
+				container_of(sd, struct vimc_sen_device, sd);
+
+	code->code = vsen->mbus_format.code;
+
+	return 0;
+}
+
+static int vimc_sen_enum_frame_size(struct v4l2_subdev *sd,
+				    struct v4l2_subdev_pad_config *cfg,
+				    struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct vimc_sen_device *vsen =
+				container_of(sd, struct vimc_sen_device, sd);
+
+	/* TODO: Add support to other formats */
+	if (fse->index)
+		return -EINVAL;
+
+	/* TODO: Add support for other codes */
+	if (fse->code != vsen->mbus_format.code)
+		return -EINVAL;
+
+	fse->min_width = vsen->mbus_format.width;
+	fse->max_width = vsen->mbus_format.width;
+	fse->min_height = vsen->mbus_format.height;
+	fse->max_height = vsen->mbus_format.height;
+
+	return 0;
+}
+
+static int vimc_sen_get_fmt(struct v4l2_subdev *sd,
+			    struct v4l2_subdev_pad_config *cfg,
+			    struct v4l2_subdev_format *format)
+{
+	struct vimc_sen_device *vsen =
+				container_of(sd, struct vimc_sen_device, sd);
+
+	format->format = vsen->mbus_format;
+
+	return 0;
+}
+
+static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = {
+	.enum_mbus_code		= vimc_sen_enum_mbus_code,
+	.enum_frame_size	= vimc_sen_enum_frame_size,
+	.get_fmt		= vimc_sen_get_fmt,
+	/* TODO: Add support to other formats */
+	.set_fmt		= vimc_sen_get_fmt,
+};
+
+/* media operations */
+static const struct media_entity_operations vimc_sen_mops = {
+	.link_validate = v4l2_subdev_link_validate,
+};
+
+static int vimc_thread_sen(void *data)
+{
+	struct vimc_sen_device *vsen = data;
+	unsigned int i;
+
+	set_freezable();
+	set_current_state(TASK_UNINTERRUPTIBLE);
+
+	for (;;) {
+		try_to_freeze();
+		if (kthread_should_stop())
+			break;
+
+		memset(vsen->frame, 100, vsen->frame_size);
+
+		/* Send the frame to all source pads */
+		for (i = 0; i < vsen->sd.entity.num_pads; i++)
+			vimc_propagate_frame(&vsen->sd.entity.pads[i],
+					     vsen->frame);
+
+		/* 60 frames per second */
+		schedule_timeout(HZ/60);
+	}
+
+	return 0;
+}
+
+static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct vimc_sen_device *vsen =
+				container_of(sd, struct vimc_sen_device, sd);
+	int ret;
+
+	if (enable) {
+		const struct vimc_pix_map *vpix;
+
+		if (vsen->kthread_sen)
+			return -EINVAL;
+
+		/* Calculate the frame size */
+		vpix = vimc_pix_map_by_code(vsen->mbus_format.code);
+		vsen->frame_size = vsen->mbus_format.width * vpix->bpp *
+				   vsen->mbus_format.height;
+
+		/*
+		 * Allocate the frame buffer. Use vmalloc to be able to
+		 * allocate a large amount of memory
+		 */
+		vsen->frame = vmalloc(vsen->frame_size);
+		if (!vsen->frame)
+			return -ENOMEM;
+
+		/* Initialize the image generator thread */
+		vsen->kthread_sen = kthread_run(vimc_thread_sen, vsen, "%s-sen",
+						vsen->sd.v4l2_dev->name);
+		if (IS_ERR(vsen->kthread_sen)) {
+			dev_err(vsen->sd.v4l2_dev->dev,
+				"%s: kernel_thread() failed\n",	vsen->sd.name);
+			vfree(vsen->frame);
+			vsen->frame = NULL;
+			return PTR_ERR(vsen->kthread_sen);
+		}
+	} else {
+		if (!vsen->kthread_sen)
+			return -EINVAL;
+
+		/* Stop image generator */
+		ret = kthread_stop(vsen->kthread_sen);
+		vsen->kthread_sen = NULL;
+
+		vfree(vsen->frame);
+		vsen->frame = NULL;
+		return ret;
+	}
+
+	return 0;
+}
+
+struct v4l2_subdev_video_ops vimc_sen_video_ops = {
+	.s_stream = vimc_sen_s_stream,
+};
+
+static const struct v4l2_subdev_ops vimc_sen_ops = {
+	.pad = &vimc_sen_pad_ops,
+	.video = &vimc_sen_video_ops,
+};
+
+static void vimc_sen_destroy(struct vimc_ent_device *ved)
+{
+	struct vimc_sen_device *vsen =
+				container_of(ved, struct vimc_sen_device, ved);
+
+	v4l2_device_unregister_subdev(&vsen->sd);
+	media_entity_cleanup(ved->ent);
+	kfree(vsen);
+}
+
+struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag)
+{
+	struct vimc_sen_device *vsen;
+	unsigned int i;
+	int ret;
+
+	/* NOTE: a sensor node may be created with more then one pad */
+	if (!name || !num_pads || !pads_flag)
+		return ERR_PTR(-EINVAL);
+
+	/* check if all pads are sources */
+	for (i = 0; i < num_pads; i++)
+		if (!(pads_flag[i] & MEDIA_PAD_FL_SOURCE))
+			return ERR_PTR(-EINVAL);
+
+	/* Allocate the vsen struct */
+	vsen = kzalloc(sizeof(*vsen), GFP_KERNEL);
+	if (!vsen)
+		return ERR_PTR(-ENOMEM);
+
+	/* Allocate the pads */
+	vsen->ved.pads = vimc_pads_init(num_pads, pads_flag);
+	if (IS_ERR(vsen->ved.pads)) {
+		ret = PTR_ERR(vsen->ved.pads);
+		goto err_free_vsen;
+	}
+
+	/* Fill the vimc_ent_device struct */
+	vsen->ved.destroy = vimc_sen_destroy;
+	vsen->ved.ent = &vsen->sd.entity;
+
+	/* Initialize the subdev */
+	v4l2_subdev_init(&vsen->sd, &vimc_sen_ops);
+	vsen->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+	vsen->sd.entity.ops = &vimc_sen_mops;
+	vsen->sd.owner = THIS_MODULE;
+	strlcpy(vsen->sd.name, name, sizeof(vsen->sd.name));
+	v4l2_set_subdevdata(&vsen->sd, &vsen->ved);
+
+	/* Expose this subdev to user space */
+	vsen->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	/* Initialize the media entity */
+	ret = media_entity_pads_init(&vsen->sd.entity,
+				     num_pads, vsen->ved.pads);
+	if (ret)
+		goto err_clean_pads;
+
+	/* Set the active frame format (this is hardcoded for now) */
+	vsen->mbus_format.width = 640;
+	vsen->mbus_format.height = 480;
+	vsen->mbus_format.code = MEDIA_BUS_FMT_RGB888_1X24;
+	vsen->mbus_format.field = V4L2_FIELD_NONE;
+	vsen->mbus_format.colorspace = V4L2_COLORSPACE_SRGB;
+	vsen->mbus_format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
+	vsen->mbus_format.xfer_func = V4L2_XFER_FUNC_SRGB;
+
+	/* Register the subdev with the v4l2 and the media framework */
+	ret = v4l2_device_register_subdev(v4l2_dev, &vsen->sd);
+	if (ret) {
+		dev_err(vsen->sd.v4l2_dev->dev,
+			"%s: subdev register failed (err=%d)\n",
+			vsen->sd.name, ret);
+		goto err_clean_m_ent;
+	}
+
+	return &vsen->ved;
+
+err_clean_m_ent:
+	media_entity_cleanup(&vsen->sd.entity);
+err_clean_pads:
+	vimc_pads_cleanup(vsen->ved.pads);
+err_free_vsen:
+	kfree(vsen);
+
+	return ERR_PTR(ret);
+}
diff --git a/drivers/media/platform/vimc/vimc-sensor.h b/drivers/media/platform/vimc/vimc-sensor.h
new file mode 100644
index 0000000..505310e
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-sensor.h
@@ -0,0 +1,28 @@
+/*
+ * vimc-sensor.h Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIMC_SENSOR_H_
+#define _VIMC_SENSOR_H_
+
+#include "vimc-core.h"
+
+struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag);
+
+#endif
-- 
2.7.4

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

* Re: [PATCH v7] [media] vimc: Virtual Media Controller core, capture and sensor
  2017-03-26 13:31                   ` Sakari Ailus
@ 2017-03-27 15:19                     ` Helen Koike
  2017-03-27 18:09                       ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 41+ messages in thread
From: Helen Koike @ 2017-03-27 15:19 UTC (permalink / raw)
  To: Sakari Ailus, Helen Koike, Hans Verkuil
  Cc: linux-media, jgebben, mchehab, Helen Fornazier, Laurent Pinchart

Hi Sakari,

On 2017-03-26 10:31 AM, Sakari Ailus wrote:
> Hi Helen,
>
> ...
>> +static int vimc_cap_enum_input(struct file *file, void *priv,
>> +			       struct v4l2_input *i)
>> +{
>> +	/* We only have one input */
>> +	if (i->index > 0)
>> +		return -EINVAL;
>> +
>> +	i->type = V4L2_INPUT_TYPE_CAMERA;
>> +	strlcpy(i->name, "VIMC capture", sizeof(i->name));
>> +
>> +	return 0;
>> +}
>> +
>> +static int vimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
>> +{
>> +	/* We only have one input */
>> +	*i = 0;
>> +	return 0;
>> +}
>> +
>> +static int vimc_cap_s_input(struct file *file, void *priv, unsigned int i)
>> +{
>> +	/* We only have one input */
>> +	return i ? -EINVAL : 0;
>> +}
>
> You can drop the input IOCTLs altogether here. If you had e.g. a TV
> tuner, it'd be the TV tuner driver's responsibility to implement them.
>

input IOCTLs seems to be mandatory from v4l2-compliance when capability 
V4L2_CAP_VIDEO_CAPTURE is set (which is the case):

https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-test-input-output.cpp#n418

https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-compliance.cpp#n989

Helen

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

* Re: [PATCH v7] [media] vimc: Virtual Media Controller core, capture and sensor
  2017-03-27 15:19                     ` Helen Koike
@ 2017-03-27 18:09                       ` Mauro Carvalho Chehab
  2017-03-28 10:00                         ` Hans Verkuil
  0 siblings, 1 reply; 41+ messages in thread
From: Mauro Carvalho Chehab @ 2017-03-27 18:09 UTC (permalink / raw)
  To: Helen Koike
  Cc: Sakari Ailus, Helen Koike, Hans Verkuil, linux-media, jgebben,
	Helen Fornazier, Laurent Pinchart

Em Mon, 27 Mar 2017 12:19:51 -0300
Helen Koike <helen.koike@collabora.co.uk> escreveu:

> Hi Sakari,
> 
> On 2017-03-26 10:31 AM, Sakari Ailus wrote:
> > Hi Helen,
> >
> > ...  
> >> +static int vimc_cap_enum_input(struct file *file, void *priv,
> >> +			       struct v4l2_input *i)
> >> +{
> >> +	/* We only have one input */
> >> +	if (i->index > 0)
> >> +		return -EINVAL;
> >> +
> >> +	i->type = V4L2_INPUT_TYPE_CAMERA;
> >> +	strlcpy(i->name, "VIMC capture", sizeof(i->name));
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int vimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
> >> +{
> >> +	/* We only have one input */
> >> +	*i = 0;
> >> +	return 0;
> >> +}
> >> +
> >> +static int vimc_cap_s_input(struct file *file, void *priv, unsigned int i)
> >> +{
> >> +	/* We only have one input */
> >> +	return i ? -EINVAL : 0;
> >> +}  
> >
> > You can drop the input IOCTLs altogether here. If you had e.g. a TV
> > tuner, it'd be the TV tuner driver's responsibility to implement them.
> >  
> 
> input IOCTLs seems to be mandatory from v4l2-compliance when capability 
> V4L2_CAP_VIDEO_CAPTURE is set (which is the case):
> 
> https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-test-input-output.cpp#n418
> 
> https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-compliance.cpp#n989

The V4L2 spec doesn't actually define what's mandatory and what's
optional. The idea that was agreed on one of the media summits
were to define a set of profiles for different device types,
matching the features required by existing applications to work,
but this was never materialized.

So, my understanding is that any driver can implement
any V4L2 ioctl.

Yet, some applications require enum/get/set inputs, or otherwise
they wouldn't work. It is too late to change this behavior. 
So, either the driver or the core should implement those
ioctls, in order to avoid breaking backward-compatibility.

Regards,

Thanks,
Mauro

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

* Re: [PATCH v7] [media] vimc: Virtual Media Controller core, capture and sensor
  2017-03-27 18:09                       ` Mauro Carvalho Chehab
@ 2017-03-28 10:00                         ` Hans Verkuil
  2017-03-28 11:38                           ` Mauro Carvalho Chehab
  2017-03-28 14:23                           ` Sakari Ailus
  0 siblings, 2 replies; 41+ messages in thread
From: Hans Verkuil @ 2017-03-28 10:00 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Helen Koike
  Cc: Sakari Ailus, Helen Koike, linux-media, jgebben, Helen Fornazier,
	Laurent Pinchart

On 27/03/17 20:09, Mauro Carvalho Chehab wrote:
> Em Mon, 27 Mar 2017 12:19:51 -0300
> Helen Koike <helen.koike@collabora.co.uk> escreveu:
> 
>> Hi Sakari,
>>
>> On 2017-03-26 10:31 AM, Sakari Ailus wrote:
>>> Hi Helen,
>>>
>>> ...  
>>>> +static int vimc_cap_enum_input(struct file *file, void *priv,
>>>> +			       struct v4l2_input *i)
>>>> +{
>>>> +	/* We only have one input */
>>>> +	if (i->index > 0)
>>>> +		return -EINVAL;
>>>> +
>>>> +	i->type = V4L2_INPUT_TYPE_CAMERA;
>>>> +	strlcpy(i->name, "VIMC capture", sizeof(i->name));
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static int vimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
>>>> +{
>>>> +	/* We only have one input */
>>>> +	*i = 0;
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static int vimc_cap_s_input(struct file *file, void *priv, unsigned int i)
>>>> +{
>>>> +	/* We only have one input */
>>>> +	return i ? -EINVAL : 0;
>>>> +}  
>>>
>>> You can drop the input IOCTLs altogether here. If you had e.g. a TV
>>> tuner, it'd be the TV tuner driver's responsibility to implement them.
>>>  
>>
>> input IOCTLs seems to be mandatory from v4l2-compliance when capability 
>> V4L2_CAP_VIDEO_CAPTURE is set (which is the case):
>>
>> https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-test-input-output.cpp#n418
>>
>> https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-compliance.cpp#n989
> 
> The V4L2 spec doesn't actually define what's mandatory and what's
> optional. The idea that was agreed on one of the media summits
> were to define a set of profiles for different device types,
> matching the features required by existing applications to work,
> but this was never materialized.
> 
> So, my understanding is that any driver can implement
> any V4L2 ioctl.
> 
> Yet, some applications require enum/get/set inputs, or otherwise
> they wouldn't work. It is too late to change this behavior. 
> So, either the driver or the core should implement those
> ioctls, in order to avoid breaking backward-compatibility.

The closest we have to determining which ioctls are mandatory or not is
v4l2-compliance. That said, v4l2-compliance is actually a bit more strict
in some cases than the spec since some ioctls are optional in the spec, but
required in v4l2-compliance for the simple reason that there is no reason
for drivers NOT to implement those ioctls.

However, the v4l2-compliance test was never written for MC devices. It turns
out that it works reasonably well as long as a working pipeline is configured
first, but these input ioctls are a bit iffy.

There are really two options: don't implement them, or implement it as a single
input. Multiple inputs make no sense for MC devices: the video node is the
endpoint of a video pipeline, you never switch 'inputs' there.

The way the input ioctls are implemented here would fit nicely for an MC
device IMHO.

So should we define these ioctls or not?

I am inclined to define them for the following reasons:

- Some applications expect them, so adding them to the driver costs little but
  allows these applications to work, provided the correct pipeline is configured
  first.

- If a plugin is needed, then that plugin can always override these ioctls and
  for different 'inputs' reconfigure the pipeline.

I really don't see implementing this as a problem. Reporting that an MC video node
has a "VIMC capture" input seems perfectly reasonable to me.

Regards,

	Hans

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

* Re: [PATCH v7] [media] vimc: Virtual Media Controller core, capture and sensor
  2017-03-28 10:00                         ` Hans Verkuil
@ 2017-03-28 11:38                           ` Mauro Carvalho Chehab
  2017-03-28 20:37                             ` Sakari Ailus
  2017-03-28 14:23                           ` Sakari Ailus
  1 sibling, 1 reply; 41+ messages in thread
From: Mauro Carvalho Chehab @ 2017-03-28 11:38 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Helen Koike, Sakari Ailus, Helen Koike, linux-media, jgebben,
	Helen Fornazier, Laurent Pinchart

Em Tue, 28 Mar 2017 12:00:36 +0200
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> On 27/03/17 20:09, Mauro Carvalho Chehab wrote:
> > Em Mon, 27 Mar 2017 12:19:51 -0300
> > Helen Koike <helen.koike@collabora.co.uk> escreveu:
> >   
> >> Hi Sakari,
> >>
> >> On 2017-03-26 10:31 AM, Sakari Ailus wrote:  
> >>> Hi Helen,
> >>>
> >>> ...    
> >>>> +static int vimc_cap_enum_input(struct file *file, void *priv,
> >>>> +			       struct v4l2_input *i)
> >>>> +{
> >>>> +	/* We only have one input */
> >>>> +	if (i->index > 0)
> >>>> +		return -EINVAL;
> >>>> +
> >>>> +	i->type = V4L2_INPUT_TYPE_CAMERA;
> >>>> +	strlcpy(i->name, "VIMC capture", sizeof(i->name));
> >>>> +
> >>>> +	return 0;
> >>>> +}
> >>>> +
> >>>> +static int vimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
> >>>> +{
> >>>> +	/* We only have one input */
> >>>> +	*i = 0;
> >>>> +	return 0;
> >>>> +}
> >>>> +
> >>>> +static int vimc_cap_s_input(struct file *file, void *priv, unsigned int i)
> >>>> +{
> >>>> +	/* We only have one input */
> >>>> +	return i ? -EINVAL : 0;
> >>>> +}    
> >>>
> >>> You can drop the input IOCTLs altogether here. If you had e.g. a TV
> >>> tuner, it'd be the TV tuner driver's responsibility to implement them.
> >>>    
> >>
> >> input IOCTLs seems to be mandatory from v4l2-compliance when capability 
> >> V4L2_CAP_VIDEO_CAPTURE is set (which is the case):
> >>
> >> https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-test-input-output.cpp#n418
> >>
> >> https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-compliance.cpp#n989  
> > 
> > The V4L2 spec doesn't actually define what's mandatory and what's
> > optional. The idea that was agreed on one of the media summits
> > were to define a set of profiles for different device types,
> > matching the features required by existing applications to work,
> > but this was never materialized.
> > 
> > So, my understanding is that any driver can implement
> > any V4L2 ioctl.
> > 
> > Yet, some applications require enum/get/set inputs, or otherwise
> > they wouldn't work. It is too late to change this behavior. 
> > So, either the driver or the core should implement those
> > ioctls, in order to avoid breaking backward-compatibility.  
> 
> The closest we have to determining which ioctls are mandatory or not is
> v4l2-compliance.

Yes, but we should explicitly document what's mandatory at the V4L2
API spec and mention the v4l2-compliance tool there.

> That said, v4l2-compliance is actually a bit more strict
> in some cases than the spec since some ioctls are optional in the spec, but
> required in v4l2-compliance for the simple reason that there is no reason
> for drivers NOT to implement those ioctls.
> 
> However, the v4l2-compliance test was never written for MC devices. It turns
> out that it works reasonably well as long as a working pipeline is configured
> first, but these input ioctls are a bit iffy.

The way I see, v4l2-compliance V4L2 API check[1] should not be modified to
explicitly support devices with MC and/or subdev API.

Provided that a valid pipeline is set (either via MC or via some pipeline
loaded by DT, as Russell proposed), v4l2-compliance should be checking
if what the device driver provides is enough for a generic V4L2 application
to work with such pipeline.

As v4l2-compliance also supports libv4l, if are there any plugin
for that device, it should use such plugin automatically.

So, from my side, if the driver doesn't pass at v4l2-compliance, it is
not ready for upstream.

[1] Still, it would make sense to add support at v4l2-compliance
(or to have some other tool) that would check if the MC and subdev 
APIs are properly implemented - but this is another matter.

> There are really two options: don't implement them, or implement it as a single
> input. Multiple inputs make no sense for MC devices: the video node is the
> endpoint of a video pipeline, you never switch 'inputs' there.
> 
> The way the input ioctls are implemented here would fit nicely for an MC
> device IMHO.
> 
> So should we define these ioctls or not?

We should. All ioctls that generic application require should be there
on all drivers, as nobody will modify those applications to work with
"capped" drivers. Even if someone would be willing to do that, it would
take years for those apps to be reflected at the distros.

What we could do is to provide a default handler for "trivial" handling
of ioctls like G_INPUT/S_INPUT at the V4L2 core. This way, drivers that
don't have multiple inputs (like most webcam drivers) won't need to
explicitly implement it.

> I am inclined to define them for the following reasons:
> 
> - Some applications expect them, so adding them to the driver costs little but
>   allows these applications to work, provided the correct pipeline is configured
>   first.
> 
> - If a plugin is needed, then that plugin can always override these ioctls and
>   for different 'inputs' reconfigure the pipeline.
> 
> I really don't see implementing this as a problem. Reporting that an MC video node
> has a "VIMC capture" input seems perfectly reasonable to me.

Agreed.

Thanks,
Mauro

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

* Re: [PATCH v7] [media] vimc: Virtual Media Controller core, capture and sensor
  2017-03-28 10:00                         ` Hans Verkuil
  2017-03-28 11:38                           ` Mauro Carvalho Chehab
@ 2017-03-28 14:23                           ` Sakari Ailus
  2017-03-28 15:25                             ` Mauro Carvalho Chehab
  2017-03-29  7:39                             ` Hans Verkuil
  1 sibling, 2 replies; 41+ messages in thread
From: Sakari Ailus @ 2017-03-28 14:23 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Mauro Carvalho Chehab, Helen Koike, Helen Koike, linux-media,
	jgebben, Helen Fornazier, Laurent Pinchart

Hi Hans,

On Tue, Mar 28, 2017 at 12:00:36PM +0200, Hans Verkuil wrote:
> On 27/03/17 20:09, Mauro Carvalho Chehab wrote:
> > Em Mon, 27 Mar 2017 12:19:51 -0300
> > Helen Koike <helen.koike@collabora.co.uk> escreveu:
> > 
> >> Hi Sakari,
> >>
> >> On 2017-03-26 10:31 AM, Sakari Ailus wrote:
> >>> Hi Helen,
> >>>
> >>> ...  
> >>>> +static int vimc_cap_enum_input(struct file *file, void *priv,
> >>>> +			       struct v4l2_input *i)
> >>>> +{
> >>>> +	/* We only have one input */
> >>>> +	if (i->index > 0)
> >>>> +		return -EINVAL;
> >>>> +
> >>>> +	i->type = V4L2_INPUT_TYPE_CAMERA;
> >>>> +	strlcpy(i->name, "VIMC capture", sizeof(i->name));
> >>>> +
> >>>> +	return 0;
> >>>> +}
> >>>> +
> >>>> +static int vimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
> >>>> +{
> >>>> +	/* We only have one input */
> >>>> +	*i = 0;
> >>>> +	return 0;
> >>>> +}
> >>>> +
> >>>> +static int vimc_cap_s_input(struct file *file, void *priv, unsigned int i)
> >>>> +{
> >>>> +	/* We only have one input */
> >>>> +	return i ? -EINVAL : 0;
> >>>> +}  
> >>>
> >>> You can drop the input IOCTLs altogether here. If you had e.g. a TV
> >>> tuner, it'd be the TV tuner driver's responsibility to implement them.
> >>>  
> >>
> >> input IOCTLs seems to be mandatory from v4l2-compliance when capability 
> >> V4L2_CAP_VIDEO_CAPTURE is set (which is the case):
> >>
> >> https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-test-input-output.cpp#n418
> >>
> >> https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-compliance.cpp#n989
> > 
> > The V4L2 spec doesn't actually define what's mandatory and what's
> > optional. The idea that was agreed on one of the media summits
> > were to define a set of profiles for different device types,
> > matching the features required by existing applications to work,
> > but this was never materialized.
> > 
> > So, my understanding is that any driver can implement
> > any V4L2 ioctl.
> > 
> > Yet, some applications require enum/get/set inputs, or otherwise
> > they wouldn't work. It is too late to change this behavior. 
> > So, either the driver or the core should implement those
> > ioctls, in order to avoid breaking backward-compatibility.
> 
> The closest we have to determining which ioctls are mandatory or not is
> v4l2-compliance. That said, v4l2-compliance is actually a bit more strict
> in some cases than the spec since some ioctls are optional in the spec, but
> required in v4l2-compliance for the simple reason that there is no reason
> for drivers NOT to implement those ioctls.
> 
> However, the v4l2-compliance test was never written for MC devices. It turns
> out that it works reasonably well as long as a working pipeline is configured
> first, but these input ioctls are a bit iffy.
> 
> There are really two options: don't implement them, or implement it as a single
> input. Multiple inputs make no sense for MC devices: the video node is the
> endpoint of a video pipeline, you never switch 'inputs' there.
> 
> The way the input ioctls are implemented here would fit nicely for an MC
> device IMHO.
> 
> So should we define these ioctls or not?
> 
> I am inclined to define them for the following reasons:
> 
> - Some applications expect them, so adding them to the driver costs little but
>   allows these applications to work, provided the correct pipeline is configured
>   first.
> 
> - If a plugin is needed, then that plugin can always override these ioctls and
>   for different 'inputs' reconfigure the pipeline.
> 
> I really don't see implementing this as a problem. Reporting that an MC video node
> has a "VIMC capture" input seems perfectly reasonable to me.

If we implement it in order to be make an application happy, I would have
expected to hear complaints from someone using existing MC based drivers
that do not implement the input IOCTLs.

It is also confusing from application point of view since this interface
would not be the interface to configure the input of the pipeline as it
might look like.

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Re: [PATCH v7] [media] vimc: Virtual Media Controller core, capture and sensor
  2017-03-28 14:23                           ` Sakari Ailus
@ 2017-03-28 15:25                             ` Mauro Carvalho Chehab
  2017-03-28 20:39                               ` Sakari Ailus
  2017-03-29  7:39                             ` Hans Verkuil
  1 sibling, 1 reply; 41+ messages in thread
From: Mauro Carvalho Chehab @ 2017-03-28 15:25 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: Hans Verkuil, Helen Koike, Helen Koike, linux-media, jgebben,
	Helen Fornazier, Laurent Pinchart

Em Tue, 28 Mar 2017 17:23:40 +0300
Sakari Ailus <sakari.ailus@iki.fi> escreveu:

> Hi Hans,
> 
> On Tue, Mar 28, 2017 at 12:00:36PM +0200, Hans Verkuil wrote:
> > On 27/03/17 20:09, Mauro Carvalho Chehab wrote:  
> > > Em Mon, 27 Mar 2017 12:19:51 -0300
> > > Helen Koike <helen.koike@collabora.co.uk> escreveu:
> > >   
> > >> Hi Sakari,
> > >>
> > >> On 2017-03-26 10:31 AM, Sakari Ailus wrote:  
> > >>> Hi Helen,
> > >>>
> > >>> ...    
> > >>>> +static int vimc_cap_enum_input(struct file *file, void *priv,
> > >>>> +			       struct v4l2_input *i)
> > >>>> +{
> > >>>> +	/* We only have one input */
> > >>>> +	if (i->index > 0)
> > >>>> +		return -EINVAL;
> > >>>> +
> > >>>> +	i->type = V4L2_INPUT_TYPE_CAMERA;
> > >>>> +	strlcpy(i->name, "VIMC capture", sizeof(i->name));
> > >>>> +
> > >>>> +	return 0;
> > >>>> +}
> > >>>> +
> > >>>> +static int vimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
> > >>>> +{
> > >>>> +	/* We only have one input */
> > >>>> +	*i = 0;
> > >>>> +	return 0;
> > >>>> +}
> > >>>> +
> > >>>> +static int vimc_cap_s_input(struct file *file, void *priv, unsigned int i)
> > >>>> +{
> > >>>> +	/* We only have one input */
> > >>>> +	return i ? -EINVAL : 0;
> > >>>> +}    
> > >>>
> > >>> You can drop the input IOCTLs altogether here. If you had e.g. a TV
> > >>> tuner, it'd be the TV tuner driver's responsibility to implement them.
> > >>>    
> > >>
> > >> input IOCTLs seems to be mandatory from v4l2-compliance when capability 
> > >> V4L2_CAP_VIDEO_CAPTURE is set (which is the case):
> > >>
> > >> https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-test-input-output.cpp#n418
> > >>
> > >> https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-compliance.cpp#n989  
> > > 
> > > The V4L2 spec doesn't actually define what's mandatory and what's
> > > optional. The idea that was agreed on one of the media summits
> > > were to define a set of profiles for different device types,
> > > matching the features required by existing applications to work,
> > > but this was never materialized.
> > > 
> > > So, my understanding is that any driver can implement
> > > any V4L2 ioctl.
> > > 
> > > Yet, some applications require enum/get/set inputs, or otherwise
> > > they wouldn't work. It is too late to change this behavior. 
> > > So, either the driver or the core should implement those
> > > ioctls, in order to avoid breaking backward-compatibility.  
> > 
> > The closest we have to determining which ioctls are mandatory or not is
> > v4l2-compliance. That said, v4l2-compliance is actually a bit more strict
> > in some cases than the spec since some ioctls are optional in the spec, but
> > required in v4l2-compliance for the simple reason that there is no reason
> > for drivers NOT to implement those ioctls.
> > 
> > However, the v4l2-compliance test was never written for MC devices. It turns
> > out that it works reasonably well as long as a working pipeline is configured
> > first, but these input ioctls are a bit iffy.
> > 
> > There are really two options: don't implement them, or implement it as a single
> > input. Multiple inputs make no sense for MC devices: the video node is the
> > endpoint of a video pipeline, you never switch 'inputs' there.
> > 
> > The way the input ioctls are implemented here would fit nicely for an MC
> > device IMHO.
> > 
> > So should we define these ioctls or not?
> > 
> > I am inclined to define them for the following reasons:
> > 
> > - Some applications expect them, so adding them to the driver costs little but
> >   allows these applications to work, provided the correct pipeline is configured
> >   first.
> > 
> > - If a plugin is needed, then that plugin can always override these ioctls and
> >   for different 'inputs' reconfigure the pipeline.
> > 
> > I really don't see implementing this as a problem. Reporting that an MC video node
> > has a "VIMC capture" input seems perfectly reasonable to me.  
> 
> If we implement it in order to be make an application happy, I would have
> expected to hear complaints from someone using existing MC based drivers
> that do not implement the input IOCTLs.

If we implement a default method in the core, all MC-based drivers
will have it, without requiring driver changes.

> It is also confusing from application point of view since this interface
> would not be the interface to configure the input of the pipeline as it
> might look like.

It is not confusing. A MC-aware sub-dev oriented application will do
the right thing, as such apps are designed to work with some special
hardware.

The (very few) of non-MC app that doesn't fail if without those ioctl
will keep running.

The only difference on implementing it is that other non-MC
applications will start to run if the driver passes on v4l2-compliance
tests.

So, I don't see any troubles on doing that. Just benefits. 



Thanks,
Mauro

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

* Re: [PATCH v7] [media] vimc: Virtual Media Controller core, capture and sensor
  2017-03-28 11:38                           ` Mauro Carvalho Chehab
@ 2017-03-28 20:37                             ` Sakari Ailus
  2017-03-29  7:49                               ` Hans Verkuil
       [not found]                               ` <CAKQmDh9QoW7qnai=i68HBBbkLBa+Ni5K7WKeYDLONjYeyhHH0A@mail.gmail.com>
  0 siblings, 2 replies; 41+ messages in thread
From: Sakari Ailus @ 2017-03-28 20:37 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Hans Verkuil, Helen Koike, Helen Koike, linux-media, jgebben,
	Helen Fornazier, Laurent Pinchart

Hi Mauro,

On Tue, Mar 28, 2017 at 08:38:26AM -0300, Mauro Carvalho Chehab wrote:
> Em Tue, 28 Mar 2017 12:00:36 +0200
> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> 
> > On 27/03/17 20:09, Mauro Carvalho Chehab wrote:
> > > Em Mon, 27 Mar 2017 12:19:51 -0300
> > > Helen Koike <helen.koike@collabora.co.uk> escreveu:
> > >   
> > >> Hi Sakari,
> > >>
> > >> On 2017-03-26 10:31 AM, Sakari Ailus wrote:  
> > >>> Hi Helen,
> > >>>
> > >>> ...    
> > >>>> +static int vimc_cap_enum_input(struct file *file, void *priv,
> > >>>> +			       struct v4l2_input *i)
> > >>>> +{
> > >>>> +	/* We only have one input */
> > >>>> +	if (i->index > 0)
> > >>>> +		return -EINVAL;
> > >>>> +
> > >>>> +	i->type = V4L2_INPUT_TYPE_CAMERA;
> > >>>> +	strlcpy(i->name, "VIMC capture", sizeof(i->name));
> > >>>> +
> > >>>> +	return 0;
> > >>>> +}
> > >>>> +
> > >>>> +static int vimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
> > >>>> +{
> > >>>> +	/* We only have one input */
> > >>>> +	*i = 0;
> > >>>> +	return 0;
> > >>>> +}
> > >>>> +
> > >>>> +static int vimc_cap_s_input(struct file *file, void *priv, unsigned int i)
> > >>>> +{
> > >>>> +	/* We only have one input */
> > >>>> +	return i ? -EINVAL : 0;
> > >>>> +}    
> > >>>
> > >>> You can drop the input IOCTLs altogether here. If you had e.g. a TV
> > >>> tuner, it'd be the TV tuner driver's responsibility to implement them.
> > >>>    
> > >>
> > >> input IOCTLs seems to be mandatory from v4l2-compliance when capability 
> > >> V4L2_CAP_VIDEO_CAPTURE is set (which is the case):
> > >>
> > >> https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-test-input-output.cpp#n418
> > >>
> > >> https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-compliance.cpp#n989  
> > > 
> > > The V4L2 spec doesn't actually define what's mandatory and what's
> > > optional. The idea that was agreed on one of the media summits
> > > were to define a set of profiles for different device types,
> > > matching the features required by existing applications to work,
> > > but this was never materialized.
> > > 
> > > So, my understanding is that any driver can implement
> > > any V4L2 ioctl.
> > > 
> > > Yet, some applications require enum/get/set inputs, or otherwise
> > > they wouldn't work. It is too late to change this behavior. 
> > > So, either the driver or the core should implement those
> > > ioctls, in order to avoid breaking backward-compatibility.  
> > 
> > The closest we have to determining which ioctls are mandatory or not is
> > v4l2-compliance.
> 
> Yes, but we should explicitly document what's mandatory at the V4L2
> API spec and mention the v4l2-compliance tool there.
> 
> > That said, v4l2-compliance is actually a bit more strict
> > in some cases than the spec since some ioctls are optional in the spec, but
> > required in v4l2-compliance for the simple reason that there is no reason
> > for drivers NOT to implement those ioctls.
> > 
> > However, the v4l2-compliance test was never written for MC devices. It turns
> > out that it works reasonably well as long as a working pipeline is configured
> > first, but these input ioctls are a bit iffy.
> 
> The way I see, v4l2-compliance V4L2 API check[1] should not be modified to
> explicitly support devices with MC and/or subdev API.

The V4L2 API documentation states that

	Video inputs and outputs are physical connectors of a device. ...
	Drivers must implement all the input ioctls when the device has one
	or more inputs, all the output ioctls when the device has one or
	more outputs.

"Inputs" and "outputs", as the spec defines them, mean physical connectors
to the device.

Does e.g. a camera have a physical connector? I don't think one could
imagine it does, meaning also there is no need to implement these IOCTLs.

That said, I looked at a few drivers and even the omap3isp driver implements
the input IOCTLs. It provides no useful information whatsoever through them,
just like most drivers whose hardware has no physical connectors.

Still the bottom line is that the spec does not require them.

-- 
Regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Re: [PATCH v7] [media] vimc: Virtual Media Controller core, capture and sensor
  2017-03-28 15:25                             ` Mauro Carvalho Chehab
@ 2017-03-28 20:39                               ` Sakari Ailus
  0 siblings, 0 replies; 41+ messages in thread
From: Sakari Ailus @ 2017-03-28 20:39 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Hans Verkuil, Helen Koike, Helen Koike, linux-media, jgebben,
	Helen Fornazier, Laurent Pinchart

Hi Mauro,

On Tue, Mar 28, 2017 at 12:25:50PM -0300, Mauro Carvalho Chehab wrote:
...
> The (very few) of non-MC app that doesn't fail if without those ioctl
> will keep running.
> 
> The only difference on implementing it is that other non-MC
> applications will start to run if the driver passes on v4l2-compliance
> tests.
> 
> So, I don't see any troubles on doing that. Just benefits. 

Are there such applications? (Please see my other e-mail.)

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Re: [PATCH v7] [media] vimc: Virtual Media Controller core, capture and sensor
  2017-03-28 14:23                           ` Sakari Ailus
  2017-03-28 15:25                             ` Mauro Carvalho Chehab
@ 2017-03-29  7:39                             ` Hans Verkuil
  1 sibling, 0 replies; 41+ messages in thread
From: Hans Verkuil @ 2017-03-29  7:39 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: Mauro Carvalho Chehab, Helen Koike, Helen Koike, linux-media,
	jgebben, Helen Fornazier, Laurent Pinchart

On 28/03/17 16:23, Sakari Ailus wrote:
> Hi Hans,
> 
> On Tue, Mar 28, 2017 at 12:00:36PM +0200, Hans Verkuil wrote:
>> On 27/03/17 20:09, Mauro Carvalho Chehab wrote:
>>> Em Mon, 27 Mar 2017 12:19:51 -0300
>>> Helen Koike <helen.koike@collabora.co.uk> escreveu:
>>>
>>>> Hi Sakari,
>>>>
>>>> On 2017-03-26 10:31 AM, Sakari Ailus wrote:
>>>>> Hi Helen,
>>>>>
>>>>> ...  
>>>>>> +static int vimc_cap_enum_input(struct file *file, void *priv,
>>>>>> +			       struct v4l2_input *i)
>>>>>> +{
>>>>>> +	/* We only have one input */
>>>>>> +	if (i->index > 0)
>>>>>> +		return -EINVAL;
>>>>>> +
>>>>>> +	i->type = V4L2_INPUT_TYPE_CAMERA;
>>>>>> +	strlcpy(i->name, "VIMC capture", sizeof(i->name));
>>>>>> +
>>>>>> +	return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static int vimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
>>>>>> +{
>>>>>> +	/* We only have one input */
>>>>>> +	*i = 0;
>>>>>> +	return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static int vimc_cap_s_input(struct file *file, void *priv, unsigned int i)
>>>>>> +{
>>>>>> +	/* We only have one input */
>>>>>> +	return i ? -EINVAL : 0;
>>>>>> +}  
>>>>>
>>>>> You can drop the input IOCTLs altogether here. If you had e.g. a TV
>>>>> tuner, it'd be the TV tuner driver's responsibility to implement them.
>>>>>  
>>>>
>>>> input IOCTLs seems to be mandatory from v4l2-compliance when capability 
>>>> V4L2_CAP_VIDEO_CAPTURE is set (which is the case):
>>>>
>>>> https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-test-input-output.cpp#n418
>>>>
>>>> https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-compliance.cpp#n989
>>>
>>> The V4L2 spec doesn't actually define what's mandatory and what's
>>> optional. The idea that was agreed on one of the media summits
>>> were to define a set of profiles for different device types,
>>> matching the features required by existing applications to work,
>>> but this was never materialized.
>>>
>>> So, my understanding is that any driver can implement
>>> any V4L2 ioctl.
>>>
>>> Yet, some applications require enum/get/set inputs, or otherwise
>>> they wouldn't work. It is too late to change this behavior. 
>>> So, either the driver or the core should implement those
>>> ioctls, in order to avoid breaking backward-compatibility.
>>
>> The closest we have to determining which ioctls are mandatory or not is
>> v4l2-compliance. That said, v4l2-compliance is actually a bit more strict
>> in some cases than the spec since some ioctls are optional in the spec, but
>> required in v4l2-compliance for the simple reason that there is no reason
>> for drivers NOT to implement those ioctls.
>>
>> However, the v4l2-compliance test was never written for MC devices. It turns
>> out that it works reasonably well as long as a working pipeline is configured
>> first, but these input ioctls are a bit iffy.
>>
>> There are really two options: don't implement them, or implement it as a single
>> input. Multiple inputs make no sense for MC devices: the video node is the
>> endpoint of a video pipeline, you never switch 'inputs' there.
>>
>> The way the input ioctls are implemented here would fit nicely for an MC
>> device IMHO.
>>
>> So should we define these ioctls or not?
>>
>> I am inclined to define them for the following reasons:
>>
>> - Some applications expect them, so adding them to the driver costs little but
>>   allows these applications to work, provided the correct pipeline is configured
>>   first.
>>
>> - If a plugin is needed, then that plugin can always override these ioctls and
>>   for different 'inputs' reconfigure the pipeline.
>>
>> I really don't see implementing this as a problem. Reporting that an MC video node
>> has a "VIMC capture" input seems perfectly reasonable to me.
> 
> If we implement it in order to be make an application happy, I would have
> expected to hear complaints from someone using existing MC based drivers
> that do not implement the input IOCTLs.

It's for the same reason no one complained about the missing plugin: 'regular'
applications aren't used for these MC devices since they tend to be used on
embedded systems with custom software.

> It is also confusing from application point of view since this interface
> would not be the interface to configure the input of the pipeline as it
> might look like.
> 

The whole point is that, once a video pipeline is set up with media-ctl, the
driver will act just like a non-MC driver with a single input. Which is the
whole point. Applications that are MC aware won't use this, they would program
the media controller/subdevs directly.

It costs us next to nothing and it makes life easier for all. I really don't
see a downside to this.

Regards,

	Hans

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

* Re: [PATCH v7] [media] vimc: Virtual Media Controller core, capture and sensor
  2017-03-28 20:37                             ` Sakari Ailus
@ 2017-03-29  7:49                               ` Hans Verkuil
  2017-03-29  8:46                                 ` Sakari Ailus
       [not found]                               ` <CAKQmDh9QoW7qnai=i68HBBbkLBa+Ni5K7WKeYDLONjYeyhHH0A@mail.gmail.com>
  1 sibling, 1 reply; 41+ messages in thread
From: Hans Verkuil @ 2017-03-29  7:49 UTC (permalink / raw)
  To: Sakari Ailus, Mauro Carvalho Chehab
  Cc: Helen Koike, Helen Koike, linux-media, jgebben, Helen Fornazier,
	Laurent Pinchart

On 28/03/17 22:37, Sakari Ailus wrote:
> Hi Mauro,
> 
> On Tue, Mar 28, 2017 at 08:38:26AM -0300, Mauro Carvalho Chehab wrote:
>> Em Tue, 28 Mar 2017 12:00:36 +0200
>> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
>>
>>> On 27/03/17 20:09, Mauro Carvalho Chehab wrote:
>>>> Em Mon, 27 Mar 2017 12:19:51 -0300
>>>> Helen Koike <helen.koike@collabora.co.uk> escreveu:
>>>>   
>>>>> Hi Sakari,
>>>>>
>>>>> On 2017-03-26 10:31 AM, Sakari Ailus wrote:  
>>>>>> Hi Helen,
>>>>>>
>>>>>> ...    
>>>>>>> +static int vimc_cap_enum_input(struct file *file, void *priv,
>>>>>>> +			       struct v4l2_input *i)
>>>>>>> +{
>>>>>>> +	/* We only have one input */
>>>>>>> +	if (i->index > 0)
>>>>>>> +		return -EINVAL;
>>>>>>> +
>>>>>>> +	i->type = V4L2_INPUT_TYPE_CAMERA;
>>>>>>> +	strlcpy(i->name, "VIMC capture", sizeof(i->name));
>>>>>>> +
>>>>>>> +	return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int vimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
>>>>>>> +{
>>>>>>> +	/* We only have one input */
>>>>>>> +	*i = 0;
>>>>>>> +	return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int vimc_cap_s_input(struct file *file, void *priv, unsigned int i)
>>>>>>> +{
>>>>>>> +	/* We only have one input */
>>>>>>> +	return i ? -EINVAL : 0;
>>>>>>> +}    
>>>>>>
>>>>>> You can drop the input IOCTLs altogether here. If you had e.g. a TV
>>>>>> tuner, it'd be the TV tuner driver's responsibility to implement them.
>>>>>>    
>>>>>
>>>>> input IOCTLs seems to be mandatory from v4l2-compliance when capability 
>>>>> V4L2_CAP_VIDEO_CAPTURE is set (which is the case):
>>>>>
>>>>> https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-test-input-output.cpp#n418
>>>>>
>>>>> https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-compliance.cpp#n989  
>>>>
>>>> The V4L2 spec doesn't actually define what's mandatory and what's
>>>> optional. The idea that was agreed on one of the media summits
>>>> were to define a set of profiles for different device types,
>>>> matching the features required by existing applications to work,
>>>> but this was never materialized.
>>>>
>>>> So, my understanding is that any driver can implement
>>>> any V4L2 ioctl.
>>>>
>>>> Yet, some applications require enum/get/set inputs, or otherwise
>>>> they wouldn't work. It is too late to change this behavior. 
>>>> So, either the driver or the core should implement those
>>>> ioctls, in order to avoid breaking backward-compatibility.  
>>>
>>> The closest we have to determining which ioctls are mandatory or not is
>>> v4l2-compliance.
>>
>> Yes, but we should explicitly document what's mandatory at the V4L2
>> API spec and mention the v4l2-compliance tool there.
>>
>>> That said, v4l2-compliance is actually a bit more strict
>>> in some cases than the spec since some ioctls are optional in the spec, but
>>> required in v4l2-compliance for the simple reason that there is no reason
>>> for drivers NOT to implement those ioctls.
>>>
>>> However, the v4l2-compliance test was never written for MC devices. It turns
>>> out that it works reasonably well as long as a working pipeline is configured
>>> first, but these input ioctls are a bit iffy.
>>
>> The way I see, v4l2-compliance V4L2 API check[1] should not be modified to
>> explicitly support devices with MC and/or subdev API.
> 
> The V4L2 API documentation states that
> 
> 	Video inputs and outputs are physical connectors of a device. ...
> 	Drivers must implement all the input ioctls when the device has one
> 	or more inputs, all the output ioctls when the device has one or
> 	more outputs.
> 
> "Inputs" and "outputs", as the spec defines them, mean physical connectors
> to the device.
> 
> Does e.g. a camera have a physical connector? I don't think one could
> imagine it does, meaning also there is no need to implement these IOCTLs.
> 
> That said, I looked at a few drivers and even the omap3isp driver implements
> the input IOCTLs. It provides no useful information whatsoever through them,
> just like most drivers whose hardware has no physical connectors.
> 
> Still the bottom line is that the spec does not require them.

The spec isn't gospel. The reality is that all non-MC drivers have these ioctls
and a sensor is considered to be an input.

Section 4.1.2 says: "The video input and video standard ioctls must be supported
by all video capture devices.". Which actually is also wrong since the video
standard ioctls do not make sense for DV inputs or sensors.

I'll make a patch correcting these issues.

Regards,

	Hans

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

* Re: [PATCH v7] [media] vimc: Virtual Media Controller core, capture and sensor
       [not found]                               ` <CAKQmDh9QoW7qnai=i68HBBbkLBa+Ni5K7WKeYDLONjYeyhHH0A@mail.gmail.com>
@ 2017-03-29  8:02                                 ` Hans Verkuil
  0 siblings, 0 replies; 41+ messages in thread
From: Hans Verkuil @ 2017-03-29  8:02 UTC (permalink / raw)
  To: Nicolas Dufresne, Sakari Ailus
  Cc: DVB_Linux_Media, jgebben, Helen Koike, Helen Koike,
	Mauro Carvalho Chehab, Helen Fornazier, Laurent Pinchart

On 29/03/17 00:35, Nicolas Dufresne wrote:
> 
> 
> Le 28 mars 2017 4:38 PM, "Sakari Ailus" <sakari.ailus@iki.fi <mailto:sakari.ailus@iki.fi>> a écrit :
> 
>     Hi Mauro,
> 
>     On Tue, Mar 28, 2017 at 08:38:26AM -0300, Mauro Carvalho Chehab wrote:
>     > Em Tue, 28 Mar 2017 12:00:36 +0200
>     > Hans Verkuil <hverkuil@xs4all.nl <mailto:hverkuil@xs4all.nl>> escreveu:
>     >
>     > > On 27/03/17 20:09, Mauro Carvalho Chehab wrote:
>     > > > Em Mon, 27 Mar 2017 12:19:51 -0300
>     > > > Helen Koike <helen.koike@collabora.co.uk <mailto:helen.koike@collabora.co.uk>> escreveu:
>     > > >
>     > > >> Hi Sakari,
>     > > >>
>     > > >> On 2017-03-26 10:31 AM, Sakari Ailus wrote:
>     > > >>> Hi Helen,
>     > > >>>
>     > > >>> ...
>     > > >>>> +static int vimc_cap_enum_input(struct file *file, void *priv,
>     > > >>>> +                             struct v4l2_input *i)
>     > > >>>> +{
>     > > >>>> +      /* We only have one input */
>     > > >>>> +      if (i->index > 0)
>     > > >>>> +              return -EINVAL;
>     > > >>>> +
>     > > >>>> +      i->type = V4L2_INPUT_TYPE_CAMERA;
>     > > >>>> +      strlcpy(i->name, "VIMC capture", sizeof(i->name));
>     > > >>>> +
>     > > >>>> +      return 0;
>     > > >>>> +}
>     > > >>>> +
>     > > >>>> +static int vimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
>     > > >>>> +{
>     > > >>>> +      /* We only have one input */
>     > > >>>> +      *i = 0;
>     > > >>>> +      return 0;
>     > > >>>> +}
>     > > >>>> +
>     > > >>>> +static int vimc_cap_s_input(struct file *file, void *priv, unsigned int i)
>     > > >>>> +{
>     > > >>>> +      /* We only have one input */
>     > > >>>> +      return i ? -EINVAL : 0;
>     > > >>>> +}
>     > > >>>
>     > > >>> You can drop the input IOCTLs altogether here. If you had e.g. a TV
>     > > >>> tuner, it'd be the TV tuner driver's responsibility to implement them.
>     > > >>>
>     > > >>
>     > > >> input IOCTLs seems to be mandatory from v4l2-compliance when capability
>     > > >> V4L2_CAP_VIDEO_CAPTURE is set (which is the case):
>     > > >>
>     > > >> https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-test-input-output.cpp#n418
>     <https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-test-input-output.cpp#n418>
>     > > >>
>     > > >> https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-compliance.cpp#n989 <https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-compliance.cpp#n989>
>     > > >
>     > > > The V4L2 spec doesn't actually define what's mandatory and what's
>     > > > optional. The idea that was agreed on one of the media summits
>     > > > were to define a set of profiles for different device types,
>     > > > matching the features required by existing applications to work,
>     > > > but this was never materialized.
>     > > >
>     > > > So, my understanding is that any driver can implement
>     > > > any V4L2 ioctl.
>     > > >
>     > > > Yet, some applications require enum/get/set inputs, or otherwise
>     > > > they wouldn't work. It is too late to change this behavior.
>     > > > So, either the driver or the core should implement those
>     > > > ioctls, in order to avoid breaking backward-compatibility.
>     > >
>     > > The closest we have to determining which ioctls are mandatory or not is
>     > > v4l2-compliance.
>     >
>     > Yes, but we should explicitly document what's mandatory at the V4L2
>     > API spec and mention the v4l2-compliance tool there.
>     >
>     > > That said, v4l2-compliance is actually a bit more strict
>     > > in some cases than the spec since some ioctls are optional in the spec, but
>     > > required in v4l2-compliance for the simple reason that there is no reason
>     > > for drivers NOT to implement those ioctls.
>     > >
>     > > However, the v4l2-compliance test was never written for MC devices. It turns
>     > > out that it works reasonably well as long as a working pipeline is configured
>     > > first, but these input ioctls are a bit iffy.
>     >
>     > The way I see, v4l2-compliance V4L2 API check[1] should not be modified to
>     > explicitly support devices with MC and/or subdev API.
> 
>     The V4L2 API documentation states that
> 
>             Video inputs and outputs are physical connectors of a device. ...
>             Drivers must implement all the input ioctls when the device has one
>             or more inputs, all the output ioctls when the device has one or
>             more outputs.
> 
>     "Inputs" and "outputs", as the spec defines them, mean physical connectors
>     to the device.
> 
>     Does e.g. a camera have a physical connector? I don't think one could
>     imagine it does, meaning also there is no need to implement these IOCTLs.
> 
> 
> In the case of MC drivers, could that be used to allow selecting the sensor ? It's not physical connector, but it's physically different input. 

For MC drivers each video node has just a single input or output: it cannot be used
to switch between inputs, since that depends on the configured video pipeline.

It's just there to satisfy existing applications. I also think it can be useful
to provide quick information about the device where possible, e.g. "scalar input",
"bayer capture", etc.

Regards,

	Hans

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

* Re: [PATCH v7] [media] vimc: Virtual Media Controller core, capture and sensor
  2017-03-29  7:49                               ` Hans Verkuil
@ 2017-03-29  8:46                                 ` Sakari Ailus
  0 siblings, 0 replies; 41+ messages in thread
From: Sakari Ailus @ 2017-03-29  8:46 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Mauro Carvalho Chehab, Helen Koike, Helen Koike, linux-media,
	jgebben, Helen Fornazier, Laurent Pinchart

Hi Hans,

On Wed, Mar 29, 2017 at 09:49:05AM +0200, Hans Verkuil wrote:
> On 28/03/17 22:37, Sakari Ailus wrote:
> > Hi Mauro,
> > 
> > On Tue, Mar 28, 2017 at 08:38:26AM -0300, Mauro Carvalho Chehab wrote:
> >> Em Tue, 28 Mar 2017 12:00:36 +0200
> >> Hans Verkuil <hverkuil@xs4all.nl> escreveu:
> >>
> >>> On 27/03/17 20:09, Mauro Carvalho Chehab wrote:
> >>>> Em Mon, 27 Mar 2017 12:19:51 -0300
> >>>> Helen Koike <helen.koike@collabora.co.uk> escreveu:
> >>>>   
> >>>>> Hi Sakari,
> >>>>>
> >>>>> On 2017-03-26 10:31 AM, Sakari Ailus wrote:  
> >>>>>> Hi Helen,
> >>>>>>
> >>>>>> ...    
> >>>>>>> +static int vimc_cap_enum_input(struct file *file, void *priv,
> >>>>>>> +			       struct v4l2_input *i)
> >>>>>>> +{
> >>>>>>> +	/* We only have one input */
> >>>>>>> +	if (i->index > 0)
> >>>>>>> +		return -EINVAL;
> >>>>>>> +
> >>>>>>> +	i->type = V4L2_INPUT_TYPE_CAMERA;
> >>>>>>> +	strlcpy(i->name, "VIMC capture", sizeof(i->name));
> >>>>>>> +
> >>>>>>> +	return 0;
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +static int vimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
> >>>>>>> +{
> >>>>>>> +	/* We only have one input */
> >>>>>>> +	*i = 0;
> >>>>>>> +	return 0;
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +static int vimc_cap_s_input(struct file *file, void *priv, unsigned int i)
> >>>>>>> +{
> >>>>>>> +	/* We only have one input */
> >>>>>>> +	return i ? -EINVAL : 0;
> >>>>>>> +}    
> >>>>>>
> >>>>>> You can drop the input IOCTLs altogether here. If you had e.g. a TV
> >>>>>> tuner, it'd be the TV tuner driver's responsibility to implement them.
> >>>>>>    
> >>>>>
> >>>>> input IOCTLs seems to be mandatory from v4l2-compliance when capability 
> >>>>> V4L2_CAP_VIDEO_CAPTURE is set (which is the case):
> >>>>>
> >>>>> https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-test-input-output.cpp#n418
> >>>>>
> >>>>> https://git.linuxtv.org/v4l-utils.git/tree/utils/v4l2-compliance/v4l2-compliance.cpp#n989  
> >>>>
> >>>> The V4L2 spec doesn't actually define what's mandatory and what's
> >>>> optional. The idea that was agreed on one of the media summits
> >>>> were to define a set of profiles for different device types,
> >>>> matching the features required by existing applications to work,
> >>>> but this was never materialized.
> >>>>
> >>>> So, my understanding is that any driver can implement
> >>>> any V4L2 ioctl.
> >>>>
> >>>> Yet, some applications require enum/get/set inputs, or otherwise
> >>>> they wouldn't work. It is too late to change this behavior. 
> >>>> So, either the driver or the core should implement those
> >>>> ioctls, in order to avoid breaking backward-compatibility.  
> >>>
> >>> The closest we have to determining which ioctls are mandatory or not is
> >>> v4l2-compliance.
> >>
> >> Yes, but we should explicitly document what's mandatory at the V4L2
> >> API spec and mention the v4l2-compliance tool there.
> >>
> >>> That said, v4l2-compliance is actually a bit more strict
> >>> in some cases than the spec since some ioctls are optional in the spec, but
> >>> required in v4l2-compliance for the simple reason that there is no reason
> >>> for drivers NOT to implement those ioctls.
> >>>
> >>> However, the v4l2-compliance test was never written for MC devices. It turns
> >>> out that it works reasonably well as long as a working pipeline is configured
> >>> first, but these input ioctls are a bit iffy.
> >>
> >> The way I see, v4l2-compliance V4L2 API check[1] should not be modified to
> >> explicitly support devices with MC and/or subdev API.
> > 
> > The V4L2 API documentation states that
> > 
> > 	Video inputs and outputs are physical connectors of a device. ...
> > 	Drivers must implement all the input ioctls when the device has one
> > 	or more inputs, all the output ioctls when the device has one or
> > 	more outputs.
> > 
> > "Inputs" and "outputs", as the spec defines them, mean physical connectors
> > to the device.
> > 
> > Does e.g. a camera have a physical connector? I don't think one could
> > imagine it does, meaning also there is no need to implement these IOCTLs.
> > 
> > That said, I looked at a few drivers and even the omap3isp driver implements
> > the input IOCTLs. It provides no useful information whatsoever through them,
> > just like most drivers whose hardware has no physical connectors.
> > 
> > Still the bottom line is that the spec does not require them.
> 
> The spec isn't gospel. The reality is that all non-MC drivers have these ioctls
> and a sensor is considered to be an input.
> 
> Section 4.1.2 says: "The video input and video standard ioctls must be supported
> by all video capture devices.". Which actually is also wrong since the video
> standard ioctls do not make sense for DV inputs or sensors.
> 
> I'll make a patch correcting these issues.

I'm far from certain that providing an additional interface that provides no
useful information or a way to control something is beneficial. However, if
you choose to do that, then please follow Mauro's suggestion to add helper
functions for drivers to do this.

I wonder whether calling the input just e.g. "default input" or such would
do the job.

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* [PATCH v9] [media] vimc: Virtual Media Controller core, capture and sensor
  2017-03-27 13:33                     ` [PATCH v8] " Helen Koike
@ 2017-04-03 22:16                       ` Helen Koike
  2017-04-06 14:29                         ` Helen Koike
  2017-04-07  9:54                         ` Hans Verkuil
  0 siblings, 2 replies; 41+ messages in thread
From: Helen Koike @ 2017-04-03 22:16 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-media, jgebben, mchehab, Sakari Ailus, Laurent Pinchart

First version of the Virtual Media Controller.
Add a simple version of the core of the driver, the capture and
sensor nodes in the topology, generating a grey image in a hardcoded
format.

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

---

Patch based in media/master tree, and available here:
https://github.com/helen-fornazier/opw-staging/tree/vimc/devel/v8

Changes since v8:
- fix vimc_sen_enum_mbus_code: return EINVAL if code->index > 0
- remove input ioctls from vimc-capture.c as v4l2 intends to have default
input ioctls in the core
- change kernel-docs for vimc_pix_map_by_* so the line fits in 80
characters

Changes since v7:
- remove left over union in struct vimc_ent_device
- remove v4l2_dev pointer from vimc_sen_device structure
- remove unused dev parameter from vimc_propagate_frame()
- remove struct device *dev from struct vimc_cap_device and vimc_sen_device
- in vimc_sen_create: call media_entity_pads_init() after v4l2_subdev_init()
to avoid double initialization of vsen->sd.entity.name
- in vimc_sen_destroy: move media_entity_cleanup to be after v4l2_device_unregister_subdev
- rename video_device back to vdev instead of vd
- adjust copyright with range 2015-2017
- add node names in dev_err prints for vimc-capture.c and vimc-sensor.c
- remove prefix "cap" in dev_dbg as it already uses the name of the node

Changes since v6:
- add kernel-docs in vimc-core.h
- reorder list_del call in vimc_cap_return_all_buffers()
- call media_pipeline_stop in vimc_cap_start_streaming when fail
- remove DMA comment (left over from the sample driver)
- remove vb2_set_plane_payload call in vimc_cap_buffer_prepare
- remove format verification in vimc_cap_link_validate
- set vimc_pix_map_list as static
- use MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN in vimc_raw_create()
- register media device after creating the topology and unregister it before destroying the topology
- replace devm_kzalloc by kzalloc for allocating struct vimc_device
- uset TASK_UNINTERRUPTIBLE for vimc_thread_sen
- do not allow the creation of a sensor with no pads
- add more verbose description in Kconfig
- change copyright to 2017
- arrange includes: number before letters
- remove v4l2_dev pointer from vimc_cap_device structure
- coding style adjustments
- remove entity variable in vimc_cap_pipeline_s_stream
- declare and assign variables in vimc_cap_link_validate
- declare vimc_dev_release() and vimc_pdev closer to vimc_init()
- remove pad check in vimc_sen_enum_{mbus_code,frame_size} that is already performed by v4l2-subdev.c
- fix multiline comments to start with /*
- reorder variable declaration in functions
- remove pad and subdevice type check in vimc_cap_pipeline_s_stream
- add a note that sensor nodes can can have more then one source pad
- remove the use of entity->use_count in the core and attach the ved structure in sd or vd, use
  ent->obj_type to know which structure (sd or vd) to use
- rename vdev (video_device) to vd to be similar to sd (subdev)

Changes since v5:
- Fix message "Entity type for entity Sensor A was not initialized!"
  by initializing the sensor entity.function after the calling
  v4l2_subded_init
- populate device_caps in vimc_cap_create instead of in
  vimc_cap_querycap
- Fix typo in vimc-core.c s/de/the

Changes since v4:
- coding style fixes
- remove BUG_ON
- change copyright to 2016
- depens on VIDEO_V4L2_SUBDEV_API instead of select
- remove assignement of V4L2_CAP_DEVICE_CAPS
- s/vimc_cap_g_fmt_vid_cap/vimc_cap_fmt_vid_cap
- fix vimc_cap_queue_setup declaration type
- remove wrong buffer size check and add it in vimc_cap_buffer_prepare
- vimc_cap_create: remove unecessary check if v4l2_dev or v4l2_dev->dev is null
- vimc_cap_create: only allow a single pad
- vimc_sen_create: only allow source pads, remove unecessary source pads
checks in vimc_thread_sen

Changes since v3: fix rmmod crash and built-in compile
- Re-order unregister calls in vimc_device_unregister function (remove
rmmod issue)
- Call media_device_unregister_entity in vimc_raw_destroy
- Add depends on VIDEO_DEV && VIDEO_V4L2 and select VIDEOBUF2_VMALLOC
- Check *nplanes in queue_setup (this remove v4l2-compliance fail)
- Include <linux/kthread.h> in vimc-sensor.c
- Move include of <linux/slab.h> from vimc-core.c to vimc-core.h
- Generate 60 frames per sec instead of 1 in the sensor

Changes since v2: update with current media master tree
- Add struct media_pipeline in vimc_cap_device
- Use vb2_v4l2_buffer instead of vb2_buffer
- Typos
- Remove usage of entity->type and use entity->function instead
- Remove fmt argument from queue setup
- Use ktime_get_ns instead of v4l2_get_timestamp
- Iterate over link's list using list_for_each_entry
- Use media_device_{init, cleanup}
- Use entity->use_count to keep track of entities instead of the old
entity->id
- Replace media_entity_init by media_entity_pads_init
---
 drivers/media/platform/Kconfig             |   2 +
 drivers/media/platform/Makefile            |   1 +
 drivers/media/platform/vimc/Kconfig        |  14 +
 drivers/media/platform/vimc/Makefile       |   3 +
 drivers/media/platform/vimc/vimc-capture.c | 505 +++++++++++++++++++++
 drivers/media/platform/vimc/vimc-capture.h |  28 ++
 drivers/media/platform/vimc/vimc-core.c    | 695 +++++++++++++++++++++++++++++
 drivers/media/platform/vimc/vimc-core.h    | 112 +++++
 drivers/media/platform/vimc/vimc-sensor.c  | 276 ++++++++++++
 drivers/media/platform/vimc/vimc-sensor.h  |  28 ++
 10 files changed, 1664 insertions(+)
 create mode 100644 drivers/media/platform/vimc/Kconfig
 create mode 100644 drivers/media/platform/vimc/Makefile
 create mode 100644 drivers/media/platform/vimc/vimc-capture.c
 create mode 100644 drivers/media/platform/vimc/vimc-capture.h
 create mode 100644 drivers/media/platform/vimc/vimc-core.c
 create mode 100644 drivers/media/platform/vimc/vimc-core.h
 create mode 100644 drivers/media/platform/vimc/vimc-sensor.c
 create mode 100644 drivers/media/platform/vimc/vimc-sensor.h

diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index ab0bb48..b764fba 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -466,6 +466,8 @@ menuconfig V4L_TEST_DRIVERS
 
 if V4L_TEST_DRIVERS
 
+source "drivers/media/platform/vimc/Kconfig"
+
 source "drivers/media/platform/vivid/Kconfig"
 
 config VIDEO_VIM2M
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 8959f6e..4af4cce 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_VIDEO_PXA27x)	+= pxa_camera.o
 
 obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o
 
+obj-$(CONFIG_VIDEO_VIMC)		+= vimc/
 obj-$(CONFIG_VIDEO_VIVID)		+= vivid/
 obj-$(CONFIG_VIDEO_VIM2M)		+= vim2m.o
 
diff --git a/drivers/media/platform/vimc/Kconfig b/drivers/media/platform/vimc/Kconfig
new file mode 100644
index 0000000..dd285fa
--- /dev/null
+++ b/drivers/media/platform/vimc/Kconfig
@@ -0,0 +1,14 @@
+config VIDEO_VIMC
+	tristate "Virtual Media Controller Driver (VIMC)"
+	depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+	select VIDEOBUF2_VMALLOC
+	default n
+	---help---
+	  Skeleton driver for Virtual Media Controller
+
+	  This drivers can be compared to the Vivid driver for emulating
+	  a media node that exposes a complex media topology. The topology
+	  is hard coded for now but is meant to be highly configurable in
+	  the future.
+
+	  When in doubt, say N.
diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile
new file mode 100644
index 0000000..c45195e
--- /dev/null
+++ b/drivers/media/platform/vimc/Makefile
@@ -0,0 +1,3 @@
+vimc-objs := vimc-core.o vimc-capture.o vimc-sensor.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
new file mode 100644
index 0000000..2ecd9c9
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-capture.c
@@ -0,0 +1,505 @@
+/*
+ * vimc-capture.c Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-vmalloc.h>
+
+#include "vimc-capture.h"
+
+struct vimc_cap_device {
+	struct vimc_ent_device ved;
+	struct video_device vdev;
+	struct v4l2_pix_format format;
+	struct vb2_queue queue;
+	struct list_head buf_list;
+	/*
+	 * NOTE: in a real driver, a spin lock must be used to access the
+	 * queue because the frames are generated from a hardware interruption
+	 * and the isr is not allowed to sleep.
+	 * Even if it is not necessary a spinlock in the vimc driver, we
+	 * use it here as a code reference
+	 */
+	spinlock_t qlock;
+	struct mutex lock;
+	u32 sequence;
+	struct media_pipeline pipe;
+};
+
+struct vimc_cap_buffer {
+	/*
+	 * struct vb2_v4l2_buffer must be the first element
+	 * the videobuf2 framework will allocate this struct based on
+	 * buf_struct_size and use the first sizeof(struct vb2_buffer) bytes of
+	 * memory as a vb2_buffer
+	 */
+	struct vb2_v4l2_buffer vb2;
+	struct list_head list;
+};
+
+static int vimc_cap_querycap(struct file *file, void *priv,
+			     struct v4l2_capability *cap)
+{
+	struct vimc_cap_device *vcap = video_drvdata(file);
+
+	strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
+	strlcpy(cap->card, KBUILD_MODNAME, sizeof(cap->card));
+	snprintf(cap->bus_info, sizeof(cap->bus_info),
+		 "platform:%s", vcap->vdev.v4l2_dev->name);
+
+	return 0;
+}
+
+static int vimc_cap_fmt_vid_cap(struct file *file, void *priv,
+				  struct v4l2_format *f)
+{
+	struct vimc_cap_device *vcap = video_drvdata(file);
+
+	f->fmt.pix = vcap->format;
+
+	return 0;
+}
+
+static int vimc_cap_enum_fmt_vid_cap(struct file *file, void *priv,
+				     struct v4l2_fmtdesc *f)
+{
+	struct vimc_cap_device *vcap = video_drvdata(file);
+
+	if (f->index > 0)
+		return -EINVAL;
+
+	/* We only support one format for now */
+	f->pixelformat = vcap->format.pixelformat;
+
+	return 0;
+}
+
+static const struct v4l2_file_operations vimc_cap_fops = {
+	.owner		= THIS_MODULE,
+	.open		= v4l2_fh_open,
+	.release	= vb2_fop_release,
+	.read           = vb2_fop_read,
+	.poll		= vb2_fop_poll,
+	.unlocked_ioctl = video_ioctl2,
+	.mmap           = vb2_fop_mmap,
+};
+
+static const struct v4l2_ioctl_ops vimc_cap_ioctl_ops = {
+	.vidioc_querycap = vimc_cap_querycap,
+
+	.vidioc_g_fmt_vid_cap = vimc_cap_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap = vimc_cap_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap = vimc_cap_fmt_vid_cap,
+	.vidioc_enum_fmt_vid_cap = vimc_cap_enum_fmt_vid_cap,
+
+	.vidioc_reqbufs = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs = vb2_ioctl_create_bufs,
+	.vidioc_querybuf = vb2_ioctl_querybuf,
+	.vidioc_qbuf = vb2_ioctl_qbuf,
+	.vidioc_dqbuf = vb2_ioctl_dqbuf,
+	.vidioc_expbuf = vb2_ioctl_expbuf,
+	.vidioc_streamon = vb2_ioctl_streamon,
+	.vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+static void vimc_cap_return_all_buffers(struct vimc_cap_device *vcap,
+					enum vb2_buffer_state state)
+{
+	struct vimc_cap_buffer *vbuf, *node;
+
+	spin_lock(&vcap->qlock);
+
+	list_for_each_entry_safe(vbuf, node, &vcap->buf_list, list) {
+		list_del(&vbuf->list);
+		vb2_buffer_done(&vbuf->vb2.vb2_buf, state);
+	}
+
+	spin_unlock(&vcap->qlock);
+}
+
+static int vimc_cap_pipeline_s_stream(struct vimc_cap_device *vcap, int enable)
+{
+	struct v4l2_subdev *sd;
+	struct media_pad *pad;
+	int ret;
+
+	/* Start the stream in the subdevice direct connected */
+	pad = media_entity_remote_pad(&vcap->vdev.entity.pads[0]);
+
+	/*
+	 * if it is a raw node from vimc-core, there is nothing to activate
+	 * TODO: remove this when there are no more raw nodes in the
+	 * core and return error instead
+	 */
+	if (pad->entity->obj_type == MEDIA_ENTITY_TYPE_BASE)
+		return 0;
+
+	sd = media_entity_to_v4l2_subdev(pad->entity);
+	ret = v4l2_subdev_call(sd, video, s_stream, enable);
+	if (ret && ret != -ENOIOCTLCMD)
+		return ret;
+
+	return 0;
+}
+
+static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
+	struct media_entity *entity = &vcap->vdev.entity;
+	int ret;
+
+	vcap->sequence = 0;
+
+	/* Start the media pipeline */
+	ret = media_pipeline_start(entity, &vcap->pipe);
+	if (ret) {
+		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
+		return ret;
+	}
+
+	/* Enable streaming from the pipe */
+	ret = vimc_cap_pipeline_s_stream(vcap, 1);
+	if (ret) {
+		media_pipeline_stop(entity);
+		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Stop the stream engine. Any remaining buffers in the stream queue are
+ * dequeued and passed on to the vb2 framework marked as STATE_ERROR.
+ */
+static void vimc_cap_stop_streaming(struct vb2_queue *vq)
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
+
+	/* Disable streaming from the pipe */
+	vimc_cap_pipeline_s_stream(vcap, 0);
+
+	/* Stop the media pipeline */
+	media_pipeline_stop(&vcap->vdev.entity);
+
+	/* Release all active buffers */
+	vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_ERROR);
+}
+
+static void vimc_cap_buf_queue(struct vb2_buffer *vb2_buf)
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb2_buf->vb2_queue);
+	struct vimc_cap_buffer *buf = container_of(vb2_buf,
+						   struct vimc_cap_buffer,
+						   vb2.vb2_buf);
+
+	spin_lock(&vcap->qlock);
+	list_add_tail(&buf->list, &vcap->buf_list);
+	spin_unlock(&vcap->qlock);
+}
+
+static int vimc_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
+				unsigned int *nplanes, unsigned int sizes[],
+				struct device *alloc_devs[])
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
+
+	if (*nplanes)
+		return sizes[0] < vcap->format.sizeimage ? -EINVAL : 0;
+	/* We don't support multiplanes for now */
+	*nplanes = 1;
+	sizes[0] = vcap->format.sizeimage;
+
+	return 0;
+}
+
+static int vimc_cap_buffer_prepare(struct vb2_buffer *vb)
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb->vb2_queue);
+	unsigned long size = vcap->format.sizeimage;
+
+	if (vb2_plane_size(vb, 0) < size) {
+		dev_err(vcap->vdev.v4l2_dev->dev,
+			"%s: buffer too small (%lu < %lu)\n",
+			vcap->vdev.name, vb2_plane_size(vb, 0), size);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static const struct vb2_ops vimc_cap_qops = {
+	.start_streaming	= vimc_cap_start_streaming,
+	.stop_streaming		= vimc_cap_stop_streaming,
+	.buf_queue		= vimc_cap_buf_queue,
+	.queue_setup		= vimc_cap_queue_setup,
+	.buf_prepare		= vimc_cap_buffer_prepare,
+	/*
+	 * Since q->lock is set we can use the standard
+	 * vb2_ops_wait_prepare/finish helper functions.
+	 */
+	.wait_prepare		= vb2_ops_wait_prepare,
+	.wait_finish		= vb2_ops_wait_finish,
+};
+
+/*
+ * NOTE: this function is a copy of v4l2_subdev_link_validate_get_format
+ * maybe the v4l2 function should be public
+ */
+static int vimc_cap_v4l2_subdev_link_validate_get_format(struct media_pad *pad,
+						struct v4l2_subdev_format *fmt)
+{
+	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity);
+
+	fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	fmt->pad = pad->index;
+
+	return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt);
+}
+
+static int vimc_cap_link_validate(struct media_link *link)
+{
+	struct v4l2_subdev_format source_fmt;
+	const struct vimc_pix_map *vpix;
+	struct vimc_cap_device *vcap = container_of(link->sink->entity,
+						    struct vimc_cap_device,
+						    vdev.entity);
+	struct v4l2_pix_format *sink_fmt = &vcap->format;
+	int ret;
+
+	/*
+	 * if it is a raw node from vimc-core, ignore the link for now
+	 * TODO: remove this when there are no more raw nodes in the
+	 * core and return error instead
+	 */
+	if (link->source->entity->obj_type == MEDIA_ENTITY_TYPE_BASE)
+		return 0;
+
+	/* Get the the format of the subdev */
+	ret = vimc_cap_v4l2_subdev_link_validate_get_format(link->source,
+							    &source_fmt);
+	if (ret)
+		return ret;
+
+	dev_dbg(vcap->vdev.v4l2_dev->dev,
+		"%s: link validate formats src:%dx%d %d sink:%dx%d %d\n",
+		vcap->vdev.name,
+		source_fmt.format.width, source_fmt.format.height,
+		source_fmt.format.code,
+		sink_fmt->width, sink_fmt->height,
+		sink_fmt->pixelformat);
+
+	/* The width, height and code must match. */
+	vpix = vimc_pix_map_by_pixelformat(sink_fmt->pixelformat);
+	if (source_fmt.format.width != sink_fmt->width
+	    || source_fmt.format.height != sink_fmt->height
+	    || vpix->code != source_fmt.format.code)
+		return -EINVAL;
+
+	/*
+	 * The field order must match, or the sink field order must be NONE
+	 * to support interlaced hardware connected to bridges that support
+	 * progressive formats only.
+	 */
+	if (source_fmt.format.field != sink_fmt->field &&
+	    sink_fmt->field != V4L2_FIELD_NONE)
+		return -EINVAL;
+
+	return 0;
+}
+
+static const struct media_entity_operations vimc_cap_mops = {
+	.link_validate		= vimc_cap_link_validate,
+};
+
+static void vimc_cap_destroy(struct vimc_ent_device *ved)
+{
+	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
+						    ved);
+
+	vb2_queue_release(&vcap->queue);
+	media_entity_cleanup(ved->ent);
+	video_unregister_device(&vcap->vdev);
+	vimc_pads_cleanup(vcap->ved.pads);
+	kfree(vcap);
+}
+
+static void vimc_cap_process_frame(struct vimc_ent_device *ved,
+				   struct media_pad *sink, const void *frame)
+{
+	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
+						    ved);
+	struct vimc_cap_buffer *vimc_buf;
+	void *vbuf;
+
+	/* If the stream in this node is not active, just return */
+	mutex_lock(&vcap->lock);
+	if (!vb2_is_busy(&vcap->queue)) {
+		mutex_unlock(&vcap->lock);
+		return;
+	}
+	mutex_unlock(&vcap->lock);
+
+	spin_lock(&vcap->qlock);
+
+	/* Get the first entry of the list */
+	vimc_buf = list_first_entry_or_null(&vcap->buf_list,
+					    typeof(*vimc_buf), list);
+	if (!vimc_buf) {
+		spin_unlock(&vcap->qlock);
+		return;
+	}
+
+	/* Remove this entry from the list */
+	list_del(&vimc_buf->list);
+
+	spin_unlock(&vcap->qlock);
+
+	/* Fill the buffer */
+	vimc_buf->vb2.vb2_buf.timestamp = ktime_get_ns();
+	vimc_buf->vb2.sequence = vcap->sequence++;
+	vimc_buf->vb2.field = vcap->format.field;
+
+	vbuf = vb2_plane_vaddr(&vimc_buf->vb2.vb2_buf, 0);
+
+	memcpy(vbuf, frame, vcap->format.sizeimage);
+
+	/* Set it as ready */
+	vb2_set_plane_payload(&vimc_buf->vb2.vb2_buf, 0,
+			      vcap->format.sizeimage);
+	vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag)
+{
+	const struct vimc_pix_map *vpix;
+	struct vimc_cap_device *vcap;
+	struct video_device *vdev;
+	struct vb2_queue *q;
+	int ret;
+
+	/*
+	 * Check entity configuration params
+	 * NOTE: we only support a single sink pad
+	 */
+	if (!name || num_pads != 1 || !pads_flag ||
+	    !(pads_flag[0] & MEDIA_PAD_FL_SINK))
+		return ERR_PTR(-EINVAL);
+
+	/* Allocate the vimc_cap_device struct */
+	vcap = kzalloc(sizeof(*vcap), GFP_KERNEL);
+	if (!vcap)
+		return ERR_PTR(-ENOMEM);
+
+	/* Allocate the pads */
+	vcap->ved.pads = vimc_pads_init(num_pads, pads_flag);
+	if (IS_ERR(vcap->ved.pads)) {
+		ret = PTR_ERR(vcap->ved.pads);
+		goto err_free_vcap;
+	}
+
+	/* Initialize the media entity */
+	vcap->vdev.entity.name = name;
+	vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L;
+	ret = media_entity_pads_init(&vcap->vdev.entity,
+				     num_pads, vcap->ved.pads);
+	if (ret)
+		goto err_clean_pads;
+
+	/* Initialize the lock */
+	mutex_init(&vcap->lock);
+
+	/* Initialize the vb2 queue */
+	q = &vcap->queue;
+	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	q->io_modes = VB2_MMAP | VB2_DMABUF;
+	q->drv_priv = vcap;
+	q->buf_struct_size = sizeof(struct vimc_cap_buffer);
+	q->ops = &vimc_cap_qops;
+	q->mem_ops = &vb2_vmalloc_memops;
+	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	q->min_buffers_needed = 2;
+	q->lock = &vcap->lock;
+
+	ret = vb2_queue_init(q);
+	if (ret) {
+		dev_err(vcap->vdev.v4l2_dev->dev,
+			"%s: vb2 queue init failed (err=%d)\n",
+			vcap->vdev.name, ret);
+		goto err_clean_m_ent;
+	}
+
+	/* Initialize buffer list and its lock */
+	INIT_LIST_HEAD(&vcap->buf_list);
+	spin_lock_init(&vcap->qlock);
+
+	/* Set the frame format (this is hardcoded for now) */
+	vcap->format.width = 640;
+	vcap->format.height = 480;
+	vcap->format.pixelformat = V4L2_PIX_FMT_RGB24;
+	vcap->format.field = V4L2_FIELD_NONE;
+	vcap->format.colorspace = V4L2_COLORSPACE_SRGB;
+
+	vpix = vimc_pix_map_by_pixelformat(vcap->format.pixelformat);
+
+	vcap->format.bytesperline = vcap->format.width * vpix->bpp;
+	vcap->format.sizeimage = vcap->format.bytesperline *
+				 vcap->format.height;
+
+	/* Fill the vimc_ent_device struct */
+	vcap->ved.destroy = vimc_cap_destroy;
+	vcap->ved.ent = &vcap->vdev.entity;
+	vcap->ved.process_frame = vimc_cap_process_frame;
+
+	/* Initialize the video_device struct */
+	vdev = &vcap->vdev;
+	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	vdev->entity.ops = &vimc_cap_mops;
+	vdev->release = video_device_release_empty;
+	vdev->fops = &vimc_cap_fops;
+	vdev->ioctl_ops = &vimc_cap_ioctl_ops;
+	vdev->lock = &vcap->lock;
+	vdev->queue = q;
+	vdev->v4l2_dev = v4l2_dev;
+	vdev->vfl_dir = VFL_DIR_RX;
+	strlcpy(vdev->name, name, sizeof(vdev->name));
+	video_set_drvdata(vdev, &vcap->ved);
+
+	/* Register the video_device with the v4l2 and the media framework */
+	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+	if (ret) {
+		dev_err(vcap->vdev.v4l2_dev->dev,
+			"%s: video register failed (err=%d)\n",
+			vcap->vdev.name, ret);
+		goto err_release_queue;
+	}
+
+	return &vcap->ved;
+
+err_release_queue:
+	vb2_queue_release(q);
+err_clean_m_ent:
+	media_entity_cleanup(&vcap->vdev.entity);
+err_clean_pads:
+	vimc_pads_cleanup(vcap->ved.pads);
+err_free_vcap:
+	kfree(vcap);
+
+	return ERR_PTR(ret);
+}
diff --git a/drivers/media/platform/vimc/vimc-capture.h b/drivers/media/platform/vimc/vimc-capture.h
new file mode 100644
index 0000000..581a813
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-capture.h
@@ -0,0 +1,28 @@
+/*
+ * vimc-capture.h Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIMC_CAPTURE_H_
+#define _VIMC_CAPTURE_H_
+
+#include "vimc-core.h"
+
+struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag);
+
+#endif
diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c
new file mode 100644
index 0000000..bc107da
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-core.c
@@ -0,0 +1,695 @@
+/*
+ * vimc-core.c Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <media/media-device.h>
+#include <media/v4l2-device.h>
+
+#include "vimc-capture.h"
+#include "vimc-core.h"
+#include "vimc-sensor.h"
+
+#define VIMC_PDEV_NAME "vimc"
+#define VIMC_MDEV_MODEL_NAME "VIMC MDEV"
+
+#define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) {	\
+	.src_ent = src,						\
+	.src_pad = srcpad,					\
+	.sink_ent = sink,					\
+	.sink_pad = sinkpad,					\
+	.flags = link_flags,					\
+}
+
+struct vimc_device {
+	/*
+	 * The pipeline configuration
+	 * (filled before calling vimc_device_register)
+	 */
+	const struct vimc_pipeline_config *pipe_cfg;
+
+	/* The Associated media_device parent */
+	struct media_device mdev;
+
+	/* Internal v4l2 parent device*/
+	struct v4l2_device v4l2_dev;
+
+	/* Internal topology */
+	struct vimc_ent_device **ved;
+};
+
+/**
+ * enum vimc_ent_node - Select the functionality of a node in the topology
+ * @VIMC_ENT_NODE_SENSOR:	A node of type SENSOR simulates a camera sensor
+ *				generating internal images in bayer format and
+ *				propagating those images through the pipeline
+ * @VIMC_ENT_NODE_CAPTURE:	A node of type CAPTURE is a v4l2 video_device
+ *				that exposes the received image from the
+ *				pipeline to the user space
+ * @VIMC_ENT_NODE_INPUT:	A node of type INPUT is a v4l2 video_device that
+ *				receives images from the user space and
+ *				propagates them through the pipeline
+ * @VIMC_ENT_NODE_DEBAYER:	A node type DEBAYER expects to receive a frame
+ *				in bayer format converts it to RGB
+ * @VIMC_ENT_NODE_SCALER:	A node of type SCALER scales the received image
+ *				by a given multiplier
+ *
+ * This enum is used in the entity configuration struct to allow the definition
+ * of a custom topology specifying the role of each node on it.
+ */
+enum vimc_ent_node {
+	VIMC_ENT_NODE_SENSOR,
+	VIMC_ENT_NODE_CAPTURE,
+	VIMC_ENT_NODE_INPUT,
+	VIMC_ENT_NODE_DEBAYER,
+	VIMC_ENT_NODE_SCALER,
+};
+
+/* Structure which describes individual configuration for each entity */
+struct vimc_ent_config {
+	const char *name;
+	size_t pads_qty;
+	const unsigned long *pads_flag;
+	enum vimc_ent_node node;
+};
+
+/* Structure which describes links between entities */
+struct vimc_ent_link {
+	unsigned int src_ent;
+	u16 src_pad;
+	unsigned int sink_ent;
+	u16 sink_pad;
+	u32 flags;
+};
+
+/* Structure which describes the whole topology */
+struct vimc_pipeline_config {
+	const struct vimc_ent_config *ents;
+	size_t num_ents;
+	const struct vimc_ent_link *links;
+	size_t num_links;
+};
+
+/* --------------------------------------------------------------------------
+ * Topology Configuration
+ */
+
+static const struct vimc_ent_config ent_config[] = {
+	{
+		.name = "Sensor A",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_SENSOR,
+	},
+	{
+		.name = "Sensor B",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_SENSOR,
+	},
+	{
+		.name = "Debayer A",
+		.pads_qty = 2,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
+						     MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_DEBAYER,
+	},
+	{
+		.name = "Debayer B",
+		.pads_qty = 2,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
+						     MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_DEBAYER,
+	},
+	{
+		.name = "Raw Capture 0",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
+		.node = VIMC_ENT_NODE_CAPTURE,
+	},
+	{
+		.name = "Raw Capture 1",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
+		.node = VIMC_ENT_NODE_CAPTURE,
+	},
+	{
+		.name = "RGB/YUV Input",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_INPUT,
+	},
+	{
+		.name = "Scaler",
+		.pads_qty = 2,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
+						     MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_SCALER,
+	},
+	{
+		.name = "RGB/YUV Capture",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
+		.node = VIMC_ENT_NODE_CAPTURE,
+	},
+};
+
+static const struct vimc_ent_link ent_links[] = {
+	/* Link: Sensor A (Pad 0)->(Pad 0) Debayer A */
+	VIMC_ENT_LINK(0, 0, 2, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Sensor A (Pad 0)->(Pad 0) Raw Capture 0 */
+	VIMC_ENT_LINK(0, 0, 4, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Sensor B (Pad 0)->(Pad 0) Debayer B */
+	VIMC_ENT_LINK(1, 0, 3, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Sensor B (Pad 0)->(Pad 0) Raw Capture 1 */
+	VIMC_ENT_LINK(1, 0, 5, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Debayer A (Pad 1)->(Pad 0) Scaler */
+	VIMC_ENT_LINK(2, 1, 7, 0, MEDIA_LNK_FL_ENABLED),
+	/* Link: Debayer B (Pad 1)->(Pad 0) Scaler */
+	VIMC_ENT_LINK(3, 1, 7, 0, 0),
+	/* Link: RGB/YUV Input (Pad 0)->(Pad 0) Scaler */
+	VIMC_ENT_LINK(6, 0, 7, 0, 0),
+	/* Link: Scaler (Pad 1)->(Pad 0) RGB/YUV Capture */
+	VIMC_ENT_LINK(7, 1, 8, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+};
+
+static const struct vimc_pipeline_config pipe_cfg = {
+	.ents		= ent_config,
+	.num_ents	= ARRAY_SIZE(ent_config),
+	.links		= ent_links,
+	.num_links	= ARRAY_SIZE(ent_links)
+};
+
+/* -------------------------------------------------------------------------- */
+
+static const struct vimc_pix_map vimc_pix_map_list[] = {
+	/* TODO: add all missing formats */
+
+	/* RGB formats */
+	{
+		.code = MEDIA_BUS_FMT_BGR888_1X24,
+		.pixelformat = V4L2_PIX_FMT_BGR24,
+		.bpp = 3,
+	},
+	{
+		.code = MEDIA_BUS_FMT_RGB888_1X24,
+		.pixelformat = V4L2_PIX_FMT_RGB24,
+		.bpp = 3,
+	},
+	{
+		.code = MEDIA_BUS_FMT_ARGB8888_1X32,
+		.pixelformat = V4L2_PIX_FMT_ARGB32,
+		.bpp = 4,
+	},
+
+	/* Bayer formats */
+	{
+		.code = MEDIA_BUS_FMT_SBGGR8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SBGGR8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGBRG8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SGBRG8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGRBG8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SGRBG8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SRGGB8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SRGGB8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SBGGR10_1X10,
+		.pixelformat = V4L2_PIX_FMT_SBGGR10,
+		.bpp = 2,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGBRG10_1X10,
+		.pixelformat = V4L2_PIX_FMT_SGBRG10,
+		.bpp = 2,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGRBG10_1X10,
+		.pixelformat = V4L2_PIX_FMT_SGRBG10,
+		.bpp = 2,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SRGGB10_1X10,
+		.pixelformat = V4L2_PIX_FMT_SRGGB10,
+		.bpp = 2,
+	},
+
+	/* 10bit raw bayer a-law compressed to 8 bits */
+	{
+		.code = MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SBGGR10ALAW8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SGBRG10ALAW8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SGRBG10ALAW8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SRGGB10ALAW8,
+		.bpp = 1,
+	},
+
+	/* 10bit raw bayer DPCM compressed to 8 bits */
+	{
+		.code = MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SBGGR10DPCM8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SGBRG10DPCM8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SRGGB10DPCM8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SBGGR12_1X12,
+		.pixelformat = V4L2_PIX_FMT_SBGGR12,
+		.bpp = 2,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGBRG12_1X12,
+		.pixelformat = V4L2_PIX_FMT_SGBRG12,
+		.bpp = 2,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGRBG12_1X12,
+		.pixelformat = V4L2_PIX_FMT_SGRBG12,
+		.bpp = 2,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SRGGB12_1X12,
+		.pixelformat = V4L2_PIX_FMT_SRGGB12,
+		.bpp = 2,
+	},
+};
+
+const struct vimc_pix_map *vimc_pix_map_by_code(u32 code)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(vimc_pix_map_list); i++) {
+		if (vimc_pix_map_list[i].code == code)
+			return &vimc_pix_map_list[i];
+	}
+	return NULL;
+}
+
+const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(vimc_pix_map_list); i++) {
+		if (vimc_pix_map_list[i].pixelformat == pixelformat)
+			return &vimc_pix_map_list[i];
+	}
+	return NULL;
+}
+
+int vimc_propagate_frame(struct media_pad *src, const void *frame)
+{
+	struct media_link *link;
+
+	if (!(src->flags & MEDIA_PAD_FL_SOURCE))
+		return -EINVAL;
+
+	/* Send this frame to all sink pads that are direct linked */
+	list_for_each_entry(link, &src->entity->links, list) {
+		if (link->source == src &&
+		    (link->flags & MEDIA_LNK_FL_ENABLED)) {
+			struct vimc_ent_device *ved = NULL;
+			struct media_entity *entity = link->sink->entity;
+
+			if (is_media_entity_v4l2_subdev(entity)) {
+				struct v4l2_subdev *sd =
+					container_of(entity, struct v4l2_subdev,
+						     entity);
+				ved = v4l2_get_subdevdata(sd);
+			} else if (is_media_entity_v4l2_video_device(entity)) {
+				struct video_device *vdev =
+					container_of(entity,
+						     struct video_device,
+						     entity);
+				ved = video_get_drvdata(vdev);
+			}
+			if (ved && ved->process_frame)
+				ved->process_frame(ved, link->sink, frame);
+		}
+	}
+
+	return 0;
+}
+
+static void vimc_device_unregister(struct vimc_device *vimc)
+{
+	unsigned int i;
+
+	media_device_unregister(&vimc->mdev);
+	/* Cleanup (only initialized) entities */
+	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
+		if (vimc->ved[i] && vimc->ved[i]->destroy)
+			vimc->ved[i]->destroy(vimc->ved[i]);
+
+		vimc->ved[i] = NULL;
+	}
+	v4l2_device_unregister(&vimc->v4l2_dev);
+	media_device_cleanup(&vimc->mdev);
+}
+
+/* Helper function to allocate and initialize pads */
+struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag)
+{
+	struct media_pad *pads;
+	unsigned int i;
+
+	/* Allocate memory for the pads */
+	pads = kcalloc(num_pads, sizeof(*pads), GFP_KERNEL);
+	if (!pads)
+		return ERR_PTR(-ENOMEM);
+
+	/* Initialize the pads */
+	for (i = 0; i < num_pads; i++) {
+		pads[i].index = i;
+		pads[i].flags = pads_flag[i];
+	}
+
+	return pads;
+}
+
+/*
+ * TODO: remove this function when all the
+ * entities specific code are implemented
+ */
+static void vimc_raw_destroy(struct vimc_ent_device *ved)
+{
+	media_device_unregister_entity(ved->ent);
+
+	media_entity_cleanup(ved->ent);
+
+	vimc_pads_cleanup(ved->pads);
+
+	kfree(ved->ent);
+
+	kfree(ved);
+}
+
+/*
+ * TODO: remove this function when all the
+ * entities specific code are implemented
+ */
+static struct vimc_ent_device *vimc_raw_create(struct v4l2_device *v4l2_dev,
+					       const char *const name,
+					       u16 num_pads,
+					       const unsigned long *pads_flag)
+{
+	struct vimc_ent_device *ved;
+	int ret;
+
+	/* Allocate the main ved struct */
+	ved = kzalloc(sizeof(*ved), GFP_KERNEL);
+	if (!ved)
+		return ERR_PTR(-ENOMEM);
+
+	/* Allocate the media entity */
+	ved->ent = kzalloc(sizeof(*ved->ent), GFP_KERNEL);
+	if (!ved->ent) {
+		ret = -ENOMEM;
+		goto err_free_ved;
+	}
+
+	/* Allocate the pads */
+	ved->pads = vimc_pads_init(num_pads, pads_flag);
+	if (IS_ERR(ved->pads)) {
+		ret = PTR_ERR(ved->pads);
+		goto err_free_ent;
+	}
+
+	/* Initialize the media entity */
+	ved->ent->name = name;
+	ved->ent->function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
+	ret = media_entity_pads_init(ved->ent, num_pads, ved->pads);
+	if (ret)
+		goto err_cleanup_pads;
+
+	/* Register the media entity */
+	ret = media_device_register_entity(v4l2_dev->mdev, ved->ent);
+	if (ret)
+		goto err_cleanup_entity;
+
+	/* Fill out the destroy function and return */
+	ved->destroy = vimc_raw_destroy;
+	return ved;
+
+err_cleanup_entity:
+	media_entity_cleanup(ved->ent);
+err_cleanup_pads:
+	vimc_pads_cleanup(ved->pads);
+err_free_ent:
+	kfree(ved->ent);
+err_free_ved:
+	kfree(ved);
+
+	return ERR_PTR(ret);
+}
+
+static int vimc_device_register(struct vimc_device *vimc)
+{
+	unsigned int i;
+	int ret;
+
+	/* Allocate memory for the vimc_ent_devices pointers */
+	vimc->ved = devm_kcalloc(vimc->mdev.dev, vimc->pipe_cfg->num_ents,
+				 sizeof(*vimc->ved), GFP_KERNEL);
+	if (!vimc->ved)
+		return -ENOMEM;
+
+	/* Link the media device within the v4l2_device */
+	vimc->v4l2_dev.mdev = &vimc->mdev;
+
+	/* Register the v4l2 struct */
+	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);
+		return ret;
+	}
+
+	/* Initialize entities */
+	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
+		struct vimc_ent_device *(*create_func)(struct v4l2_device *,
+						       const char *const,
+						       u16,
+						       const unsigned long *);
+
+		/* Register the specific node */
+		switch (vimc->pipe_cfg->ents[i].node) {
+		case VIMC_ENT_NODE_SENSOR:
+			create_func = vimc_sen_create;
+			break;
+
+		case VIMC_ENT_NODE_CAPTURE:
+			create_func = vimc_cap_create;
+			break;
+
+		/* TODO: Instantiate the specific topology node */
+		case VIMC_ENT_NODE_INPUT:
+		case VIMC_ENT_NODE_DEBAYER:
+		case VIMC_ENT_NODE_SCALER:
+		default:
+			/*
+			 * TODO: remove this when all the entities specific
+			 * code are implemented
+			 */
+			create_func = vimc_raw_create;
+			break;
+		}
+
+		vimc->ved[i] = create_func(&vimc->v4l2_dev,
+					   vimc->pipe_cfg->ents[i].name,
+					   vimc->pipe_cfg->ents[i].pads_qty,
+					   vimc->pipe_cfg->ents[i].pads_flag);
+		if (IS_ERR(vimc->ved[i])) {
+			ret = PTR_ERR(vimc->ved[i]);
+			vimc->ved[i] = NULL;
+			goto err;
+		}
+	}
+
+	/* 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];
+
+		ret = media_create_pad_link(vimc->ved[link->src_ent]->ent,
+					    link->src_pad,
+					    vimc->ved[link->sink_ent]->ent,
+					    link->sink_pad,
+					    link->flags);
+		if (ret)
+			goto err;
+	}
+
+	/* Register the media device */
+	ret = media_device_register(&vimc->mdev);
+	if (ret) {
+		dev_err(vimc->mdev.dev,
+			"media device register failed (err=%d)\n", ret);
+		return ret;
+	}
+
+	/* 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);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	/* Destroy the so far created topology */
+	vimc_device_unregister(vimc);
+
+	return ret;
+}
+
+static int vimc_probe(struct platform_device *pdev)
+{
+	struct vimc_device *vimc;
+	int ret;
+
+	/* Prepare the vimc topology structure */
+
+	/* Allocate memory for the vimc structure */
+	vimc = kzalloc(sizeof(*vimc), GFP_KERNEL);
+	if (!vimc)
+		return -ENOMEM;
+
+	/* Set the pipeline configuration struct */
+	vimc->pipe_cfg = &pipe_cfg;
+
+	/* Initialize media device */
+	strlcpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME,
+		sizeof(vimc->mdev.model));
+	vimc->mdev.dev = &pdev->dev;
+	media_device_init(&vimc->mdev);
+
+	/* Create vimc topology */
+	ret = vimc_device_register(vimc);
+	if (ret) {
+		dev_err(vimc->mdev.dev,
+			"vimc device registration failed (err=%d)\n", ret);
+		kfree(vimc);
+		return ret;
+	}
+
+	/* Link the topology object with the platform device object */
+	platform_set_drvdata(pdev, vimc);
+
+	return 0;
+}
+
+static int vimc_remove(struct platform_device *pdev)
+{
+	struct vimc_device *vimc = platform_get_drvdata(pdev);
+
+	/* Destroy all the topology */
+	vimc_device_unregister(vimc);
+	kfree(vimc);
+
+	return 0;
+}
+
+static void vimc_dev_release(struct device *dev)
+{
+}
+
+static struct platform_device vimc_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,
+	},
+};
+
+static int __init vimc_init(void)
+{
+	int ret;
+
+	ret = platform_device_register(&vimc_pdev);
+	if (ret) {
+		dev_err(&vimc_pdev.dev,
+			"platform device registration failed (err=%d)\n", ret);
+		return ret;
+	}
+
+	ret = platform_driver_register(&vimc_pdrv);
+	if (ret) {
+		dev_err(&vimc_pdev.dev,
+			"platform driver registration failed (err=%d)\n", ret);
+
+		platform_device_unregister(&vimc_pdev);
+	}
+
+	return ret;
+}
+
+static void __exit vimc_exit(void)
+{
+	platform_driver_unregister(&vimc_pdrv);
+
+	platform_device_unregister(&vimc_pdev);
+}
+
+module_init(vimc_init);
+module_exit(vimc_exit);
+
+MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC)");
+MODULE_AUTHOR("Helen Fornazier <helen.fornazier@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/vimc/vimc-core.h b/drivers/media/platform/vimc/vimc-core.h
new file mode 100644
index 0000000..4525d23
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-core.h
@@ -0,0 +1,112 @@
+/*
+ * vimc-core.h Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIMC_CORE_H_
+#define _VIMC_CORE_H_
+
+#include <linux/slab.h>
+#include <media/v4l2-device.h>
+
+/**
+ * struct vimc_pix_map - maps media bus code with v4l2 pixel format
+ *
+ * @code:		media bus format code defined by MEDIA_BUS_FMT_* macros
+ * @bbp:		number of bytes each pixel occupies
+ * @pixelformat:	pixel format devined by V4L2_PIX_FMT_* macros
+ *
+ * Struct which matches the MEDIA_BUS_FMT_* codes with the corresponding
+ * V4L2_PIX_FMT_* fourcc pixelformat and its bytes per pixel (bpp)
+ */
+struct vimc_pix_map {
+	unsigned int code;
+	unsigned int bpp;
+	u32 pixelformat;
+};
+
+/**
+ * struct vimc_ent_device - core struct that represents a node in the topology
+ *
+ * @ent:		the pointer to struct media_entity for the node
+ * @pads:		the list of pads of the node
+ * @destroy:		callback to destroy the node
+ * @process_frame:	callback send a frame to that node
+ *
+ * Each node of the topology must create a vimc_ent_device struct. Depending on
+ * the node it will be of an instance of v4l2_subdev or video_device struct
+ * where both contains a struct media_entity.
+ * Those structures should embedded the vimc_ent_device struct through
+ * v4l2_set_subdevdata() and video_set_drvdata() respectivaly, allowing the
+ * vimc_ent_device struct to be retrieved from the corresponding struct
+ * media_entity
+ */
+struct vimc_ent_device {
+	struct media_entity *ent;
+	struct media_pad *pads;
+	void (*destroy)(struct vimc_ent_device *);
+	void (*process_frame)(struct vimc_ent_device *ved,
+			      struct media_pad *sink, const void *frame);
+};
+
+/**
+ * vimc_propagate_frame - propagate a frame through the topology
+ *
+ * @src:	the source pad where the frame is being originated
+ * @frame:	the frame to be propagated
+ *
+ * This function will call the process_frame callback from the vimc_ent_device
+ * struct of the nodes directly connected to the @src pad
+ */
+int vimc_propagate_frame(struct media_pad *src, const void *frame);
+
+/**
+ * vimc_pads_init - initialize pads
+ *
+ * @num_pads:	number of pads to initialize
+ * @pads_flags:	flags to use in each pad
+ *
+ * Helper functions to allocate/initialize pads
+ */
+struct media_pad *vimc_pads_init(u16 num_pads,
+				 const unsigned long *pads_flag);
+
+/**
+ * vimc_pads_cleanup - free pads
+ *
+ * @pads: pointer to the pads
+ *
+ * Helper function to free the pads initialized with vimc_pads_init
+ */
+static inline void vimc_pads_cleanup(struct media_pad *pads)
+{
+	kfree(pads);
+}
+
+/**
+ * vimc_pix_map_by_code - get vimc_pix_map struct by media bus code
+ *
+ * @code:		media bus format code defined by MEDIA_BUS_FMT_* macros
+ */
+const struct vimc_pix_map *vimc_pix_map_by_code(u32 code);
+
+/**
+ * vimc_pix_map_by_pixelformat - get vimc_pix_map struct by v4l2 pixel format
+ *
+ * @pixelformat:	pixel format devined by V4L2_PIX_FMT_* macros
+ */
+const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat);
+
+#endif
diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c
new file mode 100644
index 0000000..591f6a4
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-sensor.c
@@ -0,0 +1,276 @@
+/*
+ * vimc-sensor.c Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/freezer.h>
+#include <linux/kthread.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/vmalloc.h>
+#include <media/v4l2-subdev.h>
+
+#include "vimc-sensor.h"
+
+struct vimc_sen_device {
+	struct vimc_ent_device ved;
+	struct v4l2_subdev sd;
+	struct task_struct *kthread_sen;
+	u8 *frame;
+	/* The active format */
+	struct v4l2_mbus_framefmt mbus_format;
+	int frame_size;
+};
+
+static int vimc_sen_enum_mbus_code(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_pad_config *cfg,
+				   struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct vimc_sen_device *vsen =
+				container_of(sd, struct vimc_sen_device, sd);
+
+	/* TODO: Add support for other codes */
+	if (code->index)
+		return -EINVAL;
+
+	code->code = vsen->mbus_format.code;
+
+	return 0;
+}
+
+static int vimc_sen_enum_frame_size(struct v4l2_subdev *sd,
+				    struct v4l2_subdev_pad_config *cfg,
+				    struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct vimc_sen_device *vsen =
+				container_of(sd, struct vimc_sen_device, sd);
+
+	/* TODO: Add support to other formats */
+	if (fse->index)
+		return -EINVAL;
+
+	/* TODO: Add support for other codes */
+	if (fse->code != vsen->mbus_format.code)
+		return -EINVAL;
+
+	fse->min_width = vsen->mbus_format.width;
+	fse->max_width = vsen->mbus_format.width;
+	fse->min_height = vsen->mbus_format.height;
+	fse->max_height = vsen->mbus_format.height;
+
+	return 0;
+}
+
+static int vimc_sen_get_fmt(struct v4l2_subdev *sd,
+			    struct v4l2_subdev_pad_config *cfg,
+			    struct v4l2_subdev_format *format)
+{
+	struct vimc_sen_device *vsen =
+				container_of(sd, struct vimc_sen_device, sd);
+
+	format->format = vsen->mbus_format;
+
+	return 0;
+}
+
+static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = {
+	.enum_mbus_code		= vimc_sen_enum_mbus_code,
+	.enum_frame_size	= vimc_sen_enum_frame_size,
+	.get_fmt		= vimc_sen_get_fmt,
+	/* TODO: Add support to other formats */
+	.set_fmt		= vimc_sen_get_fmt,
+};
+
+/* media operations */
+static const struct media_entity_operations vimc_sen_mops = {
+	.link_validate = v4l2_subdev_link_validate,
+};
+
+static int vimc_thread_sen(void *data)
+{
+	struct vimc_sen_device *vsen = data;
+	unsigned int i;
+
+	set_freezable();
+	set_current_state(TASK_UNINTERRUPTIBLE);
+
+	for (;;) {
+		try_to_freeze();
+		if (kthread_should_stop())
+			break;
+
+		memset(vsen->frame, 100, vsen->frame_size);
+
+		/* Send the frame to all source pads */
+		for (i = 0; i < vsen->sd.entity.num_pads; i++)
+			vimc_propagate_frame(&vsen->sd.entity.pads[i],
+					     vsen->frame);
+
+		/* 60 frames per second */
+		schedule_timeout(HZ/60);
+	}
+
+	return 0;
+}
+
+static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct vimc_sen_device *vsen =
+				container_of(sd, struct vimc_sen_device, sd);
+	int ret;
+
+	if (enable) {
+		const struct vimc_pix_map *vpix;
+
+		if (vsen->kthread_sen)
+			return -EINVAL;
+
+		/* Calculate the frame size */
+		vpix = vimc_pix_map_by_code(vsen->mbus_format.code);
+		vsen->frame_size = vsen->mbus_format.width * vpix->bpp *
+				   vsen->mbus_format.height;
+
+		/*
+		 * Allocate the frame buffer. Use vmalloc to be able to
+		 * allocate a large amount of memory
+		 */
+		vsen->frame = vmalloc(vsen->frame_size);
+		if (!vsen->frame)
+			return -ENOMEM;
+
+		/* Initialize the image generator thread */
+		vsen->kthread_sen = kthread_run(vimc_thread_sen, vsen, "%s-sen",
+						vsen->sd.v4l2_dev->name);
+		if (IS_ERR(vsen->kthread_sen)) {
+			dev_err(vsen->sd.v4l2_dev->dev,
+				"%s: kernel_thread() failed\n",	vsen->sd.name);
+			vfree(vsen->frame);
+			vsen->frame = NULL;
+			return PTR_ERR(vsen->kthread_sen);
+		}
+	} else {
+		if (!vsen->kthread_sen)
+			return -EINVAL;
+
+		/* Stop image generator */
+		ret = kthread_stop(vsen->kthread_sen);
+		vsen->kthread_sen = NULL;
+
+		vfree(vsen->frame);
+		vsen->frame = NULL;
+		return ret;
+	}
+
+	return 0;
+}
+
+struct v4l2_subdev_video_ops vimc_sen_video_ops = {
+	.s_stream = vimc_sen_s_stream,
+};
+
+static const struct v4l2_subdev_ops vimc_sen_ops = {
+	.pad = &vimc_sen_pad_ops,
+	.video = &vimc_sen_video_ops,
+};
+
+static void vimc_sen_destroy(struct vimc_ent_device *ved)
+{
+	struct vimc_sen_device *vsen =
+				container_of(ved, struct vimc_sen_device, ved);
+
+	v4l2_device_unregister_subdev(&vsen->sd);
+	media_entity_cleanup(ved->ent);
+	kfree(vsen);
+}
+
+struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag)
+{
+	struct vimc_sen_device *vsen;
+	unsigned int i;
+	int ret;
+
+	/* NOTE: a sensor node may be created with more then one pad */
+	if (!name || !num_pads || !pads_flag)
+		return ERR_PTR(-EINVAL);
+
+	/* check if all pads are sources */
+	for (i = 0; i < num_pads; i++)
+		if (!(pads_flag[i] & MEDIA_PAD_FL_SOURCE))
+			return ERR_PTR(-EINVAL);
+
+	/* Allocate the vsen struct */
+	vsen = kzalloc(sizeof(*vsen), GFP_KERNEL);
+	if (!vsen)
+		return ERR_PTR(-ENOMEM);
+
+	/* Allocate the pads */
+	vsen->ved.pads = vimc_pads_init(num_pads, pads_flag);
+	if (IS_ERR(vsen->ved.pads)) {
+		ret = PTR_ERR(vsen->ved.pads);
+		goto err_free_vsen;
+	}
+
+	/* Fill the vimc_ent_device struct */
+	vsen->ved.destroy = vimc_sen_destroy;
+	vsen->ved.ent = &vsen->sd.entity;
+
+	/* Initialize the subdev */
+	v4l2_subdev_init(&vsen->sd, &vimc_sen_ops);
+	vsen->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+	vsen->sd.entity.ops = &vimc_sen_mops;
+	vsen->sd.owner = THIS_MODULE;
+	strlcpy(vsen->sd.name, name, sizeof(vsen->sd.name));
+	v4l2_set_subdevdata(&vsen->sd, &vsen->ved);
+
+	/* Expose this subdev to user space */
+	vsen->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	/* Initialize the media entity */
+	ret = media_entity_pads_init(&vsen->sd.entity,
+				     num_pads, vsen->ved.pads);
+	if (ret)
+		goto err_clean_pads;
+
+	/* Set the active frame format (this is hardcoded for now) */
+	vsen->mbus_format.width = 640;
+	vsen->mbus_format.height = 480;
+	vsen->mbus_format.code = MEDIA_BUS_FMT_RGB888_1X24;
+	vsen->mbus_format.field = V4L2_FIELD_NONE;
+	vsen->mbus_format.colorspace = V4L2_COLORSPACE_SRGB;
+	vsen->mbus_format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
+	vsen->mbus_format.xfer_func = V4L2_XFER_FUNC_SRGB;
+
+	/* Register the subdev with the v4l2 and the media framework */
+	ret = v4l2_device_register_subdev(v4l2_dev, &vsen->sd);
+	if (ret) {
+		dev_err(vsen->sd.v4l2_dev->dev,
+			"%s: subdev register failed (err=%d)\n",
+			vsen->sd.name, ret);
+		goto err_clean_m_ent;
+	}
+
+	return &vsen->ved;
+
+err_clean_m_ent:
+	media_entity_cleanup(&vsen->sd.entity);
+err_clean_pads:
+	vimc_pads_cleanup(vsen->ved.pads);
+err_free_vsen:
+	kfree(vsen);
+
+	return ERR_PTR(ret);
+}
diff --git a/drivers/media/platform/vimc/vimc-sensor.h b/drivers/media/platform/vimc/vimc-sensor.h
new file mode 100644
index 0000000..505310e
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-sensor.h
@@ -0,0 +1,28 @@
+/*
+ * vimc-sensor.h Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIMC_SENSOR_H_
+#define _VIMC_SENSOR_H_
+
+#include "vimc-core.h"
+
+struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag);
+
+#endif
-- 
2.7.4

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

* Re: [PATCH v9] [media] vimc: Virtual Media Controller core, capture and sensor
  2017-04-03 22:16                       ` [PATCH v9] " Helen Koike
@ 2017-04-06 14:29                         ` Helen Koike
  2017-04-07  9:54                         ` Hans Verkuil
  1 sibling, 0 replies; 41+ messages in thread
From: Helen Koike @ 2017-04-06 14:29 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-media, jgebben, mchehab, Sakari Ailus, Laurent Pinchart

Hi,

There are two points below that I'll change in v10. I'll wait for your 
comments in the rest of the code so I can send all the requested changes 
in v10

On 2017-04-03 07:16 PM, Helen Koike wrote:
> First version of the Virtual Media Controller.
> Add a simple version of the core of the driver, the capture and
> sensor nodes in the topology, generating a grey image in a hardcoded
> format.
>
> Signed-off-by: Helen Koike <helen.koike@collabora.com>
>
> ---
>
> Patch based in media/master tree, and available here:
> https://github.com/helen-fornazier/opw-staging/tree/vimc/devel/v8
>
> Changes since v8:
> - fix vimc_sen_enum_mbus_code: return EINVAL if code->index > 0
> - remove input ioctls from vimc-capture.c as v4l2 intends to have default
> input ioctls in the core
> - change kernel-docs for vimc_pix_map_by_* so the line fits in 80
> characters
>
> Changes since v7:
> - remove left over union in struct vimc_ent_device
> - remove v4l2_dev pointer from vimc_sen_device structure
> - remove unused dev parameter from vimc_propagate_frame()
> - remove struct device *dev from struct vimc_cap_device and vimc_sen_device
> - in vimc_sen_create: call media_entity_pads_init() after v4l2_subdev_init()
> to avoid double initialization of vsen->sd.entity.name
> - in vimc_sen_destroy: move media_entity_cleanup to be after v4l2_device_unregister_subdev
> - rename video_device back to vdev instead of vd
> - adjust copyright with range 2015-2017
> - add node names in dev_err prints for vimc-capture.c and vimc-sensor.c
> - remove prefix "cap" in dev_dbg as it already uses the name of the node
>
> Changes since v6:
> - add kernel-docs in vimc-core.h
> - reorder list_del call in vimc_cap_return_all_buffers()
> - call media_pipeline_stop in vimc_cap_start_streaming when fail
> - remove DMA comment (left over from the sample driver)
> - remove vb2_set_plane_payload call in vimc_cap_buffer_prepare
> - remove format verification in vimc_cap_link_validate
> - set vimc_pix_map_list as static
> - use MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN in vimc_raw_create()
> - register media device after creating the topology and unregister it before destroying the topology
> - replace devm_kzalloc by kzalloc for allocating struct vimc_device
> - uset TASK_UNINTERRUPTIBLE for vimc_thread_sen
> - do not allow the creation of a sensor with no pads
> - add more verbose description in Kconfig
> - change copyright to 2017
> - arrange includes: number before letters
> - remove v4l2_dev pointer from vimc_cap_device structure
> - coding style adjustments
> - remove entity variable in vimc_cap_pipeline_s_stream
> - declare and assign variables in vimc_cap_link_validate
> - declare vimc_dev_release() and vimc_pdev closer to vimc_init()
> - remove pad check in vimc_sen_enum_{mbus_code,frame_size} that is already performed by v4l2-subdev.c
> - fix multiline comments to start with /*
> - reorder variable declaration in functions
> - remove pad and subdevice type check in vimc_cap_pipeline_s_stream
> - add a note that sensor nodes can can have more then one source pad
> - remove the use of entity->use_count in the core and attach the ved structure in sd or vd, use
>   ent->obj_type to know which structure (sd or vd) to use
> - rename vdev (video_device) to vd to be similar to sd (subdev)
>
> Changes since v5:
> - Fix message "Entity type for entity Sensor A was not initialized!"
>   by initializing the sensor entity.function after the calling
>   v4l2_subded_init
> - populate device_caps in vimc_cap_create instead of in
>   vimc_cap_querycap
> - Fix typo in vimc-core.c s/de/the
>
> Changes since v4:
> - coding style fixes
> - remove BUG_ON
> - change copyright to 2016
> - depens on VIDEO_V4L2_SUBDEV_API instead of select
> - remove assignement of V4L2_CAP_DEVICE_CAPS
> - s/vimc_cap_g_fmt_vid_cap/vimc_cap_fmt_vid_cap
> - fix vimc_cap_queue_setup declaration type
> - remove wrong buffer size check and add it in vimc_cap_buffer_prepare
> - vimc_cap_create: remove unecessary check if v4l2_dev or v4l2_dev->dev is null
> - vimc_cap_create: only allow a single pad
> - vimc_sen_create: only allow source pads, remove unecessary source pads
> checks in vimc_thread_sen
>
> Changes since v3: fix rmmod crash and built-in compile
> - Re-order unregister calls in vimc_device_unregister function (remove
> rmmod issue)
> - Call media_device_unregister_entity in vimc_raw_destroy
> - Add depends on VIDEO_DEV && VIDEO_V4L2 and select VIDEOBUF2_VMALLOC
> - Check *nplanes in queue_setup (this remove v4l2-compliance fail)
> - Include <linux/kthread.h> in vimc-sensor.c
> - Move include of <linux/slab.h> from vimc-core.c to vimc-core.h
> - Generate 60 frames per sec instead of 1 in the sensor
>
> Changes since v2: update with current media master tree
> - Add struct media_pipeline in vimc_cap_device
> - Use vb2_v4l2_buffer instead of vb2_buffer
> - Typos
> - Remove usage of entity->type and use entity->function instead
> - Remove fmt argument from queue setup
> - Use ktime_get_ns instead of v4l2_get_timestamp
> - Iterate over link's list using list_for_each_entry
> - Use media_device_{init, cleanup}
> - Use entity->use_count to keep track of entities instead of the old
> entity->id
> - Replace media_entity_init by media_entity_pads_init
> ---
>  drivers/media/platform/Kconfig             |   2 +
>  drivers/media/platform/Makefile            |   1 +
>  drivers/media/platform/vimc/Kconfig        |  14 +
>  drivers/media/platform/vimc/Makefile       |   3 +
>  drivers/media/platform/vimc/vimc-capture.c | 505 +++++++++++++++++++++
>  drivers/media/platform/vimc/vimc-capture.h |  28 ++
>  drivers/media/platform/vimc/vimc-core.c    | 695 +++++++++++++++++++++++++++++
>  drivers/media/platform/vimc/vimc-core.h    | 112 +++++
>  drivers/media/platform/vimc/vimc-sensor.c  | 276 ++++++++++++
>  drivers/media/platform/vimc/vimc-sensor.h  |  28 ++
>  10 files changed, 1664 insertions(+)
>  create mode 100644 drivers/media/platform/vimc/Kconfig
>  create mode 100644 drivers/media/platform/vimc/Makefile
>  create mode 100644 drivers/media/platform/vimc/vimc-capture.c
>  create mode 100644 drivers/media/platform/vimc/vimc-capture.h
>  create mode 100644 drivers/media/platform/vimc/vimc-core.c
>  create mode 100644 drivers/media/platform/vimc/vimc-core.h
>  create mode 100644 drivers/media/platform/vimc/vimc-sensor.c
>  create mode 100644 drivers/media/platform/vimc/vimc-sensor.h
>
> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> index ab0bb48..b764fba 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -466,6 +466,8 @@ menuconfig V4L_TEST_DRIVERS
>
>  if V4L_TEST_DRIVERS
>
> +source "drivers/media/platform/vimc/Kconfig"
> +
>  source "drivers/media/platform/vivid/Kconfig"
>
>  config VIDEO_VIM2M
> diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> index 8959f6e..4af4cce 100644
> --- a/drivers/media/platform/Makefile
> +++ b/drivers/media/platform/Makefile
> @@ -13,6 +13,7 @@ obj-$(CONFIG_VIDEO_PXA27x)	+= pxa_camera.o
>
>  obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o
>
> +obj-$(CONFIG_VIDEO_VIMC)		+= vimc/
>  obj-$(CONFIG_VIDEO_VIVID)		+= vivid/
>  obj-$(CONFIG_VIDEO_VIM2M)		+= vim2m.o
>
> diff --git a/drivers/media/platform/vimc/Kconfig b/drivers/media/platform/vimc/Kconfig
> new file mode 100644
> index 0000000..dd285fa
> --- /dev/null
> +++ b/drivers/media/platform/vimc/Kconfig
> @@ -0,0 +1,14 @@
> +config VIDEO_VIMC
> +	tristate "Virtual Media Controller Driver (VIMC)"
> +	depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
> +	select VIDEOBUF2_VMALLOC
> +	default n
> +	---help---
> +	  Skeleton driver for Virtual Media Controller
> +
> +	  This drivers can be compared to the Vivid driver for emulating
> +	  a media node that exposes a complex media topology. The topology
> +	  is hard coded for now but is meant to be highly configurable in
> +	  the future.
> +
> +	  When in doubt, say N.
> diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile
> new file mode 100644
> index 0000000..c45195e
> --- /dev/null
> +++ b/drivers/media/platform/vimc/Makefile
> @@ -0,0 +1,3 @@
> +vimc-objs := vimc-core.o vimc-capture.o vimc-sensor.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
> new file mode 100644
> index 0000000..2ecd9c9
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-capture.c
> @@ -0,0 +1,505 @@
> +/*
> + * vimc-capture.c Virtual Media Controller Driver
> + *
> + * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <media/v4l2-ioctl.h>
> +#include <media/videobuf2-core.h>
> +#include <media/videobuf2-vmalloc.h>
> +
> +#include "vimc-capture.h"
> +
> +struct vimc_cap_device {
> +	struct vimc_ent_device ved;
> +	struct video_device vdev;
> +	struct v4l2_pix_format format;
> +	struct vb2_queue queue;
> +	struct list_head buf_list;
> +	/*
> +	 * NOTE: in a real driver, a spin lock must be used to access the
> +	 * queue because the frames are generated from a hardware interruption
> +	 * and the isr is not allowed to sleep.
> +	 * Even if it is not necessary a spinlock in the vimc driver, we
> +	 * use it here as a code reference
> +	 */
> +	spinlock_t qlock;
> +	struct mutex lock;
> +	u32 sequence;
> +	struct media_pipeline pipe;
> +};
> +
> +struct vimc_cap_buffer {
> +	/*
> +	 * struct vb2_v4l2_buffer must be the first element
> +	 * the videobuf2 framework will allocate this struct based on
> +	 * buf_struct_size and use the first sizeof(struct vb2_buffer) bytes of
> +	 * memory as a vb2_buffer
> +	 */
> +	struct vb2_v4l2_buffer vb2;
> +	struct list_head list;
> +};
> +
> +static int vimc_cap_querycap(struct file *file, void *priv,
> +			     struct v4l2_capability *cap)
> +{
> +	struct vimc_cap_device *vcap = video_drvdata(file);
> +
> +	strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
> +	strlcpy(cap->card, KBUILD_MODNAME, sizeof(cap->card));
> +	snprintf(cap->bus_info, sizeof(cap->bus_info),
> +		 "platform:%s", vcap->vdev.v4l2_dev->name);
> +
> +	return 0;
> +}
> +
> +static int vimc_cap_fmt_vid_cap(struct file *file, void *priv,
> +				  struct v4l2_format *f)
> +{
> +	struct vimc_cap_device *vcap = video_drvdata(file);
> +
> +	f->fmt.pix = vcap->format;
> +
> +	return 0;
> +}
> +
> +static int vimc_cap_enum_fmt_vid_cap(struct file *file, void *priv,
> +				     struct v4l2_fmtdesc *f)
> +{
> +	struct vimc_cap_device *vcap = video_drvdata(file);
> +
> +	if (f->index > 0)
> +		return -EINVAL;
> +
> +	/* We only support one format for now */
> +	f->pixelformat = vcap->format.pixelformat;
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_file_operations vimc_cap_fops = {
> +	.owner		= THIS_MODULE,
> +	.open		= v4l2_fh_open,
> +	.release	= vb2_fop_release,
> +	.read           = vb2_fop_read,
> +	.poll		= vb2_fop_poll,
> +	.unlocked_ioctl = video_ioctl2,
> +	.mmap           = vb2_fop_mmap,
> +};
> +
> +static const struct v4l2_ioctl_ops vimc_cap_ioctl_ops = {
> +	.vidioc_querycap = vimc_cap_querycap,
> +
> +	.vidioc_g_fmt_vid_cap = vimc_cap_fmt_vid_cap,
> +	.vidioc_s_fmt_vid_cap = vimc_cap_fmt_vid_cap,
> +	.vidioc_try_fmt_vid_cap = vimc_cap_fmt_vid_cap,
> +	.vidioc_enum_fmt_vid_cap = vimc_cap_enum_fmt_vid_cap,
> +
> +	.vidioc_reqbufs = vb2_ioctl_reqbufs,
> +	.vidioc_create_bufs = vb2_ioctl_create_bufs,
> +	.vidioc_querybuf = vb2_ioctl_querybuf,
> +	.vidioc_qbuf = vb2_ioctl_qbuf,
> +	.vidioc_dqbuf = vb2_ioctl_dqbuf,
> +	.vidioc_expbuf = vb2_ioctl_expbuf,
> +	.vidioc_streamon = vb2_ioctl_streamon,
> +	.vidioc_streamoff = vb2_ioctl_streamoff,
> +};
> +
> +static void vimc_cap_return_all_buffers(struct vimc_cap_device *vcap,
> +					enum vb2_buffer_state state)
> +{
> +	struct vimc_cap_buffer *vbuf, *node;
> +
> +	spin_lock(&vcap->qlock);
> +
> +	list_for_each_entry_safe(vbuf, node, &vcap->buf_list, list) {
> +		list_del(&vbuf->list);
> +		vb2_buffer_done(&vbuf->vb2.vb2_buf, state);
> +	}
> +
> +	spin_unlock(&vcap->qlock);
> +}
> +
> +static int vimc_cap_pipeline_s_stream(struct vimc_cap_device *vcap, int enable)
> +{
> +	struct v4l2_subdev *sd;
> +	struct media_pad *pad;
> +	int ret;
> +
> +	/* Start the stream in the subdevice direct connected */
> +	pad = media_entity_remote_pad(&vcap->vdev.entity.pads[0]);
> +
> +	/*
> +	 * if it is a raw node from vimc-core, there is nothing to activate
> +	 * TODO: remove this when there are no more raw nodes in the
> +	 * core and return error instead
> +	 */
> +	if (pad->entity->obj_type == MEDIA_ENTITY_TYPE_BASE)
> +		return 0;
> +
> +	sd = media_entity_to_v4l2_subdev(pad->entity);
> +	ret = v4l2_subdev_call(sd, video, s_stream, enable);
> +	if (ret && ret != -ENOIOCTLCMD)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count)
> +{
> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
> +	struct media_entity *entity = &vcap->vdev.entity;
> +	int ret;
> +
> +	vcap->sequence = 0;
> +
> +	/* Start the media pipeline */
> +	ret = media_pipeline_start(entity, &vcap->pipe);
> +	if (ret) {
> +		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
> +		return ret;
> +	}
> +
> +	/* Enable streaming from the pipe */
> +	ret = vimc_cap_pipeline_s_stream(vcap, 1);
> +	if (ret) {
> +		media_pipeline_stop(entity);
> +		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * Stop the stream engine. Any remaining buffers in the stream queue are
> + * dequeued and passed on to the vb2 framework marked as STATE_ERROR.
> + */
> +static void vimc_cap_stop_streaming(struct vb2_queue *vq)
> +{
> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
> +
> +	/* Disable streaming from the pipe */
> +	vimc_cap_pipeline_s_stream(vcap, 0);
> +
> +	/* Stop the media pipeline */
> +	media_pipeline_stop(&vcap->vdev.entity);
> +
> +	/* Release all active buffers */
> +	vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_ERROR);
> +}
> +
> +static void vimc_cap_buf_queue(struct vb2_buffer *vb2_buf)
> +{
> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb2_buf->vb2_queue);
> +	struct vimc_cap_buffer *buf = container_of(vb2_buf,
> +						   struct vimc_cap_buffer,
> +						   vb2.vb2_buf);
> +
> +	spin_lock(&vcap->qlock);
> +	list_add_tail(&buf->list, &vcap->buf_list);
> +	spin_unlock(&vcap->qlock);
> +}
> +
> +static int vimc_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
> +				unsigned int *nplanes, unsigned int sizes[],
> +				struct device *alloc_devs[])
> +{
> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
> +
> +	if (*nplanes)
> +		return sizes[0] < vcap->format.sizeimage ? -EINVAL : 0;
> +	/* We don't support multiplanes for now */
> +	*nplanes = 1;
> +	sizes[0] = vcap->format.sizeimage;
> +
> +	return 0;
> +}
> +
> +static int vimc_cap_buffer_prepare(struct vb2_buffer *vb)
> +{
> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb->vb2_queue);
> +	unsigned long size = vcap->format.sizeimage;
> +
> +	if (vb2_plane_size(vb, 0) < size) {
> +		dev_err(vcap->vdev.v4l2_dev->dev,
> +			"%s: buffer too small (%lu < %lu)\n",
> +			vcap->vdev.name, vb2_plane_size(vb, 0), size);
> +		return -EINVAL;
> +	}
> +	return 0;
> +}
> +
> +static const struct vb2_ops vimc_cap_qops = {
> +	.start_streaming	= vimc_cap_start_streaming,
> +	.stop_streaming		= vimc_cap_stop_streaming,
> +	.buf_queue		= vimc_cap_buf_queue,
> +	.queue_setup		= vimc_cap_queue_setup,
> +	.buf_prepare		= vimc_cap_buffer_prepare,
> +	/*
> +	 * Since q->lock is set we can use the standard
> +	 * vb2_ops_wait_prepare/finish helper functions.
> +	 */
> +	.wait_prepare		= vb2_ops_wait_prepare,
> +	.wait_finish		= vb2_ops_wait_finish,
> +};
> +
> +/*
> + * NOTE: this function is a copy of v4l2_subdev_link_validate_get_format
> + * maybe the v4l2 function should be public
> + */
> +static int vimc_cap_v4l2_subdev_link_validate_get_format(struct media_pad *pad,
> +						struct v4l2_subdev_format *fmt)
> +{
> +	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity);
> +
> +	fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +	fmt->pad = pad->index;
> +
> +	return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt);
> +}
> +
> +static int vimc_cap_link_validate(struct media_link *link)
> +{
> +	struct v4l2_subdev_format source_fmt;
> +	const struct vimc_pix_map *vpix;
> +	struct vimc_cap_device *vcap = container_of(link->sink->entity,
> +						    struct vimc_cap_device,
> +						    vdev.entity);
> +	struct v4l2_pix_format *sink_fmt = &vcap->format;
> +	int ret;
> +
> +	/*
> +	 * if it is a raw node from vimc-core, ignore the link for now
> +	 * TODO: remove this when there are no more raw nodes in the
> +	 * core and return error instead
> +	 */
> +	if (link->source->entity->obj_type == MEDIA_ENTITY_TYPE_BASE)
> +		return 0;
> +
> +	/* Get the the format of the subdev */
> +	ret = vimc_cap_v4l2_subdev_link_validate_get_format(link->source,
> +							    &source_fmt);
> +	if (ret)
> +		return ret;
> +
> +	dev_dbg(vcap->vdev.v4l2_dev->dev,
> +		"%s: link validate formats src:%dx%d %d sink:%dx%d %d\n",
> +		vcap->vdev.name,
> +		source_fmt.format.width, source_fmt.format.height,
> +		source_fmt.format.code,
> +		sink_fmt->width, sink_fmt->height,
> +		sink_fmt->pixelformat);
> +
> +	/* The width, height and code must match. */
> +	vpix = vimc_pix_map_by_pixelformat(sink_fmt->pixelformat);
> +	if (source_fmt.format.width != sink_fmt->width
> +	    || source_fmt.format.height != sink_fmt->height
> +	    || vpix->code != source_fmt.format.code)
> +		return -EINVAL;

This should return -EPIPE instead of -EINVAL as in 
v4l2_subdev_link_validate_default

> +
> +	/*
> +	 * The field order must match, or the sink field order must be NONE
> +	 * to support interlaced hardware connected to bridges that support
> +	 * progressive formats only.
> +	 */
> +	if (source_fmt.format.field != sink_fmt->field &&
> +	    sink_fmt->field != V4L2_FIELD_NONE)
> +		return -EINVAL;

Same here (-EPIPE)

> +
> +	return 0;
> +}
> +
> +static const struct media_entity_operations vimc_cap_mops = {
> +	.link_validate		= vimc_cap_link_validate,
> +};
> +
> +static void vimc_cap_destroy(struct vimc_ent_device *ved)
> +{
> +	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
> +						    ved);
> +
> +	vb2_queue_release(&vcap->queue);
> +	media_entity_cleanup(ved->ent);
> +	video_unregister_device(&vcap->vdev);
> +	vimc_pads_cleanup(vcap->ved.pads);
> +	kfree(vcap);
> +}
> +
> +static void vimc_cap_process_frame(struct vimc_ent_device *ved,
> +				   struct media_pad *sink, const void *frame)
> +{
> +	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
> +						    ved);
> +	struct vimc_cap_buffer *vimc_buf;
> +	void *vbuf;
> +
> +	/* If the stream in this node is not active, just return */
> +	mutex_lock(&vcap->lock);
> +	if (!vb2_is_busy(&vcap->queue)) {
> +		mutex_unlock(&vcap->lock);
> +		return;
> +	}
> +	mutex_unlock(&vcap->lock);

Remove this verification of vb2_is_busy as locking vcap->lock can cause 
a deadlock with stop_streaming and not verifying it doesn't harm


> +
> +	spin_lock(&vcap->qlock);
> +
> +	/* Get the first entry of the list */
> +	vimc_buf = list_first_entry_or_null(&vcap->buf_list,
> +					    typeof(*vimc_buf), list);
> +	if (!vimc_buf) {
> +		spin_unlock(&vcap->qlock);
> +		return;
> +	}
> +
> +	/* Remove this entry from the list */
> +	list_del(&vimc_buf->list);
> +
> +	spin_unlock(&vcap->qlock);
> +
> +	/* Fill the buffer */
> +	vimc_buf->vb2.vb2_buf.timestamp = ktime_get_ns();
> +	vimc_buf->vb2.sequence = vcap->sequence++;
> +	vimc_buf->vb2.field = vcap->format.field;
> +
> +	vbuf = vb2_plane_vaddr(&vimc_buf->vb2.vb2_buf, 0);
> +
> +	memcpy(vbuf, frame, vcap->format.sizeimage);
> +
> +	/* Set it as ready */
> +	vb2_set_plane_payload(&vimc_buf->vb2.vb2_buf, 0,
> +			      vcap->format.sizeimage);
> +	vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE);
> +}
> +
> +struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
> +					const char *const name,
> +					u16 num_pads,
> +					const unsigned long *pads_flag)
> +{
> +	const struct vimc_pix_map *vpix;
> +	struct vimc_cap_device *vcap;
> +	struct video_device *vdev;
> +	struct vb2_queue *q;
> +	int ret;
> +
> +	/*
> +	 * Check entity configuration params
> +	 * NOTE: we only support a single sink pad
> +	 */
> +	if (!name || num_pads != 1 || !pads_flag ||
> +	    !(pads_flag[0] & MEDIA_PAD_FL_SINK))
> +		return ERR_PTR(-EINVAL);
> +
> +	/* Allocate the vimc_cap_device struct */
> +	vcap = kzalloc(sizeof(*vcap), GFP_KERNEL);
> +	if (!vcap)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/* Allocate the pads */
> +	vcap->ved.pads = vimc_pads_init(num_pads, pads_flag);
> +	if (IS_ERR(vcap->ved.pads)) {
> +		ret = PTR_ERR(vcap->ved.pads);
> +		goto err_free_vcap;
> +	}
> +
> +	/* Initialize the media entity */
> +	vcap->vdev.entity.name = name;
> +	vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L;
> +	ret = media_entity_pads_init(&vcap->vdev.entity,
> +				     num_pads, vcap->ved.pads);
> +	if (ret)
> +		goto err_clean_pads;
> +
> +	/* Initialize the lock */
> +	mutex_init(&vcap->lock);
> +
> +	/* Initialize the vb2 queue */
> +	q = &vcap->queue;
> +	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	q->io_modes = VB2_MMAP | VB2_DMABUF;
> +	q->drv_priv = vcap;
> +	q->buf_struct_size = sizeof(struct vimc_cap_buffer);
> +	q->ops = &vimc_cap_qops;
> +	q->mem_ops = &vb2_vmalloc_memops;
> +	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> +	q->min_buffers_needed = 2;
> +	q->lock = &vcap->lock;
> +
> +	ret = vb2_queue_init(q);
> +	if (ret) {
> +		dev_err(vcap->vdev.v4l2_dev->dev,
> +			"%s: vb2 queue init failed (err=%d)\n",
> +			vcap->vdev.name, ret);
> +		goto err_clean_m_ent;
> +	}
> +
> +	/* Initialize buffer list and its lock */
> +	INIT_LIST_HEAD(&vcap->buf_list);
> +	spin_lock_init(&vcap->qlock);
> +
> +	/* Set the frame format (this is hardcoded for now) */
> +	vcap->format.width = 640;
> +	vcap->format.height = 480;
> +	vcap->format.pixelformat = V4L2_PIX_FMT_RGB24;
> +	vcap->format.field = V4L2_FIELD_NONE;
> +	vcap->format.colorspace = V4L2_COLORSPACE_SRGB;
> +
> +	vpix = vimc_pix_map_by_pixelformat(vcap->format.pixelformat);
> +
> +	vcap->format.bytesperline = vcap->format.width * vpix->bpp;
> +	vcap->format.sizeimage = vcap->format.bytesperline *
> +				 vcap->format.height;
> +
> +	/* Fill the vimc_ent_device struct */
> +	vcap->ved.destroy = vimc_cap_destroy;
> +	vcap->ved.ent = &vcap->vdev.entity;
> +	vcap->ved.process_frame = vimc_cap_process_frame;
> +
> +	/* Initialize the video_device struct */
> +	vdev = &vcap->vdev;
> +	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
> +	vdev->entity.ops = &vimc_cap_mops;
> +	vdev->release = video_device_release_empty;
> +	vdev->fops = &vimc_cap_fops;
> +	vdev->ioctl_ops = &vimc_cap_ioctl_ops;
> +	vdev->lock = &vcap->lock;
> +	vdev->queue = q;
> +	vdev->v4l2_dev = v4l2_dev;
> +	vdev->vfl_dir = VFL_DIR_RX;
> +	strlcpy(vdev->name, name, sizeof(vdev->name));
> +	video_set_drvdata(vdev, &vcap->ved);
> +
> +	/* Register the video_device with the v4l2 and the media framework */
> +	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
> +	if (ret) {
> +		dev_err(vcap->vdev.v4l2_dev->dev,
> +			"%s: video register failed (err=%d)\n",
> +			vcap->vdev.name, ret);
> +		goto err_release_queue;
> +	}
> +
> +	return &vcap->ved;
> +
> +err_release_queue:
> +	vb2_queue_release(q);
> +err_clean_m_ent:
> +	media_entity_cleanup(&vcap->vdev.entity);
> +err_clean_pads:
> +	vimc_pads_cleanup(vcap->ved.pads);
> +err_free_vcap:
> +	kfree(vcap);
> +
> +	return ERR_PTR(ret);
> +}
> diff --git a/drivers/media/platform/vimc/vimc-capture.h b/drivers/media/platform/vimc/vimc-capture.h
> new file mode 100644
> index 0000000..581a813
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-capture.h
> @@ -0,0 +1,28 @@
> +/*
> + * vimc-capture.h Virtual Media Controller Driver
> + *
> + * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef _VIMC_CAPTURE_H_
> +#define _VIMC_CAPTURE_H_
> +
> +#include "vimc-core.h"
> +
> +struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
> +					const char *const name,
> +					u16 num_pads,
> +					const unsigned long *pads_flag);
> +
> +#endif
> diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c
> new file mode 100644
> index 0000000..bc107da
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-core.c
> @@ -0,0 +1,695 @@
> +/*
> + * vimc-core.c Virtual Media Controller Driver
> + *
> + * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <media/media-device.h>
> +#include <media/v4l2-device.h>
> +
> +#include "vimc-capture.h"
> +#include "vimc-core.h"
> +#include "vimc-sensor.h"
> +
> +#define VIMC_PDEV_NAME "vimc"
> +#define VIMC_MDEV_MODEL_NAME "VIMC MDEV"
> +
> +#define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) {	\
> +	.src_ent = src,						\
> +	.src_pad = srcpad,					\
> +	.sink_ent = sink,					\
> +	.sink_pad = sinkpad,					\
> +	.flags = link_flags,					\
> +}
> +
> +struct vimc_device {
> +	/*
> +	 * The pipeline configuration
> +	 * (filled before calling vimc_device_register)
> +	 */
> +	const struct vimc_pipeline_config *pipe_cfg;
> +
> +	/* The Associated media_device parent */
> +	struct media_device mdev;
> +
> +	/* Internal v4l2 parent device*/
> +	struct v4l2_device v4l2_dev;
> +
> +	/* Internal topology */
> +	struct vimc_ent_device **ved;
> +};
> +
> +/**
> + * enum vimc_ent_node - Select the functionality of a node in the topology
> + * @VIMC_ENT_NODE_SENSOR:	A node of type SENSOR simulates a camera sensor
> + *				generating internal images in bayer format and
> + *				propagating those images through the pipeline
> + * @VIMC_ENT_NODE_CAPTURE:	A node of type CAPTURE is a v4l2 video_device
> + *				that exposes the received image from the
> + *				pipeline to the user space
> + * @VIMC_ENT_NODE_INPUT:	A node of type INPUT is a v4l2 video_device that
> + *				receives images from the user space and
> + *				propagates them through the pipeline
> + * @VIMC_ENT_NODE_DEBAYER:	A node type DEBAYER expects to receive a frame
> + *				in bayer format converts it to RGB
> + * @VIMC_ENT_NODE_SCALER:	A node of type SCALER scales the received image
> + *				by a given multiplier
> + *
> + * This enum is used in the entity configuration struct to allow the definition
> + * of a custom topology specifying the role of each node on it.
> + */
> +enum vimc_ent_node {
> +	VIMC_ENT_NODE_SENSOR,
> +	VIMC_ENT_NODE_CAPTURE,
> +	VIMC_ENT_NODE_INPUT,
> +	VIMC_ENT_NODE_DEBAYER,
> +	VIMC_ENT_NODE_SCALER,
> +};
> +
> +/* Structure which describes individual configuration for each entity */
> +struct vimc_ent_config {
> +	const char *name;
> +	size_t pads_qty;
> +	const unsigned long *pads_flag;
> +	enum vimc_ent_node node;
> +};
> +
> +/* Structure which describes links between entities */
> +struct vimc_ent_link {
> +	unsigned int src_ent;
> +	u16 src_pad;
> +	unsigned int sink_ent;
> +	u16 sink_pad;
> +	u32 flags;
> +};
> +
> +/* Structure which describes the whole topology */
> +struct vimc_pipeline_config {
> +	const struct vimc_ent_config *ents;
> +	size_t num_ents;
> +	const struct vimc_ent_link *links;
> +	size_t num_links;
> +};
> +
> +/* --------------------------------------------------------------------------
> + * Topology Configuration
> + */
> +
> +static const struct vimc_ent_config ent_config[] = {
> +	{
> +		.name = "Sensor A",
> +		.pads_qty = 1,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
> +		.node = VIMC_ENT_NODE_SENSOR,
> +	},
> +	{
> +		.name = "Sensor B",
> +		.pads_qty = 1,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
> +		.node = VIMC_ENT_NODE_SENSOR,
> +	},
> +	{
> +		.name = "Debayer A",
> +		.pads_qty = 2,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
> +						     MEDIA_PAD_FL_SOURCE},
> +		.node = VIMC_ENT_NODE_DEBAYER,
> +	},
> +	{
> +		.name = "Debayer B",
> +		.pads_qty = 2,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
> +						     MEDIA_PAD_FL_SOURCE},
> +		.node = VIMC_ENT_NODE_DEBAYER,
> +	},
> +	{
> +		.name = "Raw Capture 0",
> +		.pads_qty = 1,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
> +		.node = VIMC_ENT_NODE_CAPTURE,
> +	},
> +	{
> +		.name = "Raw Capture 1",
> +		.pads_qty = 1,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
> +		.node = VIMC_ENT_NODE_CAPTURE,
> +	},
> +	{
> +		.name = "RGB/YUV Input",
> +		.pads_qty = 1,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
> +		.node = VIMC_ENT_NODE_INPUT,
> +	},
> +	{
> +		.name = "Scaler",
> +		.pads_qty = 2,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
> +						     MEDIA_PAD_FL_SOURCE},
> +		.node = VIMC_ENT_NODE_SCALER,
> +	},
> +	{
> +		.name = "RGB/YUV Capture",
> +		.pads_qty = 1,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
> +		.node = VIMC_ENT_NODE_CAPTURE,
> +	},
> +};
> +
> +static const struct vimc_ent_link ent_links[] = {
> +	/* Link: Sensor A (Pad 0)->(Pad 0) Debayer A */
> +	VIMC_ENT_LINK(0, 0, 2, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
> +	/* Link: Sensor A (Pad 0)->(Pad 0) Raw Capture 0 */
> +	VIMC_ENT_LINK(0, 0, 4, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
> +	/* Link: Sensor B (Pad 0)->(Pad 0) Debayer B */
> +	VIMC_ENT_LINK(1, 0, 3, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
> +	/* Link: Sensor B (Pad 0)->(Pad 0) Raw Capture 1 */
> +	VIMC_ENT_LINK(1, 0, 5, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
> +	/* Link: Debayer A (Pad 1)->(Pad 0) Scaler */
> +	VIMC_ENT_LINK(2, 1, 7, 0, MEDIA_LNK_FL_ENABLED),
> +	/* Link: Debayer B (Pad 1)->(Pad 0) Scaler */
> +	VIMC_ENT_LINK(3, 1, 7, 0, 0),
> +	/* Link: RGB/YUV Input (Pad 0)->(Pad 0) Scaler */
> +	VIMC_ENT_LINK(6, 0, 7, 0, 0),
> +	/* Link: Scaler (Pad 1)->(Pad 0) RGB/YUV Capture */
> +	VIMC_ENT_LINK(7, 1, 8, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
> +};
> +
> +static const struct vimc_pipeline_config pipe_cfg = {
> +	.ents		= ent_config,
> +	.num_ents	= ARRAY_SIZE(ent_config),
> +	.links		= ent_links,
> +	.num_links	= ARRAY_SIZE(ent_links)
> +};
> +
> +/* -------------------------------------------------------------------------- */
> +
> +static const struct vimc_pix_map vimc_pix_map_list[] = {
> +	/* TODO: add all missing formats */
> +
> +	/* RGB formats */
> +	{
> +		.code = MEDIA_BUS_FMT_BGR888_1X24,
> +		.pixelformat = V4L2_PIX_FMT_BGR24,
> +		.bpp = 3,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_RGB888_1X24,
> +		.pixelformat = V4L2_PIX_FMT_RGB24,
> +		.bpp = 3,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_ARGB8888_1X32,
> +		.pixelformat = V4L2_PIX_FMT_ARGB32,
> +		.bpp = 4,
> +	},
> +
> +	/* Bayer formats */
> +	{
> +		.code = MEDIA_BUS_FMT_SBGGR8_1X8,
> +		.pixelformat = V4L2_PIX_FMT_SBGGR8,
> +		.bpp = 1,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SGBRG8_1X8,
> +		.pixelformat = V4L2_PIX_FMT_SGBRG8,
> +		.bpp = 1,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SGRBG8_1X8,
> +		.pixelformat = V4L2_PIX_FMT_SGRBG8,
> +		.bpp = 1,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SRGGB8_1X8,
> +		.pixelformat = V4L2_PIX_FMT_SRGGB8,
> +		.bpp = 1,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SBGGR10_1X10,
> +		.pixelformat = V4L2_PIX_FMT_SBGGR10,
> +		.bpp = 2,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SGBRG10_1X10,
> +		.pixelformat = V4L2_PIX_FMT_SGBRG10,
> +		.bpp = 2,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SGRBG10_1X10,
> +		.pixelformat = V4L2_PIX_FMT_SGRBG10,
> +		.bpp = 2,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SRGGB10_1X10,
> +		.pixelformat = V4L2_PIX_FMT_SRGGB10,
> +		.bpp = 2,
> +	},
> +
> +	/* 10bit raw bayer a-law compressed to 8 bits */
> +	{
> +		.code = MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8,
> +		.pixelformat = V4L2_PIX_FMT_SBGGR10ALAW8,
> +		.bpp = 1,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8,
> +		.pixelformat = V4L2_PIX_FMT_SGBRG10ALAW8,
> +		.bpp = 1,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8,
> +		.pixelformat = V4L2_PIX_FMT_SGRBG10ALAW8,
> +		.bpp = 1,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8,
> +		.pixelformat = V4L2_PIX_FMT_SRGGB10ALAW8,
> +		.bpp = 1,
> +	},
> +
> +	/* 10bit raw bayer DPCM compressed to 8 bits */
> +	{
> +		.code = MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8,
> +		.pixelformat = V4L2_PIX_FMT_SBGGR10DPCM8,
> +		.bpp = 1,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8,
> +		.pixelformat = V4L2_PIX_FMT_SGBRG10DPCM8,
> +		.bpp = 1,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8,
> +		.pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8,
> +		.bpp = 1,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8,
> +		.pixelformat = V4L2_PIX_FMT_SRGGB10DPCM8,
> +		.bpp = 1,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SBGGR12_1X12,
> +		.pixelformat = V4L2_PIX_FMT_SBGGR12,
> +		.bpp = 2,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SGBRG12_1X12,
> +		.pixelformat = V4L2_PIX_FMT_SGBRG12,
> +		.bpp = 2,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SGRBG12_1X12,
> +		.pixelformat = V4L2_PIX_FMT_SGRBG12,
> +		.bpp = 2,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SRGGB12_1X12,
> +		.pixelformat = V4L2_PIX_FMT_SRGGB12,
> +		.bpp = 2,
> +	},
> +};
> +
> +const struct vimc_pix_map *vimc_pix_map_by_code(u32 code)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(vimc_pix_map_list); i++) {
> +		if (vimc_pix_map_list[i].code == code)
> +			return &vimc_pix_map_list[i];
> +	}
> +	return NULL;
> +}
> +
> +const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(vimc_pix_map_list); i++) {
> +		if (vimc_pix_map_list[i].pixelformat == pixelformat)
> +			return &vimc_pix_map_list[i];
> +	}
> +	return NULL;
> +}
> +
> +int vimc_propagate_frame(struct media_pad *src, const void *frame)
> +{
> +	struct media_link *link;
> +
> +	if (!(src->flags & MEDIA_PAD_FL_SOURCE))
> +		return -EINVAL;
> +
> +	/* Send this frame to all sink pads that are direct linked */
> +	list_for_each_entry(link, &src->entity->links, list) {
> +		if (link->source == src &&
> +		    (link->flags & MEDIA_LNK_FL_ENABLED)) {
> +			struct vimc_ent_device *ved = NULL;
> +			struct media_entity *entity = link->sink->entity;
> +
> +			if (is_media_entity_v4l2_subdev(entity)) {
> +				struct v4l2_subdev *sd =
> +					container_of(entity, struct v4l2_subdev,
> +						     entity);
> +				ved = v4l2_get_subdevdata(sd);
> +			} else if (is_media_entity_v4l2_video_device(entity)) {
> +				struct video_device *vdev =
> +					container_of(entity,
> +						     struct video_device,
> +						     entity);
> +				ved = video_get_drvdata(vdev);
> +			}
> +			if (ved && ved->process_frame)
> +				ved->process_frame(ved, link->sink, frame);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static void vimc_device_unregister(struct vimc_device *vimc)
> +{
> +	unsigned int i;
> +
> +	media_device_unregister(&vimc->mdev);
> +	/* Cleanup (only initialized) entities */
> +	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
> +		if (vimc->ved[i] && vimc->ved[i]->destroy)
> +			vimc->ved[i]->destroy(vimc->ved[i]);
> +
> +		vimc->ved[i] = NULL;
> +	}
> +	v4l2_device_unregister(&vimc->v4l2_dev);
> +	media_device_cleanup(&vimc->mdev);
> +}
> +
> +/* Helper function to allocate and initialize pads */
> +struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag)
> +{
> +	struct media_pad *pads;
> +	unsigned int i;
> +
> +	/* Allocate memory for the pads */
> +	pads = kcalloc(num_pads, sizeof(*pads), GFP_KERNEL);
> +	if (!pads)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/* Initialize the pads */
> +	for (i = 0; i < num_pads; i++) {
> +		pads[i].index = i;
> +		pads[i].flags = pads_flag[i];
> +	}
> +
> +	return pads;
> +}
> +
> +/*
> + * TODO: remove this function when all the
> + * entities specific code are implemented
> + */
> +static void vimc_raw_destroy(struct vimc_ent_device *ved)
> +{
> +	media_device_unregister_entity(ved->ent);
> +
> +	media_entity_cleanup(ved->ent);
> +
> +	vimc_pads_cleanup(ved->pads);
> +
> +	kfree(ved->ent);
> +
> +	kfree(ved);
> +}
> +
> +/*
> + * TODO: remove this function when all the
> + * entities specific code are implemented
> + */
> +static struct vimc_ent_device *vimc_raw_create(struct v4l2_device *v4l2_dev,
> +					       const char *const name,
> +					       u16 num_pads,
> +					       const unsigned long *pads_flag)
> +{
> +	struct vimc_ent_device *ved;
> +	int ret;
> +
> +	/* Allocate the main ved struct */
> +	ved = kzalloc(sizeof(*ved), GFP_KERNEL);
> +	if (!ved)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/* Allocate the media entity */
> +	ved->ent = kzalloc(sizeof(*ved->ent), GFP_KERNEL);
> +	if (!ved->ent) {
> +		ret = -ENOMEM;
> +		goto err_free_ved;
> +	}
> +
> +	/* Allocate the pads */
> +	ved->pads = vimc_pads_init(num_pads, pads_flag);
> +	if (IS_ERR(ved->pads)) {
> +		ret = PTR_ERR(ved->pads);
> +		goto err_free_ent;
> +	}
> +
> +	/* Initialize the media entity */
> +	ved->ent->name = name;
> +	ved->ent->function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
> +	ret = media_entity_pads_init(ved->ent, num_pads, ved->pads);
> +	if (ret)
> +		goto err_cleanup_pads;
> +
> +	/* Register the media entity */
> +	ret = media_device_register_entity(v4l2_dev->mdev, ved->ent);
> +	if (ret)
> +		goto err_cleanup_entity;
> +
> +	/* Fill out the destroy function and return */
> +	ved->destroy = vimc_raw_destroy;
> +	return ved;
> +
> +err_cleanup_entity:
> +	media_entity_cleanup(ved->ent);
> +err_cleanup_pads:
> +	vimc_pads_cleanup(ved->pads);
> +err_free_ent:
> +	kfree(ved->ent);
> +err_free_ved:
> +	kfree(ved);
> +
> +	return ERR_PTR(ret);
> +}
> +
> +static int vimc_device_register(struct vimc_device *vimc)
> +{
> +	unsigned int i;
> +	int ret;
> +
> +	/* Allocate memory for the vimc_ent_devices pointers */
> +	vimc->ved = devm_kcalloc(vimc->mdev.dev, vimc->pipe_cfg->num_ents,
> +				 sizeof(*vimc->ved), GFP_KERNEL);
> +	if (!vimc->ved)
> +		return -ENOMEM;
> +
> +	/* Link the media device within the v4l2_device */
> +	vimc->v4l2_dev.mdev = &vimc->mdev;
> +
> +	/* Register the v4l2 struct */
> +	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);
> +		return ret;
> +	}
> +
> +	/* Initialize entities */
> +	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
> +		struct vimc_ent_device *(*create_func)(struct v4l2_device *,
> +						       const char *const,
> +						       u16,
> +						       const unsigned long *);
> +
> +		/* Register the specific node */
> +		switch (vimc->pipe_cfg->ents[i].node) {
> +		case VIMC_ENT_NODE_SENSOR:
> +			create_func = vimc_sen_create;
> +			break;
> +
> +		case VIMC_ENT_NODE_CAPTURE:
> +			create_func = vimc_cap_create;
> +			break;
> +
> +		/* TODO: Instantiate the specific topology node */
> +		case VIMC_ENT_NODE_INPUT:
> +		case VIMC_ENT_NODE_DEBAYER:
> +		case VIMC_ENT_NODE_SCALER:
> +		default:
> +			/*
> +			 * TODO: remove this when all the entities specific
> +			 * code are implemented
> +			 */
> +			create_func = vimc_raw_create;
> +			break;
> +		}
> +
> +		vimc->ved[i] = create_func(&vimc->v4l2_dev,
> +					   vimc->pipe_cfg->ents[i].name,
> +					   vimc->pipe_cfg->ents[i].pads_qty,
> +					   vimc->pipe_cfg->ents[i].pads_flag);
> +		if (IS_ERR(vimc->ved[i])) {
> +			ret = PTR_ERR(vimc->ved[i]);
> +			vimc->ved[i] = NULL;
> +			goto err;
> +		}
> +	}
> +
> +	/* 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];
> +
> +		ret = media_create_pad_link(vimc->ved[link->src_ent]->ent,
> +					    link->src_pad,
> +					    vimc->ved[link->sink_ent]->ent,
> +					    link->sink_pad,
> +					    link->flags);
> +		if (ret)
> +			goto err;
> +	}
> +
> +	/* Register the media device */
> +	ret = media_device_register(&vimc->mdev);
> +	if (ret) {
> +		dev_err(vimc->mdev.dev,
> +			"media device register failed (err=%d)\n", ret);
> +		return ret;
> +	}
> +
> +	/* 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);
> +		goto err;
> +	}
> +
> +	return 0;
> +
> +err:
> +	/* Destroy the so far created topology */
> +	vimc_device_unregister(vimc);
> +
> +	return ret;
> +}
> +
> +static int vimc_probe(struct platform_device *pdev)
> +{
> +	struct vimc_device *vimc;
> +	int ret;
> +
> +	/* Prepare the vimc topology structure */
> +
> +	/* Allocate memory for the vimc structure */
> +	vimc = kzalloc(sizeof(*vimc), GFP_KERNEL);
> +	if (!vimc)
> +		return -ENOMEM;
> +
> +	/* Set the pipeline configuration struct */
> +	vimc->pipe_cfg = &pipe_cfg;
> +
> +	/* Initialize media device */
> +	strlcpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME,
> +		sizeof(vimc->mdev.model));
> +	vimc->mdev.dev = &pdev->dev;
> +	media_device_init(&vimc->mdev);
> +
> +	/* Create vimc topology */
> +	ret = vimc_device_register(vimc);
> +	if (ret) {
> +		dev_err(vimc->mdev.dev,
> +			"vimc device registration failed (err=%d)\n", ret);
> +		kfree(vimc);
> +		return ret;
> +	}
> +
> +	/* Link the topology object with the platform device object */
> +	platform_set_drvdata(pdev, vimc);
> +
> +	return 0;
> +}
> +
> +static int vimc_remove(struct platform_device *pdev)
> +{
> +	struct vimc_device *vimc = platform_get_drvdata(pdev);
> +
> +	/* Destroy all the topology */
> +	vimc_device_unregister(vimc);
> +	kfree(vimc);
> +
> +	return 0;
> +}
> +
> +static void vimc_dev_release(struct device *dev)
> +{
> +}
> +
> +static struct platform_device vimc_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,
> +	},
> +};
> +
> +static int __init vimc_init(void)
> +{
> +	int ret;
> +
> +	ret = platform_device_register(&vimc_pdev);
> +	if (ret) {
> +		dev_err(&vimc_pdev.dev,
> +			"platform device registration failed (err=%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = platform_driver_register(&vimc_pdrv);
> +	if (ret) {
> +		dev_err(&vimc_pdev.dev,
> +			"platform driver registration failed (err=%d)\n", ret);
> +
> +		platform_device_unregister(&vimc_pdev);
> +	}
> +
> +	return ret;
> +}
> +
> +static void __exit vimc_exit(void)
> +{
> +	platform_driver_unregister(&vimc_pdrv);
> +
> +	platform_device_unregister(&vimc_pdev);
> +}
> +
> +module_init(vimc_init);
> +module_exit(vimc_exit);
> +
> +MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC)");
> +MODULE_AUTHOR("Helen Fornazier <helen.fornazier@gmail.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/media/platform/vimc/vimc-core.h b/drivers/media/platform/vimc/vimc-core.h
> new file mode 100644
> index 0000000..4525d23
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-core.h
> @@ -0,0 +1,112 @@
> +/*
> + * vimc-core.h Virtual Media Controller Driver
> + *
> + * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef _VIMC_CORE_H_
> +#define _VIMC_CORE_H_
> +
> +#include <linux/slab.h>
> +#include <media/v4l2-device.h>
> +
> +/**
> + * struct vimc_pix_map - maps media bus code with v4l2 pixel format
> + *
> + * @code:		media bus format code defined by MEDIA_BUS_FMT_* macros
> + * @bbp:		number of bytes each pixel occupies
> + * @pixelformat:	pixel format devined by V4L2_PIX_FMT_* macros
> + *
> + * Struct which matches the MEDIA_BUS_FMT_* codes with the corresponding
> + * V4L2_PIX_FMT_* fourcc pixelformat and its bytes per pixel (bpp)
> + */
> +struct vimc_pix_map {
> +	unsigned int code;
> +	unsigned int bpp;
> +	u32 pixelformat;
> +};
> +
> +/**
> + * struct vimc_ent_device - core struct that represents a node in the topology
> + *
> + * @ent:		the pointer to struct media_entity for the node
> + * @pads:		the list of pads of the node
> + * @destroy:		callback to destroy the node
> + * @process_frame:	callback send a frame to that node
> + *
> + * Each node of the topology must create a vimc_ent_device struct. Depending on
> + * the node it will be of an instance of v4l2_subdev or video_device struct
> + * where both contains a struct media_entity.
> + * Those structures should embedded the vimc_ent_device struct through
> + * v4l2_set_subdevdata() and video_set_drvdata() respectivaly, allowing the
> + * vimc_ent_device struct to be retrieved from the corresponding struct
> + * media_entity
> + */
> +struct vimc_ent_device {
> +	struct media_entity *ent;
> +	struct media_pad *pads;
> +	void (*destroy)(struct vimc_ent_device *);
> +	void (*process_frame)(struct vimc_ent_device *ved,
> +			      struct media_pad *sink, const void *frame);
> +};
> +
> +/**
> + * vimc_propagate_frame - propagate a frame through the topology
> + *
> + * @src:	the source pad where the frame is being originated
> + * @frame:	the frame to be propagated
> + *
> + * This function will call the process_frame callback from the vimc_ent_device
> + * struct of the nodes directly connected to the @src pad
> + */
> +int vimc_propagate_frame(struct media_pad *src, const void *frame);
> +
> +/**
> + * vimc_pads_init - initialize pads
> + *
> + * @num_pads:	number of pads to initialize
> + * @pads_flags:	flags to use in each pad
> + *
> + * Helper functions to allocate/initialize pads
> + */
> +struct media_pad *vimc_pads_init(u16 num_pads,
> +				 const unsigned long *pads_flag);
> +
> +/**
> + * vimc_pads_cleanup - free pads
> + *
> + * @pads: pointer to the pads
> + *
> + * Helper function to free the pads initialized with vimc_pads_init
> + */
> +static inline void vimc_pads_cleanup(struct media_pad *pads)
> +{
> +	kfree(pads);
> +}
> +
> +/**
> + * vimc_pix_map_by_code - get vimc_pix_map struct by media bus code
> + *
> + * @code:		media bus format code defined by MEDIA_BUS_FMT_* macros
> + */
> +const struct vimc_pix_map *vimc_pix_map_by_code(u32 code);
> +
> +/**
> + * vimc_pix_map_by_pixelformat - get vimc_pix_map struct by v4l2 pixel format
> + *
> + * @pixelformat:	pixel format devined by V4L2_PIX_FMT_* macros
> + */
> +const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat);
> +
> +#endif
> diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c
> new file mode 100644
> index 0000000..591f6a4
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-sensor.c
> @@ -0,0 +1,276 @@
> +/*
> + * vimc-sensor.c Virtual Media Controller Driver
> + *
> + * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/freezer.h>
> +#include <linux/kthread.h>
> +#include <linux/v4l2-mediabus.h>
> +#include <linux/vmalloc.h>
> +#include <media/v4l2-subdev.h>
> +
> +#include "vimc-sensor.h"
> +
> +struct vimc_sen_device {
> +	struct vimc_ent_device ved;
> +	struct v4l2_subdev sd;
> +	struct task_struct *kthread_sen;
> +	u8 *frame;
> +	/* The active format */
> +	struct v4l2_mbus_framefmt mbus_format;
> +	int frame_size;
> +};
> +
> +static int vimc_sen_enum_mbus_code(struct v4l2_subdev *sd,
> +				   struct v4l2_subdev_pad_config *cfg,
> +				   struct v4l2_subdev_mbus_code_enum *code)
> +{
> +	struct vimc_sen_device *vsen =
> +				container_of(sd, struct vimc_sen_device, sd);
> +
> +	/* TODO: Add support for other codes */
> +	if (code->index)
> +		return -EINVAL;
> +
> +	code->code = vsen->mbus_format.code;
> +
> +	return 0;
> +}
> +
> +static int vimc_sen_enum_frame_size(struct v4l2_subdev *sd,
> +				    struct v4l2_subdev_pad_config *cfg,
> +				    struct v4l2_subdev_frame_size_enum *fse)
> +{
> +	struct vimc_sen_device *vsen =
> +				container_of(sd, struct vimc_sen_device, sd);
> +
> +	/* TODO: Add support to other formats */
> +	if (fse->index)
> +		return -EINVAL;
> +
> +	/* TODO: Add support for other codes */
> +	if (fse->code != vsen->mbus_format.code)
> +		return -EINVAL;
> +
> +	fse->min_width = vsen->mbus_format.width;
> +	fse->max_width = vsen->mbus_format.width;
> +	fse->min_height = vsen->mbus_format.height;
> +	fse->max_height = vsen->mbus_format.height;
> +
> +	return 0;
> +}
> +
> +static int vimc_sen_get_fmt(struct v4l2_subdev *sd,
> +			    struct v4l2_subdev_pad_config *cfg,
> +			    struct v4l2_subdev_format *format)
> +{
> +	struct vimc_sen_device *vsen =
> +				container_of(sd, struct vimc_sen_device, sd);
> +
> +	format->format = vsen->mbus_format;
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = {
> +	.enum_mbus_code		= vimc_sen_enum_mbus_code,
> +	.enum_frame_size	= vimc_sen_enum_frame_size,
> +	.get_fmt		= vimc_sen_get_fmt,
> +	/* TODO: Add support to other formats */
> +	.set_fmt		= vimc_sen_get_fmt,
> +};
> +
> +/* media operations */
> +static const struct media_entity_operations vimc_sen_mops = {
> +	.link_validate = v4l2_subdev_link_validate,
> +};
> +
> +static int vimc_thread_sen(void *data)
> +{
> +	struct vimc_sen_device *vsen = data;
> +	unsigned int i;
> +
> +	set_freezable();
> +	set_current_state(TASK_UNINTERRUPTIBLE);
> +
> +	for (;;) {
> +		try_to_freeze();
> +		if (kthread_should_stop())
> +			break;
> +
> +		memset(vsen->frame, 100, vsen->frame_size);
> +
> +		/* Send the frame to all source pads */
> +		for (i = 0; i < vsen->sd.entity.num_pads; i++)
> +			vimc_propagate_frame(&vsen->sd.entity.pads[i],
> +					     vsen->frame);
> +
> +		/* 60 frames per second */
> +		schedule_timeout(HZ/60);
> +	}
> +
> +	return 0;
> +}
> +
> +static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct vimc_sen_device *vsen =
> +				container_of(sd, struct vimc_sen_device, sd);
> +	int ret;
> +
> +	if (enable) {
> +		const struct vimc_pix_map *vpix;
> +
> +		if (vsen->kthread_sen)
> +			return -EINVAL;
> +
> +		/* Calculate the frame size */
> +		vpix = vimc_pix_map_by_code(vsen->mbus_format.code);
> +		vsen->frame_size = vsen->mbus_format.width * vpix->bpp *
> +				   vsen->mbus_format.height;
> +
> +		/*
> +		 * Allocate the frame buffer. Use vmalloc to be able to
> +		 * allocate a large amount of memory
> +		 */
> +		vsen->frame = vmalloc(vsen->frame_size);
> +		if (!vsen->frame)
> +			return -ENOMEM;
> +
> +		/* Initialize the image generator thread */
> +		vsen->kthread_sen = kthread_run(vimc_thread_sen, vsen, "%s-sen",
> +						vsen->sd.v4l2_dev->name);
> +		if (IS_ERR(vsen->kthread_sen)) {
> +			dev_err(vsen->sd.v4l2_dev->dev,
> +				"%s: kernel_thread() failed\n",	vsen->sd.name);
> +			vfree(vsen->frame);
> +			vsen->frame = NULL;
> +			return PTR_ERR(vsen->kthread_sen);
> +		}
> +	} else {
> +		if (!vsen->kthread_sen)
> +			return -EINVAL;
> +
> +		/* Stop image generator */
> +		ret = kthread_stop(vsen->kthread_sen);
> +		vsen->kthread_sen = NULL;
> +
> +		vfree(vsen->frame);
> +		vsen->frame = NULL;
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +struct v4l2_subdev_video_ops vimc_sen_video_ops = {
> +	.s_stream = vimc_sen_s_stream,
> +};
> +
> +static const struct v4l2_subdev_ops vimc_sen_ops = {
> +	.pad = &vimc_sen_pad_ops,
> +	.video = &vimc_sen_video_ops,
> +};
> +
> +static void vimc_sen_destroy(struct vimc_ent_device *ved)
> +{
> +	struct vimc_sen_device *vsen =
> +				container_of(ved, struct vimc_sen_device, ved);
> +
> +	v4l2_device_unregister_subdev(&vsen->sd);
> +	media_entity_cleanup(ved->ent);
> +	kfree(vsen);
> +}
> +
> +struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
> +					const char *const name,
> +					u16 num_pads,
> +					const unsigned long *pads_flag)
> +{
> +	struct vimc_sen_device *vsen;
> +	unsigned int i;
> +	int ret;
> +
> +	/* NOTE: a sensor node may be created with more then one pad */
> +	if (!name || !num_pads || !pads_flag)
> +		return ERR_PTR(-EINVAL);
> +
> +	/* check if all pads are sources */
> +	for (i = 0; i < num_pads; i++)
> +		if (!(pads_flag[i] & MEDIA_PAD_FL_SOURCE))
> +			return ERR_PTR(-EINVAL);
> +
> +	/* Allocate the vsen struct */
> +	vsen = kzalloc(sizeof(*vsen), GFP_KERNEL);
> +	if (!vsen)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/* Allocate the pads */
> +	vsen->ved.pads = vimc_pads_init(num_pads, pads_flag);
> +	if (IS_ERR(vsen->ved.pads)) {
> +		ret = PTR_ERR(vsen->ved.pads);
> +		goto err_free_vsen;
> +	}
> +
> +	/* Fill the vimc_ent_device struct */
> +	vsen->ved.destroy = vimc_sen_destroy;
> +	vsen->ved.ent = &vsen->sd.entity;
> +
> +	/* Initialize the subdev */
> +	v4l2_subdev_init(&vsen->sd, &vimc_sen_ops);
> +	vsen->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
> +	vsen->sd.entity.ops = &vimc_sen_mops;
> +	vsen->sd.owner = THIS_MODULE;
> +	strlcpy(vsen->sd.name, name, sizeof(vsen->sd.name));
> +	v4l2_set_subdevdata(&vsen->sd, &vsen->ved);
> +
> +	/* Expose this subdev to user space */
> +	vsen->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
> +
> +	/* Initialize the media entity */
> +	ret = media_entity_pads_init(&vsen->sd.entity,
> +				     num_pads, vsen->ved.pads);
> +	if (ret)
> +		goto err_clean_pads;
> +
> +	/* Set the active frame format (this is hardcoded for now) */
> +	vsen->mbus_format.width = 640;
> +	vsen->mbus_format.height = 480;
> +	vsen->mbus_format.code = MEDIA_BUS_FMT_RGB888_1X24;
> +	vsen->mbus_format.field = V4L2_FIELD_NONE;
> +	vsen->mbus_format.colorspace = V4L2_COLORSPACE_SRGB;
> +	vsen->mbus_format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
> +	vsen->mbus_format.xfer_func = V4L2_XFER_FUNC_SRGB;
> +
> +	/* Register the subdev with the v4l2 and the media framework */
> +	ret = v4l2_device_register_subdev(v4l2_dev, &vsen->sd);
> +	if (ret) {
> +		dev_err(vsen->sd.v4l2_dev->dev,
> +			"%s: subdev register failed (err=%d)\n",
> +			vsen->sd.name, ret);
> +		goto err_clean_m_ent;
> +	}
> +
> +	return &vsen->ved;
> +
> +err_clean_m_ent:
> +	media_entity_cleanup(&vsen->sd.entity);
> +err_clean_pads:
> +	vimc_pads_cleanup(vsen->ved.pads);
> +err_free_vsen:
> +	kfree(vsen);
> +
> +	return ERR_PTR(ret);
> +}
> diff --git a/drivers/media/platform/vimc/vimc-sensor.h b/drivers/media/platform/vimc/vimc-sensor.h
> new file mode 100644
> index 0000000..505310e
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-sensor.h
> @@ -0,0 +1,28 @@
> +/*
> + * vimc-sensor.h Virtual Media Controller Driver
> + *
> + * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef _VIMC_SENSOR_H_
> +#define _VIMC_SENSOR_H_
> +
> +#include "vimc-core.h"
> +
> +struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
> +					const char *const name,
> +					u16 num_pads,
> +					const unsigned long *pads_flag);
> +
> +#endif
>

Helen

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

* Re: [PATCH v9] [media] vimc: Virtual Media Controller core, capture and sensor
  2017-04-03 22:16                       ` [PATCH v9] " Helen Koike
  2017-04-06 14:29                         ` Helen Koike
@ 2017-04-07  9:54                         ` Hans Verkuil
  2017-04-07 17:55                           ` [PATCH v10] " Helen Koike
  1 sibling, 1 reply; 41+ messages in thread
From: Hans Verkuil @ 2017-04-07  9:54 UTC (permalink / raw)
  To: Helen Koike; +Cc: linux-media, jgebben, mchehab, Sakari Ailus, Laurent Pinchart

Just a few small comments for v10:

On 04/04/2017 12:16 AM, Helen Koike wrote:
> First version of the Virtual Media Controller.
> Add a simple version of the core of the driver, the capture and
> sensor nodes in the topology, generating a grey image in a hardcoded
> format.
> 
> Signed-off-by: Helen Koike <helen.koike@collabora.com>
> 
> ---
> 
> Patch based in media/master tree, and available here:
> https://github.com/helen-fornazier/opw-staging/tree/vimc/devel/v8
> 
> Changes since v8:
> - fix vimc_sen_enum_mbus_code: return EINVAL if code->index > 0
> - remove input ioctls from vimc-capture.c as v4l2 intends to have default
> input ioctls in the core
> - change kernel-docs for vimc_pix_map_by_* so the line fits in 80
> characters
> 
> Changes since v7:
> - remove left over union in struct vimc_ent_device
> - remove v4l2_dev pointer from vimc_sen_device structure
> - remove unused dev parameter from vimc_propagate_frame()
> - remove struct device *dev from struct vimc_cap_device and vimc_sen_device
> - in vimc_sen_create: call media_entity_pads_init() after v4l2_subdev_init()
> to avoid double initialization of vsen->sd.entity.name
> - in vimc_sen_destroy: move media_entity_cleanup to be after v4l2_device_unregister_subdev
> - rename video_device back to vdev instead of vd
> - adjust copyright with range 2015-2017
> - add node names in dev_err prints for vimc-capture.c and vimc-sensor.c
> - remove prefix "cap" in dev_dbg as it already uses the name of the node
> 
> Changes since v6:
> - add kernel-docs in vimc-core.h
> - reorder list_del call in vimc_cap_return_all_buffers()
> - call media_pipeline_stop in vimc_cap_start_streaming when fail
> - remove DMA comment (left over from the sample driver)
> - remove vb2_set_plane_payload call in vimc_cap_buffer_prepare
> - remove format verification in vimc_cap_link_validate
> - set vimc_pix_map_list as static
> - use MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN in vimc_raw_create()
> - register media device after creating the topology and unregister it before destroying the topology
> - replace devm_kzalloc by kzalloc for allocating struct vimc_device
> - uset TASK_UNINTERRUPTIBLE for vimc_thread_sen
> - do not allow the creation of a sensor with no pads
> - add more verbose description in Kconfig
> - change copyright to 2017
> - arrange includes: number before letters
> - remove v4l2_dev pointer from vimc_cap_device structure
> - coding style adjustments
> - remove entity variable in vimc_cap_pipeline_s_stream
> - declare and assign variables in vimc_cap_link_validate
> - declare vimc_dev_release() and vimc_pdev closer to vimc_init()
> - remove pad check in vimc_sen_enum_{mbus_code,frame_size} that is already performed by v4l2-subdev.c
> - fix multiline comments to start with /*
> - reorder variable declaration in functions
> - remove pad and subdevice type check in vimc_cap_pipeline_s_stream
> - add a note that sensor nodes can can have more then one source pad
> - remove the use of entity->use_count in the core and attach the ved structure in sd or vd, use
>   ent->obj_type to know which structure (sd or vd) to use
> - rename vdev (video_device) to vd to be similar to sd (subdev)
> 
> Changes since v5:
> - Fix message "Entity type for entity Sensor A was not initialized!"
>   by initializing the sensor entity.function after the calling
>   v4l2_subded_init
> - populate device_caps in vimc_cap_create instead of in
>   vimc_cap_querycap
> - Fix typo in vimc-core.c s/de/the
> 
> Changes since v4:
> - coding style fixes
> - remove BUG_ON
> - change copyright to 2016
> - depens on VIDEO_V4L2_SUBDEV_API instead of select
> - remove assignement of V4L2_CAP_DEVICE_CAPS
> - s/vimc_cap_g_fmt_vid_cap/vimc_cap_fmt_vid_cap
> - fix vimc_cap_queue_setup declaration type
> - remove wrong buffer size check and add it in vimc_cap_buffer_prepare
> - vimc_cap_create: remove unecessary check if v4l2_dev or v4l2_dev->dev is null
> - vimc_cap_create: only allow a single pad
> - vimc_sen_create: only allow source pads, remove unecessary source pads
> checks in vimc_thread_sen
> 
> Changes since v3: fix rmmod crash and built-in compile
> - Re-order unregister calls in vimc_device_unregister function (remove
> rmmod issue)
> - Call media_device_unregister_entity in vimc_raw_destroy
> - Add depends on VIDEO_DEV && VIDEO_V4L2 and select VIDEOBUF2_VMALLOC
> - Check *nplanes in queue_setup (this remove v4l2-compliance fail)
> - Include <linux/kthread.h> in vimc-sensor.c
> - Move include of <linux/slab.h> from vimc-core.c to vimc-core.h
> - Generate 60 frames per sec instead of 1 in the sensor
> 
> Changes since v2: update with current media master tree
> - Add struct media_pipeline in vimc_cap_device
> - Use vb2_v4l2_buffer instead of vb2_buffer
> - Typos
> - Remove usage of entity->type and use entity->function instead
> - Remove fmt argument from queue setup
> - Use ktime_get_ns instead of v4l2_get_timestamp
> - Iterate over link's list using list_for_each_entry
> - Use media_device_{init, cleanup}
> - Use entity->use_count to keep track of entities instead of the old
> entity->id
> - Replace media_entity_init by media_entity_pads_init
> ---
>  drivers/media/platform/Kconfig             |   2 +
>  drivers/media/platform/Makefile            |   1 +
>  drivers/media/platform/vimc/Kconfig        |  14 +
>  drivers/media/platform/vimc/Makefile       |   3 +
>  drivers/media/platform/vimc/vimc-capture.c | 505 +++++++++++++++++++++
>  drivers/media/platform/vimc/vimc-capture.h |  28 ++
>  drivers/media/platform/vimc/vimc-core.c    | 695 +++++++++++++++++++++++++++++
>  drivers/media/platform/vimc/vimc-core.h    | 112 +++++
>  drivers/media/platform/vimc/vimc-sensor.c  | 276 ++++++++++++
>  drivers/media/platform/vimc/vimc-sensor.h  |  28 ++
>  10 files changed, 1664 insertions(+)
>  create mode 100644 drivers/media/platform/vimc/Kconfig
>  create mode 100644 drivers/media/platform/vimc/Makefile
>  create mode 100644 drivers/media/platform/vimc/vimc-capture.c
>  create mode 100644 drivers/media/platform/vimc/vimc-capture.h
>  create mode 100644 drivers/media/platform/vimc/vimc-core.c
>  create mode 100644 drivers/media/platform/vimc/vimc-core.h
>  create mode 100644 drivers/media/platform/vimc/vimc-sensor.c
>  create mode 100644 drivers/media/platform/vimc/vimc-sensor.h

Please add the patch updating MAINTAINERS. You posted it separately a long
time ago, but it is better to just add it to this patch.

> 
> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> index ab0bb48..b764fba 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -466,6 +466,8 @@ menuconfig V4L_TEST_DRIVERS
>  
>  if V4L_TEST_DRIVERS
>  
> +source "drivers/media/platform/vimc/Kconfig"
> +
>  source "drivers/media/platform/vivid/Kconfig"
>  
>  config VIDEO_VIM2M
> diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> index 8959f6e..4af4cce 100644
> --- a/drivers/media/platform/Makefile
> +++ b/drivers/media/platform/Makefile
> @@ -13,6 +13,7 @@ obj-$(CONFIG_VIDEO_PXA27x)	+= pxa_camera.o
>  
>  obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o
>  
> +obj-$(CONFIG_VIDEO_VIMC)		+= vimc/
>  obj-$(CONFIG_VIDEO_VIVID)		+= vivid/
>  obj-$(CONFIG_VIDEO_VIM2M)		+= vim2m.o
>  
> diff --git a/drivers/media/platform/vimc/Kconfig b/drivers/media/platform/vimc/Kconfig
> new file mode 100644
> index 0000000..dd285fa
> --- /dev/null
> +++ b/drivers/media/platform/vimc/Kconfig
> @@ -0,0 +1,14 @@
> +config VIDEO_VIMC
> +	tristate "Virtual Media Controller Driver (VIMC)"
> +	depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
> +	select VIDEOBUF2_VMALLOC
> +	default n
> +	---help---
> +	  Skeleton driver for Virtual Media Controller
> +
> +	  This drivers can be compared to the Vivid driver for emulating
> +	  a media node that exposes a complex media topology. The topology
> +	  is hard coded for now but is meant to be highly configurable in
> +	  the future.
> +
> +	  When in doubt, say N.
> diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile
> new file mode 100644
> index 0000000..c45195e
> --- /dev/null
> +++ b/drivers/media/platform/vimc/Makefile
> @@ -0,0 +1,3 @@
> +vimc-objs := vimc-core.o vimc-capture.o vimc-sensor.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
> new file mode 100644
> index 0000000..2ecd9c9
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-capture.c
> @@ -0,0 +1,505 @@
> +/*
> + * vimc-capture.c Virtual Media Controller Driver
> + *
> + * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <media/v4l2-ioctl.h>
> +#include <media/videobuf2-core.h>
> +#include <media/videobuf2-vmalloc.h>
> +
> +#include "vimc-capture.h"
> +
> +struct vimc_cap_device {
> +	struct vimc_ent_device ved;
> +	struct video_device vdev;
> +	struct v4l2_pix_format format;
> +	struct vb2_queue queue;
> +	struct list_head buf_list;
> +	/*
> +	 * NOTE: in a real driver, a spin lock must be used to access the
> +	 * queue because the frames are generated from a hardware interruption
> +	 * and the isr is not allowed to sleep.
> +	 * Even if it is not necessary a spinlock in the vimc driver, we
> +	 * use it here as a code reference
> +	 */
> +	spinlock_t qlock;
> +	struct mutex lock;
> +	u32 sequence;
> +	struct media_pipeline pipe;
> +};
> +
> +struct vimc_cap_buffer {
> +	/*
> +	 * struct vb2_v4l2_buffer must be the first element
> +	 * the videobuf2 framework will allocate this struct based on
> +	 * buf_struct_size and use the first sizeof(struct vb2_buffer) bytes of
> +	 * memory as a vb2_buffer
> +	 */
> +	struct vb2_v4l2_buffer vb2;
> +	struct list_head list;
> +};
> +
> +static int vimc_cap_querycap(struct file *file, void *priv,
> +			     struct v4l2_capability *cap)
> +{
> +	struct vimc_cap_device *vcap = video_drvdata(file);
> +
> +	strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
> +	strlcpy(cap->card, KBUILD_MODNAME, sizeof(cap->card));
> +	snprintf(cap->bus_info, sizeof(cap->bus_info),
> +		 "platform:%s", vcap->vdev.v4l2_dev->name);
> +
> +	return 0;
> +}
> +
> +static int vimc_cap_fmt_vid_cap(struct file *file, void *priv,
> +				  struct v4l2_format *f)
> +{
> +	struct vimc_cap_device *vcap = video_drvdata(file);
> +
> +	f->fmt.pix = vcap->format;
> +
> +	return 0;
> +}
> +
> +static int vimc_cap_enum_fmt_vid_cap(struct file *file, void *priv,
> +				     struct v4l2_fmtdesc *f)
> +{
> +	struct vimc_cap_device *vcap = video_drvdata(file);
> +
> +	if (f->index > 0)
> +		return -EINVAL;
> +
> +	/* We only support one format for now */
> +	f->pixelformat = vcap->format.pixelformat;
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_file_operations vimc_cap_fops = {
> +	.owner		= THIS_MODULE,
> +	.open		= v4l2_fh_open,
> +	.release	= vb2_fop_release,
> +	.read           = vb2_fop_read,
> +	.poll		= vb2_fop_poll,
> +	.unlocked_ioctl = video_ioctl2,
> +	.mmap           = vb2_fop_mmap,
> +};
> +
> +static const struct v4l2_ioctl_ops vimc_cap_ioctl_ops = {
> +	.vidioc_querycap = vimc_cap_querycap,
> +
> +	.vidioc_g_fmt_vid_cap = vimc_cap_fmt_vid_cap,
> +	.vidioc_s_fmt_vid_cap = vimc_cap_fmt_vid_cap,
> +	.vidioc_try_fmt_vid_cap = vimc_cap_fmt_vid_cap,
> +	.vidioc_enum_fmt_vid_cap = vimc_cap_enum_fmt_vid_cap,
> +
> +	.vidioc_reqbufs = vb2_ioctl_reqbufs,
> +	.vidioc_create_bufs = vb2_ioctl_create_bufs,
> +	.vidioc_querybuf = vb2_ioctl_querybuf,
> +	.vidioc_qbuf = vb2_ioctl_qbuf,

Can you add vidioc_prepare_buf as well?

It's a bit odd, I would expect v4l2-compliance to warn about that.

> +	.vidioc_dqbuf = vb2_ioctl_dqbuf,
> +	.vidioc_expbuf = vb2_ioctl_expbuf,
> +	.vidioc_streamon = vb2_ioctl_streamon,
> +	.vidioc_streamoff = vb2_ioctl_streamoff,
> +};
> +
> +static void vimc_cap_return_all_buffers(struct vimc_cap_device *vcap,
> +					enum vb2_buffer_state state)
> +{
> +	struct vimc_cap_buffer *vbuf, *node;
> +
> +	spin_lock(&vcap->qlock);
> +
> +	list_for_each_entry_safe(vbuf, node, &vcap->buf_list, list) {
> +		list_del(&vbuf->list);
> +		vb2_buffer_done(&vbuf->vb2.vb2_buf, state);
> +	}
> +
> +	spin_unlock(&vcap->qlock);
> +}
> +
> +static int vimc_cap_pipeline_s_stream(struct vimc_cap_device *vcap, int enable)
> +{
> +	struct v4l2_subdev *sd;
> +	struct media_pad *pad;
> +	int ret;
> +
> +	/* Start the stream in the subdevice direct connected */
> +	pad = media_entity_remote_pad(&vcap->vdev.entity.pads[0]);
> +
> +	/*
> +	 * if it is a raw node from vimc-core, there is nothing to activate
> +	 * TODO: remove this when there are no more raw nodes in the
> +	 * core and return error instead
> +	 */
> +	if (pad->entity->obj_type == MEDIA_ENTITY_TYPE_BASE)
> +		return 0;
> +
> +	sd = media_entity_to_v4l2_subdev(pad->entity);
> +	ret = v4l2_subdev_call(sd, video, s_stream, enable);
> +	if (ret && ret != -ENOIOCTLCMD)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count)
> +{
> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
> +	struct media_entity *entity = &vcap->vdev.entity;
> +	int ret;
> +
> +	vcap->sequence = 0;
> +
> +	/* Start the media pipeline */
> +	ret = media_pipeline_start(entity, &vcap->pipe);
> +	if (ret) {
> +		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
> +		return ret;
> +	}
> +
> +	/* Enable streaming from the pipe */
> +	ret = vimc_cap_pipeline_s_stream(vcap, 1);
> +	if (ret) {
> +		media_pipeline_stop(entity);
> +		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * Stop the stream engine. Any remaining buffers in the stream queue are
> + * dequeued and passed on to the vb2 framework marked as STATE_ERROR.
> + */
> +static void vimc_cap_stop_streaming(struct vb2_queue *vq)
> +{
> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
> +
> +	/* Disable streaming from the pipe */
> +	vimc_cap_pipeline_s_stream(vcap, 0);
> +
> +	/* Stop the media pipeline */
> +	media_pipeline_stop(&vcap->vdev.entity);
> +
> +	/* Release all active buffers */
> +	vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_ERROR);
> +}
> +
> +static void vimc_cap_buf_queue(struct vb2_buffer *vb2_buf)
> +{
> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb2_buf->vb2_queue);
> +	struct vimc_cap_buffer *buf = container_of(vb2_buf,
> +						   struct vimc_cap_buffer,
> +						   vb2.vb2_buf);
> +
> +	spin_lock(&vcap->qlock);
> +	list_add_tail(&buf->list, &vcap->buf_list);
> +	spin_unlock(&vcap->qlock);
> +}
> +
> +static int vimc_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
> +				unsigned int *nplanes, unsigned int sizes[],
> +				struct device *alloc_devs[])
> +{
> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
> +
> +	if (*nplanes)
> +		return sizes[0] < vcap->format.sizeimage ? -EINVAL : 0;
> +	/* We don't support multiplanes for now */
> +	*nplanes = 1;
> +	sizes[0] = vcap->format.sizeimage;
> +
> +	return 0;
> +}
> +
> +static int vimc_cap_buffer_prepare(struct vb2_buffer *vb)
> +{
> +	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb->vb2_queue);
> +	unsigned long size = vcap->format.sizeimage;
> +
> +	if (vb2_plane_size(vb, 0) < size) {
> +		dev_err(vcap->vdev.v4l2_dev->dev,
> +			"%s: buffer too small (%lu < %lu)\n",
> +			vcap->vdev.name, vb2_plane_size(vb, 0), size);
> +		return -EINVAL;
> +	}
> +	return 0;
> +}
> +
> +static const struct vb2_ops vimc_cap_qops = {
> +	.start_streaming	= vimc_cap_start_streaming,
> +	.stop_streaming		= vimc_cap_stop_streaming,
> +	.buf_queue		= vimc_cap_buf_queue,
> +	.queue_setup		= vimc_cap_queue_setup,
> +	.buf_prepare		= vimc_cap_buffer_prepare,
> +	/*
> +	 * Since q->lock is set we can use the standard
> +	 * vb2_ops_wait_prepare/finish helper functions.
> +	 */
> +	.wait_prepare		= vb2_ops_wait_prepare,
> +	.wait_finish		= vb2_ops_wait_finish,
> +};
> +
> +/*
> + * NOTE: this function is a copy of v4l2_subdev_link_validate_get_format
> + * maybe the v4l2 function should be public
> + */
> +static int vimc_cap_v4l2_subdev_link_validate_get_format(struct media_pad *pad,
> +						struct v4l2_subdev_format *fmt)
> +{
> +	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity);
> +
> +	fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +	fmt->pad = pad->index;
> +
> +	return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt);
> +}
> +
> +static int vimc_cap_link_validate(struct media_link *link)
> +{
> +	struct v4l2_subdev_format source_fmt;
> +	const struct vimc_pix_map *vpix;
> +	struct vimc_cap_device *vcap = container_of(link->sink->entity,
> +						    struct vimc_cap_device,
> +						    vdev.entity);
> +	struct v4l2_pix_format *sink_fmt = &vcap->format;
> +	int ret;
> +
> +	/*
> +	 * if it is a raw node from vimc-core, ignore the link for now
> +	 * TODO: remove this when there are no more raw nodes in the
> +	 * core and return error instead
> +	 */
> +	if (link->source->entity->obj_type == MEDIA_ENTITY_TYPE_BASE)
> +		return 0;
> +
> +	/* Get the the format of the subdev */
> +	ret = vimc_cap_v4l2_subdev_link_validate_get_format(link->source,
> +							    &source_fmt);
> +	if (ret)
> +		return ret;
> +
> +	dev_dbg(vcap->vdev.v4l2_dev->dev,
> +		"%s: link validate formats src:%dx%d %d sink:%dx%d %d\n",
> +		vcap->vdev.name,
> +		source_fmt.format.width, source_fmt.format.height,
> +		source_fmt.format.code,
> +		sink_fmt->width, sink_fmt->height,
> +		sink_fmt->pixelformat);
> +
> +	/* The width, height and code must match. */
> +	vpix = vimc_pix_map_by_pixelformat(sink_fmt->pixelformat);
> +	if (source_fmt.format.width != sink_fmt->width
> +	    || source_fmt.format.height != sink_fmt->height
> +	    || vpix->code != source_fmt.format.code)
> +		return -EINVAL;
> +
> +	/*
> +	 * The field order must match, or the sink field order must be NONE
> +	 * to support interlaced hardware connected to bridges that support
> +	 * progressive formats only.
> +	 */
> +	if (source_fmt.format.field != sink_fmt->field &&
> +	    sink_fmt->field != V4L2_FIELD_NONE)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static const struct media_entity_operations vimc_cap_mops = {
> +	.link_validate		= vimc_cap_link_validate,
> +};
> +
> +static void vimc_cap_destroy(struct vimc_ent_device *ved)
> +{
> +	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
> +						    ved);
> +
> +	vb2_queue_release(&vcap->queue);
> +	media_entity_cleanup(ved->ent);
> +	video_unregister_device(&vcap->vdev);
> +	vimc_pads_cleanup(vcap->ved.pads);
> +	kfree(vcap);
> +}
> +
> +static void vimc_cap_process_frame(struct vimc_ent_device *ved,
> +				   struct media_pad *sink, const void *frame)
> +{
> +	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
> +						    ved);
> +	struct vimc_cap_buffer *vimc_buf;
> +	void *vbuf;
> +
> +	/* If the stream in this node is not active, just return */
> +	mutex_lock(&vcap->lock);
> +	if (!vb2_is_busy(&vcap->queue)) {
> +		mutex_unlock(&vcap->lock);
> +		return;
> +	}
> +	mutex_unlock(&vcap->lock);
> +
> +	spin_lock(&vcap->qlock);
> +
> +	/* Get the first entry of the list */
> +	vimc_buf = list_first_entry_or_null(&vcap->buf_list,
> +					    typeof(*vimc_buf), list);
> +	if (!vimc_buf) {
> +		spin_unlock(&vcap->qlock);
> +		return;
> +	}
> +
> +	/* Remove this entry from the list */
> +	list_del(&vimc_buf->list);
> +
> +	spin_unlock(&vcap->qlock);
> +
> +	/* Fill the buffer */
> +	vimc_buf->vb2.vb2_buf.timestamp = ktime_get_ns();
> +	vimc_buf->vb2.sequence = vcap->sequence++;
> +	vimc_buf->vb2.field = vcap->format.field;
> +
> +	vbuf = vb2_plane_vaddr(&vimc_buf->vb2.vb2_buf, 0);
> +
> +	memcpy(vbuf, frame, vcap->format.sizeimage);
> +
> +	/* Set it as ready */
> +	vb2_set_plane_payload(&vimc_buf->vb2.vb2_buf, 0,
> +			      vcap->format.sizeimage);
> +	vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE);
> +}
> +
> +struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
> +					const char *const name,
> +					u16 num_pads,
> +					const unsigned long *pads_flag)
> +{
> +	const struct vimc_pix_map *vpix;
> +	struct vimc_cap_device *vcap;
> +	struct video_device *vdev;
> +	struct vb2_queue *q;
> +	int ret;
> +
> +	/*
> +	 * Check entity configuration params
> +	 * NOTE: we only support a single sink pad
> +	 */
> +	if (!name || num_pads != 1 || !pads_flag ||
> +	    !(pads_flag[0] & MEDIA_PAD_FL_SINK))
> +		return ERR_PTR(-EINVAL);
> +
> +	/* Allocate the vimc_cap_device struct */
> +	vcap = kzalloc(sizeof(*vcap), GFP_KERNEL);
> +	if (!vcap)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/* Allocate the pads */
> +	vcap->ved.pads = vimc_pads_init(num_pads, pads_flag);
> +	if (IS_ERR(vcap->ved.pads)) {
> +		ret = PTR_ERR(vcap->ved.pads);
> +		goto err_free_vcap;
> +	}
> +
> +	/* Initialize the media entity */
> +	vcap->vdev.entity.name = name;
> +	vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L;
> +	ret = media_entity_pads_init(&vcap->vdev.entity,
> +				     num_pads, vcap->ved.pads);
> +	if (ret)
> +		goto err_clean_pads;
> +
> +	/* Initialize the lock */
> +	mutex_init(&vcap->lock);
> +
> +	/* Initialize the vb2 queue */
> +	q = &vcap->queue;
> +	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	q->io_modes = VB2_MMAP | VB2_DMABUF;
> +	q->drv_priv = vcap;
> +	q->buf_struct_size = sizeof(struct vimc_cap_buffer);
> +	q->ops = &vimc_cap_qops;
> +	q->mem_ops = &vb2_vmalloc_memops;
> +	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> +	q->min_buffers_needed = 2;
> +	q->lock = &vcap->lock;
> +
> +	ret = vb2_queue_init(q);
> +	if (ret) {
> +		dev_err(vcap->vdev.v4l2_dev->dev,
> +			"%s: vb2 queue init failed (err=%d)\n",
> +			vcap->vdev.name, ret);
> +		goto err_clean_m_ent;
> +	}
> +
> +	/* Initialize buffer list and its lock */
> +	INIT_LIST_HEAD(&vcap->buf_list);
> +	spin_lock_init(&vcap->qlock);
> +
> +	/* Set the frame format (this is hardcoded for now) */
> +	vcap->format.width = 640;
> +	vcap->format.height = 480;
> +	vcap->format.pixelformat = V4L2_PIX_FMT_RGB24;
> +	vcap->format.field = V4L2_FIELD_NONE;
> +	vcap->format.colorspace = V4L2_COLORSPACE_SRGB;
> +
> +	vpix = vimc_pix_map_by_pixelformat(vcap->format.pixelformat);
> +
> +	vcap->format.bytesperline = vcap->format.width * vpix->bpp;
> +	vcap->format.sizeimage = vcap->format.bytesperline *
> +				 vcap->format.height;
> +
> +	/* Fill the vimc_ent_device struct */
> +	vcap->ved.destroy = vimc_cap_destroy;
> +	vcap->ved.ent = &vcap->vdev.entity;
> +	vcap->ved.process_frame = vimc_cap_process_frame;
> +
> +	/* Initialize the video_device struct */
> +	vdev = &vcap->vdev;
> +	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
> +	vdev->entity.ops = &vimc_cap_mops;
> +	vdev->release = video_device_release_empty;
> +	vdev->fops = &vimc_cap_fops;
> +	vdev->ioctl_ops = &vimc_cap_ioctl_ops;
> +	vdev->lock = &vcap->lock;
> +	vdev->queue = q;
> +	vdev->v4l2_dev = v4l2_dev;
> +	vdev->vfl_dir = VFL_DIR_RX;
> +	strlcpy(vdev->name, name, sizeof(vdev->name));
> +	video_set_drvdata(vdev, &vcap->ved);
> +
> +	/* Register the video_device with the v4l2 and the media framework */
> +	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
> +	if (ret) {
> +		dev_err(vcap->vdev.v4l2_dev->dev,
> +			"%s: video register failed (err=%d)\n",
> +			vcap->vdev.name, ret);
> +		goto err_release_queue;
> +	}
> +
> +	return &vcap->ved;
> +
> +err_release_queue:
> +	vb2_queue_release(q);
> +err_clean_m_ent:
> +	media_entity_cleanup(&vcap->vdev.entity);
> +err_clean_pads:
> +	vimc_pads_cleanup(vcap->ved.pads);
> +err_free_vcap:
> +	kfree(vcap);
> +
> +	return ERR_PTR(ret);
> +}
> diff --git a/drivers/media/platform/vimc/vimc-capture.h b/drivers/media/platform/vimc/vimc-capture.h
> new file mode 100644
> index 0000000..581a813
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-capture.h
> @@ -0,0 +1,28 @@
> +/*
> + * vimc-capture.h Virtual Media Controller Driver
> + *
> + * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef _VIMC_CAPTURE_H_
> +#define _VIMC_CAPTURE_H_
> +
> +#include "vimc-core.h"
> +
> +struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
> +					const char *const name,
> +					u16 num_pads,
> +					const unsigned long *pads_flag);
> +
> +#endif
> diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c
> new file mode 100644
> index 0000000..bc107da
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-core.c
> @@ -0,0 +1,695 @@
> +/*
> + * vimc-core.c Virtual Media Controller Driver
> + *
> + * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <media/media-device.h>
> +#include <media/v4l2-device.h>
> +
> +#include "vimc-capture.h"
> +#include "vimc-core.h"
> +#include "vimc-sensor.h"
> +
> +#define VIMC_PDEV_NAME "vimc"
> +#define VIMC_MDEV_MODEL_NAME "VIMC MDEV"
> +
> +#define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) {	\
> +	.src_ent = src,						\
> +	.src_pad = srcpad,					\
> +	.sink_ent = sink,					\
> +	.sink_pad = sinkpad,					\
> +	.flags = link_flags,					\
> +}
> +
> +struct vimc_device {
> +	/*
> +	 * The pipeline configuration
> +	 * (filled before calling vimc_device_register)
> +	 */
> +	const struct vimc_pipeline_config *pipe_cfg;
> +
> +	/* The Associated media_device parent */
> +	struct media_device mdev;
> +
> +	/* Internal v4l2 parent device*/
> +	struct v4l2_device v4l2_dev;
> +
> +	/* Internal topology */
> +	struct vimc_ent_device **ved;
> +};
> +
> +/**
> + * enum vimc_ent_node - Select the functionality of a node in the topology
> + * @VIMC_ENT_NODE_SENSOR:	A node of type SENSOR simulates a camera sensor
> + *				generating internal images in bayer format and
> + *				propagating those images through the pipeline
> + * @VIMC_ENT_NODE_CAPTURE:	A node of type CAPTURE is a v4l2 video_device
> + *				that exposes the received image from the
> + *				pipeline to the user space
> + * @VIMC_ENT_NODE_INPUT:	A node of type INPUT is a v4l2 video_device that
> + *				receives images from the user space and
> + *				propagates them through the pipeline
> + * @VIMC_ENT_NODE_DEBAYER:	A node type DEBAYER expects to receive a frame
> + *				in bayer format converts it to RGB
> + * @VIMC_ENT_NODE_SCALER:	A node of type SCALER scales the received image
> + *				by a given multiplier
> + *
> + * This enum is used in the entity configuration struct to allow the definition
> + * of a custom topology specifying the role of each node on it.
> + */
> +enum vimc_ent_node {
> +	VIMC_ENT_NODE_SENSOR,
> +	VIMC_ENT_NODE_CAPTURE,
> +	VIMC_ENT_NODE_INPUT,
> +	VIMC_ENT_NODE_DEBAYER,
> +	VIMC_ENT_NODE_SCALER,
> +};
> +
> +/* Structure which describes individual configuration for each entity */
> +struct vimc_ent_config {
> +	const char *name;
> +	size_t pads_qty;
> +	const unsigned long *pads_flag;
> +	enum vimc_ent_node node;
> +};
> +
> +/* Structure which describes links between entities */
> +struct vimc_ent_link {
> +	unsigned int src_ent;
> +	u16 src_pad;
> +	unsigned int sink_ent;
> +	u16 sink_pad;
> +	u32 flags;
> +};
> +
> +/* Structure which describes the whole topology */
> +struct vimc_pipeline_config {
> +	const struct vimc_ent_config *ents;
> +	size_t num_ents;
> +	const struct vimc_ent_link *links;
> +	size_t num_links;
> +};
> +
> +/* --------------------------------------------------------------------------
> + * Topology Configuration
> + */
> +
> +static const struct vimc_ent_config ent_config[] = {
> +	{
> +		.name = "Sensor A",
> +		.pads_qty = 1,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
> +		.node = VIMC_ENT_NODE_SENSOR,
> +	},
> +	{
> +		.name = "Sensor B",
> +		.pads_qty = 1,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
> +		.node = VIMC_ENT_NODE_SENSOR,
> +	},
> +	{
> +		.name = "Debayer A",
> +		.pads_qty = 2,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
> +						     MEDIA_PAD_FL_SOURCE},
> +		.node = VIMC_ENT_NODE_DEBAYER,
> +	},
> +	{
> +		.name = "Debayer B",
> +		.pads_qty = 2,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
> +						     MEDIA_PAD_FL_SOURCE},
> +		.node = VIMC_ENT_NODE_DEBAYER,
> +	},
> +	{
> +		.name = "Raw Capture 0",
> +		.pads_qty = 1,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
> +		.node = VIMC_ENT_NODE_CAPTURE,
> +	},
> +	{
> +		.name = "Raw Capture 1",
> +		.pads_qty = 1,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
> +		.node = VIMC_ENT_NODE_CAPTURE,
> +	},
> +	{
> +		.name = "RGB/YUV Input",
> +		.pads_qty = 1,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
> +		.node = VIMC_ENT_NODE_INPUT,
> +	},
> +	{
> +		.name = "Scaler",
> +		.pads_qty = 2,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
> +						     MEDIA_PAD_FL_SOURCE},
> +		.node = VIMC_ENT_NODE_SCALER,
> +	},
> +	{
> +		.name = "RGB/YUV Capture",
> +		.pads_qty = 1,
> +		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
> +		.node = VIMC_ENT_NODE_CAPTURE,
> +	},
> +};
> +
> +static const struct vimc_ent_link ent_links[] = {
> +	/* Link: Sensor A (Pad 0)->(Pad 0) Debayer A */
> +	VIMC_ENT_LINK(0, 0, 2, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
> +	/* Link: Sensor A (Pad 0)->(Pad 0) Raw Capture 0 */
> +	VIMC_ENT_LINK(0, 0, 4, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
> +	/* Link: Sensor B (Pad 0)->(Pad 0) Debayer B */
> +	VIMC_ENT_LINK(1, 0, 3, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
> +	/* Link: Sensor B (Pad 0)->(Pad 0) Raw Capture 1 */
> +	VIMC_ENT_LINK(1, 0, 5, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
> +	/* Link: Debayer A (Pad 1)->(Pad 0) Scaler */
> +	VIMC_ENT_LINK(2, 1, 7, 0, MEDIA_LNK_FL_ENABLED),
> +	/* Link: Debayer B (Pad 1)->(Pad 0) Scaler */
> +	VIMC_ENT_LINK(3, 1, 7, 0, 0),
> +	/* Link: RGB/YUV Input (Pad 0)->(Pad 0) Scaler */
> +	VIMC_ENT_LINK(6, 0, 7, 0, 0),
> +	/* Link: Scaler (Pad 1)->(Pad 0) RGB/YUV Capture */
> +	VIMC_ENT_LINK(7, 1, 8, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
> +};
> +
> +static const struct vimc_pipeline_config pipe_cfg = {
> +	.ents		= ent_config,
> +	.num_ents	= ARRAY_SIZE(ent_config),
> +	.links		= ent_links,
> +	.num_links	= ARRAY_SIZE(ent_links)
> +};
> +
> +/* -------------------------------------------------------------------------- */
> +
> +static const struct vimc_pix_map vimc_pix_map_list[] = {
> +	/* TODO: add all missing formats */
> +
> +	/* RGB formats */
> +	{
> +		.code = MEDIA_BUS_FMT_BGR888_1X24,
> +		.pixelformat = V4L2_PIX_FMT_BGR24,
> +		.bpp = 3,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_RGB888_1X24,
> +		.pixelformat = V4L2_PIX_FMT_RGB24,
> +		.bpp = 3,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_ARGB8888_1X32,
> +		.pixelformat = V4L2_PIX_FMT_ARGB32,
> +		.bpp = 4,
> +	},
> +
> +	/* Bayer formats */
> +	{
> +		.code = MEDIA_BUS_FMT_SBGGR8_1X8,
> +		.pixelformat = V4L2_PIX_FMT_SBGGR8,
> +		.bpp = 1,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SGBRG8_1X8,
> +		.pixelformat = V4L2_PIX_FMT_SGBRG8,
> +		.bpp = 1,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SGRBG8_1X8,
> +		.pixelformat = V4L2_PIX_FMT_SGRBG8,
> +		.bpp = 1,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SRGGB8_1X8,
> +		.pixelformat = V4L2_PIX_FMT_SRGGB8,
> +		.bpp = 1,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SBGGR10_1X10,
> +		.pixelformat = V4L2_PIX_FMT_SBGGR10,
> +		.bpp = 2,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SGBRG10_1X10,
> +		.pixelformat = V4L2_PIX_FMT_SGBRG10,
> +		.bpp = 2,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SGRBG10_1X10,
> +		.pixelformat = V4L2_PIX_FMT_SGRBG10,
> +		.bpp = 2,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SRGGB10_1X10,
> +		.pixelformat = V4L2_PIX_FMT_SRGGB10,
> +		.bpp = 2,
> +	},
> +
> +	/* 10bit raw bayer a-law compressed to 8 bits */
> +	{
> +		.code = MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8,
> +		.pixelformat = V4L2_PIX_FMT_SBGGR10ALAW8,
> +		.bpp = 1,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8,
> +		.pixelformat = V4L2_PIX_FMT_SGBRG10ALAW8,
> +		.bpp = 1,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8,
> +		.pixelformat = V4L2_PIX_FMT_SGRBG10ALAW8,
> +		.bpp = 1,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8,
> +		.pixelformat = V4L2_PIX_FMT_SRGGB10ALAW8,
> +		.bpp = 1,
> +	},
> +
> +	/* 10bit raw bayer DPCM compressed to 8 bits */
> +	{
> +		.code = MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8,
> +		.pixelformat = V4L2_PIX_FMT_SBGGR10DPCM8,
> +		.bpp = 1,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8,
> +		.pixelformat = V4L2_PIX_FMT_SGBRG10DPCM8,
> +		.bpp = 1,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8,
> +		.pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8,
> +		.bpp = 1,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8,
> +		.pixelformat = V4L2_PIX_FMT_SRGGB10DPCM8,
> +		.bpp = 1,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SBGGR12_1X12,
> +		.pixelformat = V4L2_PIX_FMT_SBGGR12,
> +		.bpp = 2,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SGBRG12_1X12,
> +		.pixelformat = V4L2_PIX_FMT_SGBRG12,
> +		.bpp = 2,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SGRBG12_1X12,
> +		.pixelformat = V4L2_PIX_FMT_SGRBG12,
> +		.bpp = 2,
> +	},
> +	{
> +		.code = MEDIA_BUS_FMT_SRGGB12_1X12,
> +		.pixelformat = V4L2_PIX_FMT_SRGGB12,
> +		.bpp = 2,
> +	},
> +};
> +
> +const struct vimc_pix_map *vimc_pix_map_by_code(u32 code)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(vimc_pix_map_list); i++) {
> +		if (vimc_pix_map_list[i].code == code)
> +			return &vimc_pix_map_list[i];
> +	}
> +	return NULL;
> +}
> +
> +const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(vimc_pix_map_list); i++) {
> +		if (vimc_pix_map_list[i].pixelformat == pixelformat)
> +			return &vimc_pix_map_list[i];
> +	}
> +	return NULL;
> +}
> +
> +int vimc_propagate_frame(struct media_pad *src, const void *frame)
> +{
> +	struct media_link *link;
> +
> +	if (!(src->flags & MEDIA_PAD_FL_SOURCE))
> +		return -EINVAL;
> +
> +	/* Send this frame to all sink pads that are direct linked */
> +	list_for_each_entry(link, &src->entity->links, list) {
> +		if (link->source == src &&
> +		    (link->flags & MEDIA_LNK_FL_ENABLED)) {
> +			struct vimc_ent_device *ved = NULL;
> +			struct media_entity *entity = link->sink->entity;
> +
> +			if (is_media_entity_v4l2_subdev(entity)) {
> +				struct v4l2_subdev *sd =
> +					container_of(entity, struct v4l2_subdev,
> +						     entity);
> +				ved = v4l2_get_subdevdata(sd);
> +			} else if (is_media_entity_v4l2_video_device(entity)) {
> +				struct video_device *vdev =
> +					container_of(entity,
> +						     struct video_device,
> +						     entity);
> +				ved = video_get_drvdata(vdev);
> +			}
> +			if (ved && ved->process_frame)
> +				ved->process_frame(ved, link->sink, frame);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static void vimc_device_unregister(struct vimc_device *vimc)
> +{
> +	unsigned int i;
> +
> +	media_device_unregister(&vimc->mdev);
> +	/* Cleanup (only initialized) entities */
> +	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
> +		if (vimc->ved[i] && vimc->ved[i]->destroy)
> +			vimc->ved[i]->destroy(vimc->ved[i]);
> +
> +		vimc->ved[i] = NULL;
> +	}
> +	v4l2_device_unregister(&vimc->v4l2_dev);
> +	media_device_cleanup(&vimc->mdev);
> +}
> +
> +/* Helper function to allocate and initialize pads */
> +struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag)
> +{
> +	struct media_pad *pads;
> +	unsigned int i;
> +
> +	/* Allocate memory for the pads */
> +	pads = kcalloc(num_pads, sizeof(*pads), GFP_KERNEL);
> +	if (!pads)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/* Initialize the pads */
> +	for (i = 0; i < num_pads; i++) {
> +		pads[i].index = i;
> +		pads[i].flags = pads_flag[i];
> +	}
> +
> +	return pads;
> +}
> +
> +/*
> + * TODO: remove this function when all the
> + * entities specific code are implemented
> + */
> +static void vimc_raw_destroy(struct vimc_ent_device *ved)
> +{
> +	media_device_unregister_entity(ved->ent);
> +
> +	media_entity_cleanup(ved->ent);
> +
> +	vimc_pads_cleanup(ved->pads);
> +
> +	kfree(ved->ent);
> +
> +	kfree(ved);
> +}
> +
> +/*
> + * TODO: remove this function when all the
> + * entities specific code are implemented
> + */
> +static struct vimc_ent_device *vimc_raw_create(struct v4l2_device *v4l2_dev,
> +					       const char *const name,
> +					       u16 num_pads,
> +					       const unsigned long *pads_flag)
> +{
> +	struct vimc_ent_device *ved;
> +	int ret;
> +
> +	/* Allocate the main ved struct */
> +	ved = kzalloc(sizeof(*ved), GFP_KERNEL);
> +	if (!ved)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/* Allocate the media entity */
> +	ved->ent = kzalloc(sizeof(*ved->ent), GFP_KERNEL);
> +	if (!ved->ent) {
> +		ret = -ENOMEM;
> +		goto err_free_ved;
> +	}
> +
> +	/* Allocate the pads */
> +	ved->pads = vimc_pads_init(num_pads, pads_flag);
> +	if (IS_ERR(ved->pads)) {
> +		ret = PTR_ERR(ved->pads);
> +		goto err_free_ent;
> +	}
> +
> +	/* Initialize the media entity */
> +	ved->ent->name = name;
> +	ved->ent->function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
> +	ret = media_entity_pads_init(ved->ent, num_pads, ved->pads);
> +	if (ret)
> +		goto err_cleanup_pads;
> +
> +	/* Register the media entity */
> +	ret = media_device_register_entity(v4l2_dev->mdev, ved->ent);
> +	if (ret)
> +		goto err_cleanup_entity;
> +
> +	/* Fill out the destroy function and return */
> +	ved->destroy = vimc_raw_destroy;
> +	return ved;
> +
> +err_cleanup_entity:
> +	media_entity_cleanup(ved->ent);
> +err_cleanup_pads:
> +	vimc_pads_cleanup(ved->pads);
> +err_free_ent:
> +	kfree(ved->ent);
> +err_free_ved:
> +	kfree(ved);
> +
> +	return ERR_PTR(ret);
> +}
> +
> +static int vimc_device_register(struct vimc_device *vimc)
> +{
> +	unsigned int i;
> +	int ret;
> +
> +	/* Allocate memory for the vimc_ent_devices pointers */
> +	vimc->ved = devm_kcalloc(vimc->mdev.dev, vimc->pipe_cfg->num_ents,
> +				 sizeof(*vimc->ved), GFP_KERNEL);
> +	if (!vimc->ved)
> +		return -ENOMEM;
> +
> +	/* Link the media device within the v4l2_device */
> +	vimc->v4l2_dev.mdev = &vimc->mdev;
> +
> +	/* Register the v4l2 struct */
> +	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);
> +		return ret;
> +	}
> +
> +	/* Initialize entities */
> +	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
> +		struct vimc_ent_device *(*create_func)(struct v4l2_device *,
> +						       const char *const,
> +						       u16,
> +						       const unsigned long *);
> +
> +		/* Register the specific node */
> +		switch (vimc->pipe_cfg->ents[i].node) {
> +		case VIMC_ENT_NODE_SENSOR:
> +			create_func = vimc_sen_create;
> +			break;
> +
> +		case VIMC_ENT_NODE_CAPTURE:
> +			create_func = vimc_cap_create;
> +			break;
> +
> +		/* TODO: Instantiate the specific topology node */
> +		case VIMC_ENT_NODE_INPUT:
> +		case VIMC_ENT_NODE_DEBAYER:
> +		case VIMC_ENT_NODE_SCALER:
> +		default:
> +			/*
> +			 * TODO: remove this when all the entities specific
> +			 * code are implemented
> +			 */
> +			create_func = vimc_raw_create;
> +			break;
> +		}
> +
> +		vimc->ved[i] = create_func(&vimc->v4l2_dev,
> +					   vimc->pipe_cfg->ents[i].name,
> +					   vimc->pipe_cfg->ents[i].pads_qty,
> +					   vimc->pipe_cfg->ents[i].pads_flag);
> +		if (IS_ERR(vimc->ved[i])) {
> +			ret = PTR_ERR(vimc->ved[i]);
> +			vimc->ved[i] = NULL;
> +			goto err;
> +		}
> +	}
> +
> +	/* 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];
> +
> +		ret = media_create_pad_link(vimc->ved[link->src_ent]->ent,
> +					    link->src_pad,
> +					    vimc->ved[link->sink_ent]->ent,
> +					    link->sink_pad,
> +					    link->flags);
> +		if (ret)
> +			goto err;
> +	}
> +
> +	/* Register the media device */
> +	ret = media_device_register(&vimc->mdev);
> +	if (ret) {
> +		dev_err(vimc->mdev.dev,
> +			"media device register failed (err=%d)\n", ret);
> +		return ret;
> +	}
> +
> +	/* 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);
> +		goto err;
> +	}
> +
> +	return 0;
> +
> +err:
> +	/* Destroy the so far created topology */
> +	vimc_device_unregister(vimc);
> +
> +	return ret;
> +}
> +
> +static int vimc_probe(struct platform_device *pdev)
> +{
> +	struct vimc_device *vimc;
> +	int ret;
> +
> +	/* Prepare the vimc topology structure */
> +
> +	/* Allocate memory for the vimc structure */
> +	vimc = kzalloc(sizeof(*vimc), GFP_KERNEL);
> +	if (!vimc)
> +		return -ENOMEM;
> +
> +	/* Set the pipeline configuration struct */
> +	vimc->pipe_cfg = &pipe_cfg;
> +
> +	/* Initialize media device */
> +	strlcpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME,
> +		sizeof(vimc->mdev.model));
> +	vimc->mdev.dev = &pdev->dev;
> +	media_device_init(&vimc->mdev);
> +
> +	/* Create vimc topology */
> +	ret = vimc_device_register(vimc);
> +	if (ret) {
> +		dev_err(vimc->mdev.dev,
> +			"vimc device registration failed (err=%d)\n", ret);
> +		kfree(vimc);
> +		return ret;
> +	}
> +
> +	/* Link the topology object with the platform device object */
> +	platform_set_drvdata(pdev, vimc);
> +
> +	return 0;
> +}
> +
> +static int vimc_remove(struct platform_device *pdev)
> +{
> +	struct vimc_device *vimc = platform_get_drvdata(pdev);
> +
> +	/* Destroy all the topology */
> +	vimc_device_unregister(vimc);
> +	kfree(vimc);
> +
> +	return 0;
> +}
> +
> +static void vimc_dev_release(struct device *dev)
> +{
> +}
> +
> +static struct platform_device vimc_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,
> +	},
> +};
> +
> +static int __init vimc_init(void)
> +{
> +	int ret;
> +
> +	ret = platform_device_register(&vimc_pdev);
> +	if (ret) {
> +		dev_err(&vimc_pdev.dev,
> +			"platform device registration failed (err=%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = platform_driver_register(&vimc_pdrv);
> +	if (ret) {
> +		dev_err(&vimc_pdev.dev,
> +			"platform driver registration failed (err=%d)\n", ret);
> +
> +		platform_device_unregister(&vimc_pdev);
> +	}
> +
> +	return ret;
> +}
> +
> +static void __exit vimc_exit(void)
> +{
> +	platform_driver_unregister(&vimc_pdrv);
> +
> +	platform_device_unregister(&vimc_pdev);
> +}
> +
> +module_init(vimc_init);
> +module_exit(vimc_exit);
> +
> +MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC)");
> +MODULE_AUTHOR("Helen Fornazier <helen.fornazier@gmail.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/media/platform/vimc/vimc-core.h b/drivers/media/platform/vimc/vimc-core.h
> new file mode 100644
> index 0000000..4525d23
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-core.h
> @@ -0,0 +1,112 @@
> +/*
> + * vimc-core.h Virtual Media Controller Driver
> + *
> + * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef _VIMC_CORE_H_
> +#define _VIMC_CORE_H_
> +
> +#include <linux/slab.h>
> +#include <media/v4l2-device.h>
> +
> +/**
> + * struct vimc_pix_map - maps media bus code with v4l2 pixel format
> + *
> + * @code:		media bus format code defined by MEDIA_BUS_FMT_* macros
> + * @bbp:		number of bytes each pixel occupies
> + * @pixelformat:	pixel format devined by V4L2_PIX_FMT_* macros
> + *
> + * Struct which matches the MEDIA_BUS_FMT_* codes with the corresponding
> + * V4L2_PIX_FMT_* fourcc pixelformat and its bytes per pixel (bpp)
> + */
> +struct vimc_pix_map {
> +	unsigned int code;
> +	unsigned int bpp;
> +	u32 pixelformat;
> +};
> +
> +/**
> + * struct vimc_ent_device - core struct that represents a node in the topology
> + *
> + * @ent:		the pointer to struct media_entity for the node
> + * @pads:		the list of pads of the node
> + * @destroy:		callback to destroy the node
> + * @process_frame:	callback send a frame to that node
> + *
> + * Each node of the topology must create a vimc_ent_device struct. Depending on
> + * the node it will be of an instance of v4l2_subdev or video_device struct
> + * where both contains a struct media_entity.
> + * Those structures should embedded the vimc_ent_device struct through
> + * v4l2_set_subdevdata() and video_set_drvdata() respectivaly, allowing the
> + * vimc_ent_device struct to be retrieved from the corresponding struct
> + * media_entity
> + */
> +struct vimc_ent_device {
> +	struct media_entity *ent;
> +	struct media_pad *pads;
> +	void (*destroy)(struct vimc_ent_device *);
> +	void (*process_frame)(struct vimc_ent_device *ved,
> +			      struct media_pad *sink, const void *frame);
> +};
> +
> +/**
> + * vimc_propagate_frame - propagate a frame through the topology
> + *
> + * @src:	the source pad where the frame is being originated
> + * @frame:	the frame to be propagated
> + *
> + * This function will call the process_frame callback from the vimc_ent_device
> + * struct of the nodes directly connected to the @src pad
> + */
> +int vimc_propagate_frame(struct media_pad *src, const void *frame);
> +
> +/**
> + * vimc_pads_init - initialize pads
> + *
> + * @num_pads:	number of pads to initialize
> + * @pads_flags:	flags to use in each pad
> + *
> + * Helper functions to allocate/initialize pads
> + */
> +struct media_pad *vimc_pads_init(u16 num_pads,
> +				 const unsigned long *pads_flag);
> +
> +/**
> + * vimc_pads_cleanup - free pads
> + *
> + * @pads: pointer to the pads
> + *
> + * Helper function to free the pads initialized with vimc_pads_init
> + */
> +static inline void vimc_pads_cleanup(struct media_pad *pads)
> +{
> +	kfree(pads);
> +}
> +
> +/**
> + * vimc_pix_map_by_code - get vimc_pix_map struct by media bus code
> + *
> + * @code:		media bus format code defined by MEDIA_BUS_FMT_* macros
> + */
> +const struct vimc_pix_map *vimc_pix_map_by_code(u32 code);
> +
> +/**
> + * vimc_pix_map_by_pixelformat - get vimc_pix_map struct by v4l2 pixel format
> + *
> + * @pixelformat:	pixel format devined by V4L2_PIX_FMT_* macros
> + */
> +const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat);
> +
> +#endif
> diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c
> new file mode 100644
> index 0000000..591f6a4
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-sensor.c
> @@ -0,0 +1,276 @@
> +/*
> + * vimc-sensor.c Virtual Media Controller Driver
> + *
> + * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/freezer.h>
> +#include <linux/kthread.h>
> +#include <linux/v4l2-mediabus.h>
> +#include <linux/vmalloc.h>
> +#include <media/v4l2-subdev.h>
> +
> +#include "vimc-sensor.h"
> +
> +struct vimc_sen_device {
> +	struct vimc_ent_device ved;
> +	struct v4l2_subdev sd;
> +	struct task_struct *kthread_sen;
> +	u8 *frame;
> +	/* The active format */
> +	struct v4l2_mbus_framefmt mbus_format;
> +	int frame_size;
> +};
> +
> +static int vimc_sen_enum_mbus_code(struct v4l2_subdev *sd,
> +				   struct v4l2_subdev_pad_config *cfg,
> +				   struct v4l2_subdev_mbus_code_enum *code)
> +{
> +	struct vimc_sen_device *vsen =
> +				container_of(sd, struct vimc_sen_device, sd);
> +
> +	/* TODO: Add support for other codes */
> +	if (code->index)
> +		return -EINVAL;
> +
> +	code->code = vsen->mbus_format.code;
> +
> +	return 0;
> +}
> +
> +static int vimc_sen_enum_frame_size(struct v4l2_subdev *sd,
> +				    struct v4l2_subdev_pad_config *cfg,
> +				    struct v4l2_subdev_frame_size_enum *fse)
> +{
> +	struct vimc_sen_device *vsen =
> +				container_of(sd, struct vimc_sen_device, sd);
> +
> +	/* TODO: Add support to other formats */
> +	if (fse->index)
> +		return -EINVAL;
> +
> +	/* TODO: Add support for other codes */
> +	if (fse->code != vsen->mbus_format.code)
> +		return -EINVAL;
> +
> +	fse->min_width = vsen->mbus_format.width;
> +	fse->max_width = vsen->mbus_format.width;
> +	fse->min_height = vsen->mbus_format.height;
> +	fse->max_height = vsen->mbus_format.height;
> +
> +	return 0;
> +}
> +
> +static int vimc_sen_get_fmt(struct v4l2_subdev *sd,
> +			    struct v4l2_subdev_pad_config *cfg,
> +			    struct v4l2_subdev_format *format)
> +{
> +	struct vimc_sen_device *vsen =
> +				container_of(sd, struct vimc_sen_device, sd);
> +
> +	format->format = vsen->mbus_format;
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = {
> +	.enum_mbus_code		= vimc_sen_enum_mbus_code,
> +	.enum_frame_size	= vimc_sen_enum_frame_size,
> +	.get_fmt		= vimc_sen_get_fmt,
> +	/* TODO: Add support to other formats */
> +	.set_fmt		= vimc_sen_get_fmt,
> +};
> +
> +/* media operations */
> +static const struct media_entity_operations vimc_sen_mops = {
> +	.link_validate = v4l2_subdev_link_validate,
> +};
> +
> +static int vimc_thread_sen(void *data)
> +{
> +	struct vimc_sen_device *vsen = data;
> +	unsigned int i;
> +
> +	set_freezable();
> +	set_current_state(TASK_UNINTERRUPTIBLE);
> +
> +	for (;;) {
> +		try_to_freeze();
> +		if (kthread_should_stop())
> +			break;
> +
> +		memset(vsen->frame, 100, vsen->frame_size);
> +
> +		/* Send the frame to all source pads */
> +		for (i = 0; i < vsen->sd.entity.num_pads; i++)
> +			vimc_propagate_frame(&vsen->sd.entity.pads[i],
> +					     vsen->frame);
> +
> +		/* 60 frames per second */
> +		schedule_timeout(HZ/60);
> +	}
> +
> +	return 0;
> +}
> +
> +static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct vimc_sen_device *vsen =
> +				container_of(sd, struct vimc_sen_device, sd);
> +	int ret;
> +
> +	if (enable) {
> +		const struct vimc_pix_map *vpix;
> +
> +		if (vsen->kthread_sen)
> +			return -EINVAL;
> +
> +		/* Calculate the frame size */
> +		vpix = vimc_pix_map_by_code(vsen->mbus_format.code);
> +		vsen->frame_size = vsen->mbus_format.width * vpix->bpp *
> +				   vsen->mbus_format.height;
> +
> +		/*
> +		 * Allocate the frame buffer. Use vmalloc to be able to
> +		 * allocate a large amount of memory
> +		 */
> +		vsen->frame = vmalloc(vsen->frame_size);
> +		if (!vsen->frame)
> +			return -ENOMEM;
> +
> +		/* Initialize the image generator thread */
> +		vsen->kthread_sen = kthread_run(vimc_thread_sen, vsen, "%s-sen",
> +						vsen->sd.v4l2_dev->name);
> +		if (IS_ERR(vsen->kthread_sen)) {
> +			dev_err(vsen->sd.v4l2_dev->dev,
> +				"%s: kernel_thread() failed\n",	vsen->sd.name);
> +			vfree(vsen->frame);
> +			vsen->frame = NULL;
> +			return PTR_ERR(vsen->kthread_sen);
> +		}
> +	} else {
> +		if (!vsen->kthread_sen)
> +			return -EINVAL;
> +
> +		/* Stop image generator */
> +		ret = kthread_stop(vsen->kthread_sen);
> +		vsen->kthread_sen = NULL;
> +
> +		vfree(vsen->frame);
> +		vsen->frame = NULL;
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +struct v4l2_subdev_video_ops vimc_sen_video_ops = {
> +	.s_stream = vimc_sen_s_stream,
> +};
> +
> +static const struct v4l2_subdev_ops vimc_sen_ops = {
> +	.pad = &vimc_sen_pad_ops,
> +	.video = &vimc_sen_video_ops,
> +};
> +
> +static void vimc_sen_destroy(struct vimc_ent_device *ved)
> +{
> +	struct vimc_sen_device *vsen =
> +				container_of(ved, struct vimc_sen_device, ved);
> +
> +	v4l2_device_unregister_subdev(&vsen->sd);
> +	media_entity_cleanup(ved->ent);
> +	kfree(vsen);
> +}
> +
> +struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
> +					const char *const name,
> +					u16 num_pads,
> +					const unsigned long *pads_flag)
> +{
> +	struct vimc_sen_device *vsen;
> +	unsigned int i;
> +	int ret;
> +
> +	/* NOTE: a sensor node may be created with more then one pad */
> +	if (!name || !num_pads || !pads_flag)
> +		return ERR_PTR(-EINVAL);
> +
> +	/* check if all pads are sources */
> +	for (i = 0; i < num_pads; i++)
> +		if (!(pads_flag[i] & MEDIA_PAD_FL_SOURCE))
> +			return ERR_PTR(-EINVAL);
> +
> +	/* Allocate the vsen struct */
> +	vsen = kzalloc(sizeof(*vsen), GFP_KERNEL);
> +	if (!vsen)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/* Allocate the pads */
> +	vsen->ved.pads = vimc_pads_init(num_pads, pads_flag);
> +	if (IS_ERR(vsen->ved.pads)) {
> +		ret = PTR_ERR(vsen->ved.pads);
> +		goto err_free_vsen;
> +	}
> +
> +	/* Fill the vimc_ent_device struct */
> +	vsen->ved.destroy = vimc_sen_destroy;
> +	vsen->ved.ent = &vsen->sd.entity;
> +
> +	/* Initialize the subdev */
> +	v4l2_subdev_init(&vsen->sd, &vimc_sen_ops);
> +	vsen->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
> +	vsen->sd.entity.ops = &vimc_sen_mops;
> +	vsen->sd.owner = THIS_MODULE;
> +	strlcpy(vsen->sd.name, name, sizeof(vsen->sd.name));
> +	v4l2_set_subdevdata(&vsen->sd, &vsen->ved);
> +
> +	/* Expose this subdev to user space */
> +	vsen->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
> +
> +	/* Initialize the media entity */
> +	ret = media_entity_pads_init(&vsen->sd.entity,
> +				     num_pads, vsen->ved.pads);
> +	if (ret)
> +		goto err_clean_pads;
> +
> +	/* Set the active frame format (this is hardcoded for now) */
> +	vsen->mbus_format.width = 640;
> +	vsen->mbus_format.height = 480;
> +	vsen->mbus_format.code = MEDIA_BUS_FMT_RGB888_1X24;
> +	vsen->mbus_format.field = V4L2_FIELD_NONE;
> +	vsen->mbus_format.colorspace = V4L2_COLORSPACE_SRGB;
> +	vsen->mbus_format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
> +	vsen->mbus_format.xfer_func = V4L2_XFER_FUNC_SRGB;
> +
> +	/* Register the subdev with the v4l2 and the media framework */
> +	ret = v4l2_device_register_subdev(v4l2_dev, &vsen->sd);
> +	if (ret) {
> +		dev_err(vsen->sd.v4l2_dev->dev,
> +			"%s: subdev register failed (err=%d)\n",
> +			vsen->sd.name, ret);
> +		goto err_clean_m_ent;
> +	}
> +
> +	return &vsen->ved;
> +
> +err_clean_m_ent:
> +	media_entity_cleanup(&vsen->sd.entity);
> +err_clean_pads:
> +	vimc_pads_cleanup(vsen->ved.pads);
> +err_free_vsen:
> +	kfree(vsen);
> +
> +	return ERR_PTR(ret);
> +}
> diff --git a/drivers/media/platform/vimc/vimc-sensor.h b/drivers/media/platform/vimc/vimc-sensor.h
> new file mode 100644
> index 0000000..505310e
> --- /dev/null
> +++ b/drivers/media/platform/vimc/vimc-sensor.h
> @@ -0,0 +1,28 @@
> +/*
> + * vimc-sensor.h Virtual Media Controller Driver
> + *
> + * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef _VIMC_SENSOR_H_
> +#define _VIMC_SENSOR_H_
> +
> +#include "vimc-core.h"
> +
> +struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
> +					const char *const name,
> +					u16 num_pads,
> +					const unsigned long *pads_flag);
> +
> +#endif
> 

Reviewed-by: Hans Verkuil <hans.verkuil@cisco.com>

Regards,

	Hans

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

* [PATCH v10] [media] vimc: Virtual Media Controller core, capture and sensor
  2017-04-07  9:54                         ` Hans Verkuil
@ 2017-04-07 17:55                           ` Helen Koike
  0 siblings, 0 replies; 41+ messages in thread
From: Helen Koike @ 2017-04-07 17:55 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-media, jgebben, mchehab, Sakari Ailus, Laurent Pinchart

First version of the Virtual Media Controller.
Add a simple version of the core of the driver, the capture and
sensor nodes in the topology, generating a grey image in a hardcoded
format.

Signed-off-by: Helen Koike <helen.koike@collabora.com>
Reviewed-by: Hans Verkuil <hans.verkuil@cisco.com>

---

Patch based in media/master tree, and available here:
https://github.com/helen-fornazier/opw-staging/tree/z/sent/vimc/skel/v10

Changes since v9:
- reuturn -EPIPE instead of -EINVAL in link_validate when formats don't
match
- remove check for vb2_is_busy in vimc_cap_process_frame as it can cause
a dead lock with stop_streaming ioctl
- add vb2_ioctl_prepare_buf in vimc_cap_ioctl_ops
- add reviewd-by tag from Hans
- update MAINTAINERS file

Changes since v8:
- fix vimc_sen_enum_mbus_code: return EINVAL if code->index > 0
- remove input ioctls from vimc-capture.c as v4l2 intends to have default
input ioctls in the core
- change kernel-docs for vimc_pix_map_by_* so the line fits in 80
characters

Changes since v7:
- remove left over union in struct vimc_ent_device
- remove v4l2_dev pointer from vimc_sen_device structure
- remove unused dev parameter from vimc_propagate_frame()
- remove struct device *dev from struct vimc_cap_device and vimc_sen_device
- in vimc_sen_create: call media_entity_pads_init() after v4l2_subdev_init()
to avoid double initialization of vsen->sd.entity.name
- in vimc_sen_destroy: move media_entity_cleanup to be after v4l2_device_unregister_subdev
- rename video_device back to vdev instead of vd
- adjust copyright with range 2015-2017
- add node names in dev_err prints for vimc-capture.c and vimc-sensor.c
- remove prefix "cap" in dev_dbg as it already uses the name of the node

Changes since v6:
- add kernel-docs in vimc-core.h
- reorder list_del call in vimc_cap_return_all_buffers()
- call media_pipeline_stop in vimc_cap_start_streaming when fail
- remove DMA comment (left over from the sample driver)
- remove vb2_set_plane_payload call in vimc_cap_buffer_prepare
- remove format verification in vimc_cap_link_validate
- set vimc_pix_map_list as static
- use MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN in vimc_raw_create()
- register media device after creating the topology and unregister it before destroying the topology
- replace devm_kzalloc by kzalloc for allocating struct vimc_device
- uset TASK_UNINTERRUPTIBLE for vimc_thread_sen
- do not allow the creation of a sensor with no pads
- add more verbose description in Kconfig
- change copyright to 2017
- arrange includes: number before letters
- remove v4l2_dev pointer from vimc_cap_device structure
- coding style adjustments
- remove entity variable in vimc_cap_pipeline_s_stream
- declare and assign variables in vimc_cap_link_validate
- declare vimc_dev_release() and vimc_pdev closer to vimc_init()
- remove pad check in vimc_sen_enum_{mbus_code,frame_size} that is already performed by v4l2-subdev.c
- fix multiline comments to start with /*
- reorder variable declaration in functions
- remove pad and subdevice type check in vimc_cap_pipeline_s_stream
- add a note that sensor nodes can can have more then one source pad
- remove the use of entity->use_count in the core and attach the ved structure in sd or vd, use
  ent->obj_type to know which structure (sd or vd) to use
- rename vdev (video_device) to vd to be similar to sd (subdev)

Changes since v5:
- Fix message "Entity type for entity Sensor A was not initialized!"
  by initializing the sensor entity.function after the calling
  v4l2_subded_init
- populate device_caps in vimc_cap_create instead of in
  vimc_cap_querycap
- Fix typo in vimc-core.c s/de/the

Changes since v4:
- coding style fixes
- remove BUG_ON
- change copyright to 2016
- depens on VIDEO_V4L2_SUBDEV_API instead of select
- remove assignement of V4L2_CAP_DEVICE_CAPS
- s/vimc_cap_g_fmt_vid_cap/vimc_cap_fmt_vid_cap
- fix vimc_cap_queue_setup declaration type
- remove wrong buffer size check and add it in vimc_cap_buffer_prepare
- vimc_cap_create: remove unecessary check if v4l2_dev or v4l2_dev->dev is null
- vimc_cap_create: only allow a single pad
- vimc_sen_create: only allow source pads, remove unecessary source pads
checks in vimc_thread_sen

Changes since v3: fix rmmod crash and built-in compile
- Re-order unregister calls in vimc_device_unregister function (remove
rmmod issue)
- Call media_device_unregister_entity in vimc_raw_destroy
- Add depends on VIDEO_DEV && VIDEO_V4L2 and select VIDEOBUF2_VMALLOC
- Check *nplanes in queue_setup (this remove v4l2-compliance fail)
- Include <linux/kthread.h> in vimc-sensor.c
- Move include of <linux/slab.h> from vimc-core.c to vimc-core.h
- Generate 60 frames per sec instead of 1 in the sensor

Changes since v2: update with current media master tree
- Add struct media_pipeline in vimc_cap_device
- Use vb2_v4l2_buffer instead of vb2_buffer
- Typos
- Remove usage of entity->type and use entity->function instead
- Remove fmt argument from queue setup
- Use ktime_get_ns instead of v4l2_get_timestamp
- Iterate over link's list using list_for_each_entry
- Use media_device_{init, cleanup}
- Use entity->use_count to keep track of entities instead of the old
entity->id
- Replace media_entity_init by media_entity_pads_init
---
 MAINTAINERS                                |   8 +
 drivers/media/platform/Kconfig             |   2 +
 drivers/media/platform/Makefile            |   1 +
 drivers/media/platform/vimc/Kconfig        |  14 +
 drivers/media/platform/vimc/Makefile       |   3 +
 drivers/media/platform/vimc/vimc-capture.c | 498 +++++++++++++++++++++
 drivers/media/platform/vimc/vimc-capture.h |  28 ++
 drivers/media/platform/vimc/vimc-core.c    | 695 +++++++++++++++++++++++++++++
 drivers/media/platform/vimc/vimc-core.h    | 112 +++++
 drivers/media/platform/vimc/vimc-sensor.c  | 276 ++++++++++++
 drivers/media/platform/vimc/vimc-sensor.h  |  28 ++
 11 files changed, 1665 insertions(+)
 create mode 100644 drivers/media/platform/vimc/Kconfig
 create mode 100644 drivers/media/platform/vimc/Makefile
 create mode 100644 drivers/media/platform/vimc/vimc-capture.c
 create mode 100644 drivers/media/platform/vimc/vimc-capture.h
 create mode 100644 drivers/media/platform/vimc/vimc-core.c
 create mode 100644 drivers/media/platform/vimc/vimc-core.h
 create mode 100644 drivers/media/platform/vimc/vimc-sensor.c
 create mode 100644 drivers/media/platform/vimc/vimc-sensor.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 1f1d103..5283269 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13390,6 +13390,14 @@ W:	https://linuxtv.org
 S:	Maintained
 F:	drivers/media/platform/vivid/*
 
+VIMC VIRTUAL MEDIA CONTROLLER DRIVER
+M:	Helen Koike <helen.koike@collabora.com>
+L:	linux-media@vger.kernel.org
+T:	git git://linuxtv.org/media_tree.git
+W:	https://linuxtv.org
+S:	Maintained
+F:	drivers/media/platform/vimc/*
+
 VLYNQ BUS
 M:	Florian Fainelli <f.fainelli@gmail.com>
 L:	openwrt-devel@lists.openwrt.org (subscribers-only)
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index ab0bb48..b764fba 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -466,6 +466,8 @@ menuconfig V4L_TEST_DRIVERS
 
 if V4L_TEST_DRIVERS
 
+source "drivers/media/platform/vimc/Kconfig"
+
 source "drivers/media/platform/vivid/Kconfig"
 
 config VIDEO_VIM2M
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 8959f6e..4af4cce 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_VIDEO_PXA27x)	+= pxa_camera.o
 
 obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o
 
+obj-$(CONFIG_VIDEO_VIMC)		+= vimc/
 obj-$(CONFIG_VIDEO_VIVID)		+= vivid/
 obj-$(CONFIG_VIDEO_VIM2M)		+= vim2m.o
 
diff --git a/drivers/media/platform/vimc/Kconfig b/drivers/media/platform/vimc/Kconfig
new file mode 100644
index 0000000..dd285fa
--- /dev/null
+++ b/drivers/media/platform/vimc/Kconfig
@@ -0,0 +1,14 @@
+config VIDEO_VIMC
+	tristate "Virtual Media Controller Driver (VIMC)"
+	depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+	select VIDEOBUF2_VMALLOC
+	default n
+	---help---
+	  Skeleton driver for Virtual Media Controller
+
+	  This drivers can be compared to the Vivid driver for emulating
+	  a media node that exposes a complex media topology. The topology
+	  is hard coded for now but is meant to be highly configurable in
+	  the future.
+
+	  When in doubt, say N.
diff --git a/drivers/media/platform/vimc/Makefile b/drivers/media/platform/vimc/Makefile
new file mode 100644
index 0000000..c45195e
--- /dev/null
+++ b/drivers/media/platform/vimc/Makefile
@@ -0,0 +1,3 @@
+vimc-objs := vimc-core.o vimc-capture.o vimc-sensor.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
new file mode 100644
index 0000000..9adb06d
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-capture.c
@@ -0,0 +1,498 @@
+/*
+ * vimc-capture.c Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-vmalloc.h>
+
+#include "vimc-capture.h"
+
+struct vimc_cap_device {
+	struct vimc_ent_device ved;
+	struct video_device vdev;
+	struct v4l2_pix_format format;
+	struct vb2_queue queue;
+	struct list_head buf_list;
+	/*
+	 * NOTE: in a real driver, a spin lock must be used to access the
+	 * queue because the frames are generated from a hardware interruption
+	 * and the isr is not allowed to sleep.
+	 * Even if it is not necessary a spinlock in the vimc driver, we
+	 * use it here as a code reference
+	 */
+	spinlock_t qlock;
+	struct mutex lock;
+	u32 sequence;
+	struct media_pipeline pipe;
+};
+
+struct vimc_cap_buffer {
+	/*
+	 * struct vb2_v4l2_buffer must be the first element
+	 * the videobuf2 framework will allocate this struct based on
+	 * buf_struct_size and use the first sizeof(struct vb2_buffer) bytes of
+	 * memory as a vb2_buffer
+	 */
+	struct vb2_v4l2_buffer vb2;
+	struct list_head list;
+};
+
+static int vimc_cap_querycap(struct file *file, void *priv,
+			     struct v4l2_capability *cap)
+{
+	struct vimc_cap_device *vcap = video_drvdata(file);
+
+	strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
+	strlcpy(cap->card, KBUILD_MODNAME, sizeof(cap->card));
+	snprintf(cap->bus_info, sizeof(cap->bus_info),
+		 "platform:%s", vcap->vdev.v4l2_dev->name);
+
+	return 0;
+}
+
+static int vimc_cap_fmt_vid_cap(struct file *file, void *priv,
+				  struct v4l2_format *f)
+{
+	struct vimc_cap_device *vcap = video_drvdata(file);
+
+	f->fmt.pix = vcap->format;
+
+	return 0;
+}
+
+static int vimc_cap_enum_fmt_vid_cap(struct file *file, void *priv,
+				     struct v4l2_fmtdesc *f)
+{
+	struct vimc_cap_device *vcap = video_drvdata(file);
+
+	if (f->index > 0)
+		return -EINVAL;
+
+	/* We only support one format for now */
+	f->pixelformat = vcap->format.pixelformat;
+
+	return 0;
+}
+
+static const struct v4l2_file_operations vimc_cap_fops = {
+	.owner		= THIS_MODULE,
+	.open		= v4l2_fh_open,
+	.release	= vb2_fop_release,
+	.read           = vb2_fop_read,
+	.poll		= vb2_fop_poll,
+	.unlocked_ioctl = video_ioctl2,
+	.mmap           = vb2_fop_mmap,
+};
+
+static const struct v4l2_ioctl_ops vimc_cap_ioctl_ops = {
+	.vidioc_querycap = vimc_cap_querycap,
+
+	.vidioc_g_fmt_vid_cap = vimc_cap_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap = vimc_cap_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap = vimc_cap_fmt_vid_cap,
+	.vidioc_enum_fmt_vid_cap = vimc_cap_enum_fmt_vid_cap,
+
+	.vidioc_reqbufs = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs = vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+	.vidioc_querybuf = vb2_ioctl_querybuf,
+	.vidioc_qbuf = vb2_ioctl_qbuf,
+	.vidioc_dqbuf = vb2_ioctl_dqbuf,
+	.vidioc_expbuf = vb2_ioctl_expbuf,
+	.vidioc_streamon = vb2_ioctl_streamon,
+	.vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+static void vimc_cap_return_all_buffers(struct vimc_cap_device *vcap,
+					enum vb2_buffer_state state)
+{
+	struct vimc_cap_buffer *vbuf, *node;
+
+	spin_lock(&vcap->qlock);
+
+	list_for_each_entry_safe(vbuf, node, &vcap->buf_list, list) {
+		list_del(&vbuf->list);
+		vb2_buffer_done(&vbuf->vb2.vb2_buf, state);
+	}
+
+	spin_unlock(&vcap->qlock);
+}
+
+static int vimc_cap_pipeline_s_stream(struct vimc_cap_device *vcap, int enable)
+{
+	struct v4l2_subdev *sd;
+	struct media_pad *pad;
+	int ret;
+
+	/* Start the stream in the subdevice direct connected */
+	pad = media_entity_remote_pad(&vcap->vdev.entity.pads[0]);
+
+	/*
+	 * if it is a raw node from vimc-core, there is nothing to activate
+	 * TODO: remove this when there are no more raw nodes in the
+	 * core and return error instead
+	 */
+	if (pad->entity->obj_type == MEDIA_ENTITY_TYPE_BASE)
+		return 0;
+
+	sd = media_entity_to_v4l2_subdev(pad->entity);
+	ret = v4l2_subdev_call(sd, video, s_stream, enable);
+	if (ret && ret != -ENOIOCTLCMD)
+		return ret;
+
+	return 0;
+}
+
+static int vimc_cap_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
+	struct media_entity *entity = &vcap->vdev.entity;
+	int ret;
+
+	vcap->sequence = 0;
+
+	/* Start the media pipeline */
+	ret = media_pipeline_start(entity, &vcap->pipe);
+	if (ret) {
+		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
+		return ret;
+	}
+
+	/* Enable streaming from the pipe */
+	ret = vimc_cap_pipeline_s_stream(vcap, 1);
+	if (ret) {
+		media_pipeline_stop(entity);
+		vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_QUEUED);
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Stop the stream engine. Any remaining buffers in the stream queue are
+ * dequeued and passed on to the vb2 framework marked as STATE_ERROR.
+ */
+static void vimc_cap_stop_streaming(struct vb2_queue *vq)
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
+
+	/* Disable streaming from the pipe */
+	vimc_cap_pipeline_s_stream(vcap, 0);
+
+	/* Stop the media pipeline */
+	media_pipeline_stop(&vcap->vdev.entity);
+
+	/* Release all active buffers */
+	vimc_cap_return_all_buffers(vcap, VB2_BUF_STATE_ERROR);
+}
+
+static void vimc_cap_buf_queue(struct vb2_buffer *vb2_buf)
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb2_buf->vb2_queue);
+	struct vimc_cap_buffer *buf = container_of(vb2_buf,
+						   struct vimc_cap_buffer,
+						   vb2.vb2_buf);
+
+	spin_lock(&vcap->qlock);
+	list_add_tail(&buf->list, &vcap->buf_list);
+	spin_unlock(&vcap->qlock);
+}
+
+static int vimc_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
+				unsigned int *nplanes, unsigned int sizes[],
+				struct device *alloc_devs[])
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
+
+	if (*nplanes)
+		return sizes[0] < vcap->format.sizeimage ? -EINVAL : 0;
+	/* We don't support multiplanes for now */
+	*nplanes = 1;
+	sizes[0] = vcap->format.sizeimage;
+
+	return 0;
+}
+
+static int vimc_cap_buffer_prepare(struct vb2_buffer *vb)
+{
+	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb->vb2_queue);
+	unsigned long size = vcap->format.sizeimage;
+
+	if (vb2_plane_size(vb, 0) < size) {
+		dev_err(vcap->vdev.v4l2_dev->dev,
+			"%s: buffer too small (%lu < %lu)\n",
+			vcap->vdev.name, vb2_plane_size(vb, 0), size);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static const struct vb2_ops vimc_cap_qops = {
+	.start_streaming	= vimc_cap_start_streaming,
+	.stop_streaming		= vimc_cap_stop_streaming,
+	.buf_queue		= vimc_cap_buf_queue,
+	.queue_setup		= vimc_cap_queue_setup,
+	.buf_prepare		= vimc_cap_buffer_prepare,
+	/*
+	 * Since q->lock is set we can use the standard
+	 * vb2_ops_wait_prepare/finish helper functions.
+	 */
+	.wait_prepare		= vb2_ops_wait_prepare,
+	.wait_finish		= vb2_ops_wait_finish,
+};
+
+/*
+ * NOTE: this function is a copy of v4l2_subdev_link_validate_get_format
+ * maybe the v4l2 function should be public
+ */
+static int vimc_cap_v4l2_subdev_link_validate_get_format(struct media_pad *pad,
+						struct v4l2_subdev_format *fmt)
+{
+	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity);
+
+	fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	fmt->pad = pad->index;
+
+	return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt);
+}
+
+static int vimc_cap_link_validate(struct media_link *link)
+{
+	struct v4l2_subdev_format source_fmt;
+	const struct vimc_pix_map *vpix;
+	struct vimc_cap_device *vcap = container_of(link->sink->entity,
+						    struct vimc_cap_device,
+						    vdev.entity);
+	struct v4l2_pix_format *sink_fmt = &vcap->format;
+	int ret;
+
+	/*
+	 * if it is a raw node from vimc-core, ignore the link for now
+	 * TODO: remove this when there are no more raw nodes in the
+	 * core and return error instead
+	 */
+	if (link->source->entity->obj_type == MEDIA_ENTITY_TYPE_BASE)
+		return 0;
+
+	/* Get the the format of the subdev */
+	ret = vimc_cap_v4l2_subdev_link_validate_get_format(link->source,
+							    &source_fmt);
+	if (ret)
+		return ret;
+
+	dev_dbg(vcap->vdev.v4l2_dev->dev,
+		"%s: link validate formats src:%dx%d %d sink:%dx%d %d\n",
+		vcap->vdev.name,
+		source_fmt.format.width, source_fmt.format.height,
+		source_fmt.format.code,
+		sink_fmt->width, sink_fmt->height,
+		sink_fmt->pixelformat);
+
+	/* The width, height and code must match. */
+	vpix = vimc_pix_map_by_pixelformat(sink_fmt->pixelformat);
+	if (source_fmt.format.width != sink_fmt->width
+	    || source_fmt.format.height != sink_fmt->height
+	    || vpix->code != source_fmt.format.code)
+		return -EPIPE;
+
+	/*
+	 * The field order must match, or the sink field order must be NONE
+	 * to support interlaced hardware connected to bridges that support
+	 * progressive formats only.
+	 */
+	if (source_fmt.format.field != sink_fmt->field &&
+	    sink_fmt->field != V4L2_FIELD_NONE)
+		return -EPIPE;
+
+	return 0;
+}
+
+static const struct media_entity_operations vimc_cap_mops = {
+	.link_validate		= vimc_cap_link_validate,
+};
+
+static void vimc_cap_destroy(struct vimc_ent_device *ved)
+{
+	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
+						    ved);
+
+	vb2_queue_release(&vcap->queue);
+	media_entity_cleanup(ved->ent);
+	video_unregister_device(&vcap->vdev);
+	vimc_pads_cleanup(vcap->ved.pads);
+	kfree(vcap);
+}
+
+static void vimc_cap_process_frame(struct vimc_ent_device *ved,
+				   struct media_pad *sink, const void *frame)
+{
+	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
+						    ved);
+	struct vimc_cap_buffer *vimc_buf;
+	void *vbuf;
+
+	spin_lock(&vcap->qlock);
+
+	/* Get the first entry of the list */
+	vimc_buf = list_first_entry_or_null(&vcap->buf_list,
+					    typeof(*vimc_buf), list);
+	if (!vimc_buf) {
+		spin_unlock(&vcap->qlock);
+		return;
+	}
+
+	/* Remove this entry from the list */
+	list_del(&vimc_buf->list);
+
+	spin_unlock(&vcap->qlock);
+
+	/* Fill the buffer */
+	vimc_buf->vb2.vb2_buf.timestamp = ktime_get_ns();
+	vimc_buf->vb2.sequence = vcap->sequence++;
+	vimc_buf->vb2.field = vcap->format.field;
+
+	vbuf = vb2_plane_vaddr(&vimc_buf->vb2.vb2_buf, 0);
+
+	memcpy(vbuf, frame, vcap->format.sizeimage);
+
+	/* Set it as ready */
+	vb2_set_plane_payload(&vimc_buf->vb2.vb2_buf, 0,
+			      vcap->format.sizeimage);
+	vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag)
+{
+	const struct vimc_pix_map *vpix;
+	struct vimc_cap_device *vcap;
+	struct video_device *vdev;
+	struct vb2_queue *q;
+	int ret;
+
+	/*
+	 * Check entity configuration params
+	 * NOTE: we only support a single sink pad
+	 */
+	if (!name || num_pads != 1 || !pads_flag ||
+	    !(pads_flag[0] & MEDIA_PAD_FL_SINK))
+		return ERR_PTR(-EINVAL);
+
+	/* Allocate the vimc_cap_device struct */
+	vcap = kzalloc(sizeof(*vcap), GFP_KERNEL);
+	if (!vcap)
+		return ERR_PTR(-ENOMEM);
+
+	/* Allocate the pads */
+	vcap->ved.pads = vimc_pads_init(num_pads, pads_flag);
+	if (IS_ERR(vcap->ved.pads)) {
+		ret = PTR_ERR(vcap->ved.pads);
+		goto err_free_vcap;
+	}
+
+	/* Initialize the media entity */
+	vcap->vdev.entity.name = name;
+	vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L;
+	ret = media_entity_pads_init(&vcap->vdev.entity,
+				     num_pads, vcap->ved.pads);
+	if (ret)
+		goto err_clean_pads;
+
+	/* Initialize the lock */
+	mutex_init(&vcap->lock);
+
+	/* Initialize the vb2 queue */
+	q = &vcap->queue;
+	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	q->io_modes = VB2_MMAP | VB2_DMABUF;
+	q->drv_priv = vcap;
+	q->buf_struct_size = sizeof(struct vimc_cap_buffer);
+	q->ops = &vimc_cap_qops;
+	q->mem_ops = &vb2_vmalloc_memops;
+	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	q->min_buffers_needed = 2;
+	q->lock = &vcap->lock;
+
+	ret = vb2_queue_init(q);
+	if (ret) {
+		dev_err(vcap->vdev.v4l2_dev->dev,
+			"%s: vb2 queue init failed (err=%d)\n",
+			vcap->vdev.name, ret);
+		goto err_clean_m_ent;
+	}
+
+	/* Initialize buffer list and its lock */
+	INIT_LIST_HEAD(&vcap->buf_list);
+	spin_lock_init(&vcap->qlock);
+
+	/* Set the frame format (this is hardcoded for now) */
+	vcap->format.width = 640;
+	vcap->format.height = 480;
+	vcap->format.pixelformat = V4L2_PIX_FMT_RGB24;
+	vcap->format.field = V4L2_FIELD_NONE;
+	vcap->format.colorspace = V4L2_COLORSPACE_SRGB;
+
+	vpix = vimc_pix_map_by_pixelformat(vcap->format.pixelformat);
+
+	vcap->format.bytesperline = vcap->format.width * vpix->bpp;
+	vcap->format.sizeimage = vcap->format.bytesperline *
+				 vcap->format.height;
+
+	/* Fill the vimc_ent_device struct */
+	vcap->ved.destroy = vimc_cap_destroy;
+	vcap->ved.ent = &vcap->vdev.entity;
+	vcap->ved.process_frame = vimc_cap_process_frame;
+
+	/* Initialize the video_device struct */
+	vdev = &vcap->vdev;
+	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	vdev->entity.ops = &vimc_cap_mops;
+	vdev->release = video_device_release_empty;
+	vdev->fops = &vimc_cap_fops;
+	vdev->ioctl_ops = &vimc_cap_ioctl_ops;
+	vdev->lock = &vcap->lock;
+	vdev->queue = q;
+	vdev->v4l2_dev = v4l2_dev;
+	vdev->vfl_dir = VFL_DIR_RX;
+	strlcpy(vdev->name, name, sizeof(vdev->name));
+	video_set_drvdata(vdev, &vcap->ved);
+
+	/* Register the video_device with the v4l2 and the media framework */
+	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+	if (ret) {
+		dev_err(vcap->vdev.v4l2_dev->dev,
+			"%s: video register failed (err=%d)\n",
+			vcap->vdev.name, ret);
+		goto err_release_queue;
+	}
+
+	return &vcap->ved;
+
+err_release_queue:
+	vb2_queue_release(q);
+err_clean_m_ent:
+	media_entity_cleanup(&vcap->vdev.entity);
+err_clean_pads:
+	vimc_pads_cleanup(vcap->ved.pads);
+err_free_vcap:
+	kfree(vcap);
+
+	return ERR_PTR(ret);
+}
diff --git a/drivers/media/platform/vimc/vimc-capture.h b/drivers/media/platform/vimc/vimc-capture.h
new file mode 100644
index 0000000..581a813
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-capture.h
@@ -0,0 +1,28 @@
+/*
+ * vimc-capture.h Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIMC_CAPTURE_H_
+#define _VIMC_CAPTURE_H_
+
+#include "vimc-core.h"
+
+struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag);
+
+#endif
diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c
new file mode 100644
index 0000000..bc107da
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-core.c
@@ -0,0 +1,695 @@
+/*
+ * vimc-core.c Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <media/media-device.h>
+#include <media/v4l2-device.h>
+
+#include "vimc-capture.h"
+#include "vimc-core.h"
+#include "vimc-sensor.h"
+
+#define VIMC_PDEV_NAME "vimc"
+#define VIMC_MDEV_MODEL_NAME "VIMC MDEV"
+
+#define VIMC_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) {	\
+	.src_ent = src,						\
+	.src_pad = srcpad,					\
+	.sink_ent = sink,					\
+	.sink_pad = sinkpad,					\
+	.flags = link_flags,					\
+}
+
+struct vimc_device {
+	/*
+	 * The pipeline configuration
+	 * (filled before calling vimc_device_register)
+	 */
+	const struct vimc_pipeline_config *pipe_cfg;
+
+	/* The Associated media_device parent */
+	struct media_device mdev;
+
+	/* Internal v4l2 parent device*/
+	struct v4l2_device v4l2_dev;
+
+	/* Internal topology */
+	struct vimc_ent_device **ved;
+};
+
+/**
+ * enum vimc_ent_node - Select the functionality of a node in the topology
+ * @VIMC_ENT_NODE_SENSOR:	A node of type SENSOR simulates a camera sensor
+ *				generating internal images in bayer format and
+ *				propagating those images through the pipeline
+ * @VIMC_ENT_NODE_CAPTURE:	A node of type CAPTURE is a v4l2 video_device
+ *				that exposes the received image from the
+ *				pipeline to the user space
+ * @VIMC_ENT_NODE_INPUT:	A node of type INPUT is a v4l2 video_device that
+ *				receives images from the user space and
+ *				propagates them through the pipeline
+ * @VIMC_ENT_NODE_DEBAYER:	A node type DEBAYER expects to receive a frame
+ *				in bayer format converts it to RGB
+ * @VIMC_ENT_NODE_SCALER:	A node of type SCALER scales the received image
+ *				by a given multiplier
+ *
+ * This enum is used in the entity configuration struct to allow the definition
+ * of a custom topology specifying the role of each node on it.
+ */
+enum vimc_ent_node {
+	VIMC_ENT_NODE_SENSOR,
+	VIMC_ENT_NODE_CAPTURE,
+	VIMC_ENT_NODE_INPUT,
+	VIMC_ENT_NODE_DEBAYER,
+	VIMC_ENT_NODE_SCALER,
+};
+
+/* Structure which describes individual configuration for each entity */
+struct vimc_ent_config {
+	const char *name;
+	size_t pads_qty;
+	const unsigned long *pads_flag;
+	enum vimc_ent_node node;
+};
+
+/* Structure which describes links between entities */
+struct vimc_ent_link {
+	unsigned int src_ent;
+	u16 src_pad;
+	unsigned int sink_ent;
+	u16 sink_pad;
+	u32 flags;
+};
+
+/* Structure which describes the whole topology */
+struct vimc_pipeline_config {
+	const struct vimc_ent_config *ents;
+	size_t num_ents;
+	const struct vimc_ent_link *links;
+	size_t num_links;
+};
+
+/* --------------------------------------------------------------------------
+ * Topology Configuration
+ */
+
+static const struct vimc_ent_config ent_config[] = {
+	{
+		.name = "Sensor A",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_SENSOR,
+	},
+	{
+		.name = "Sensor B",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_SENSOR,
+	},
+	{
+		.name = "Debayer A",
+		.pads_qty = 2,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
+						     MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_DEBAYER,
+	},
+	{
+		.name = "Debayer B",
+		.pads_qty = 2,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
+						     MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_DEBAYER,
+	},
+	{
+		.name = "Raw Capture 0",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
+		.node = VIMC_ENT_NODE_CAPTURE,
+	},
+	{
+		.name = "Raw Capture 1",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
+		.node = VIMC_ENT_NODE_CAPTURE,
+	},
+	{
+		.name = "RGB/YUV Input",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_INPUT,
+	},
+	{
+		.name = "Scaler",
+		.pads_qty = 2,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK,
+						     MEDIA_PAD_FL_SOURCE},
+		.node = VIMC_ENT_NODE_SCALER,
+	},
+	{
+		.name = "RGB/YUV Capture",
+		.pads_qty = 1,
+		.pads_flag = (const unsigned long[]){MEDIA_PAD_FL_SINK},
+		.node = VIMC_ENT_NODE_CAPTURE,
+	},
+};
+
+static const struct vimc_ent_link ent_links[] = {
+	/* Link: Sensor A (Pad 0)->(Pad 0) Debayer A */
+	VIMC_ENT_LINK(0, 0, 2, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Sensor A (Pad 0)->(Pad 0) Raw Capture 0 */
+	VIMC_ENT_LINK(0, 0, 4, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Sensor B (Pad 0)->(Pad 0) Debayer B */
+	VIMC_ENT_LINK(1, 0, 3, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Sensor B (Pad 0)->(Pad 0) Raw Capture 1 */
+	VIMC_ENT_LINK(1, 0, 5, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+	/* Link: Debayer A (Pad 1)->(Pad 0) Scaler */
+	VIMC_ENT_LINK(2, 1, 7, 0, MEDIA_LNK_FL_ENABLED),
+	/* Link: Debayer B (Pad 1)->(Pad 0) Scaler */
+	VIMC_ENT_LINK(3, 1, 7, 0, 0),
+	/* Link: RGB/YUV Input (Pad 0)->(Pad 0) Scaler */
+	VIMC_ENT_LINK(6, 0, 7, 0, 0),
+	/* Link: Scaler (Pad 1)->(Pad 0) RGB/YUV Capture */
+	VIMC_ENT_LINK(7, 1, 8, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+};
+
+static const struct vimc_pipeline_config pipe_cfg = {
+	.ents		= ent_config,
+	.num_ents	= ARRAY_SIZE(ent_config),
+	.links		= ent_links,
+	.num_links	= ARRAY_SIZE(ent_links)
+};
+
+/* -------------------------------------------------------------------------- */
+
+static const struct vimc_pix_map vimc_pix_map_list[] = {
+	/* TODO: add all missing formats */
+
+	/* RGB formats */
+	{
+		.code = MEDIA_BUS_FMT_BGR888_1X24,
+		.pixelformat = V4L2_PIX_FMT_BGR24,
+		.bpp = 3,
+	},
+	{
+		.code = MEDIA_BUS_FMT_RGB888_1X24,
+		.pixelformat = V4L2_PIX_FMT_RGB24,
+		.bpp = 3,
+	},
+	{
+		.code = MEDIA_BUS_FMT_ARGB8888_1X32,
+		.pixelformat = V4L2_PIX_FMT_ARGB32,
+		.bpp = 4,
+	},
+
+	/* Bayer formats */
+	{
+		.code = MEDIA_BUS_FMT_SBGGR8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SBGGR8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGBRG8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SGBRG8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGRBG8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SGRBG8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SRGGB8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SRGGB8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SBGGR10_1X10,
+		.pixelformat = V4L2_PIX_FMT_SBGGR10,
+		.bpp = 2,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGBRG10_1X10,
+		.pixelformat = V4L2_PIX_FMT_SGBRG10,
+		.bpp = 2,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGRBG10_1X10,
+		.pixelformat = V4L2_PIX_FMT_SGRBG10,
+		.bpp = 2,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SRGGB10_1X10,
+		.pixelformat = V4L2_PIX_FMT_SRGGB10,
+		.bpp = 2,
+	},
+
+	/* 10bit raw bayer a-law compressed to 8 bits */
+	{
+		.code = MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SBGGR10ALAW8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SGBRG10ALAW8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SGRBG10ALAW8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SRGGB10ALAW8,
+		.bpp = 1,
+	},
+
+	/* 10bit raw bayer DPCM compressed to 8 bits */
+	{
+		.code = MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SBGGR10DPCM8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SGBRG10DPCM8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8,
+		.pixelformat = V4L2_PIX_FMT_SRGGB10DPCM8,
+		.bpp = 1,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SBGGR12_1X12,
+		.pixelformat = V4L2_PIX_FMT_SBGGR12,
+		.bpp = 2,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGBRG12_1X12,
+		.pixelformat = V4L2_PIX_FMT_SGBRG12,
+		.bpp = 2,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SGRBG12_1X12,
+		.pixelformat = V4L2_PIX_FMT_SGRBG12,
+		.bpp = 2,
+	},
+	{
+		.code = MEDIA_BUS_FMT_SRGGB12_1X12,
+		.pixelformat = V4L2_PIX_FMT_SRGGB12,
+		.bpp = 2,
+	},
+};
+
+const struct vimc_pix_map *vimc_pix_map_by_code(u32 code)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(vimc_pix_map_list); i++) {
+		if (vimc_pix_map_list[i].code == code)
+			return &vimc_pix_map_list[i];
+	}
+	return NULL;
+}
+
+const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(vimc_pix_map_list); i++) {
+		if (vimc_pix_map_list[i].pixelformat == pixelformat)
+			return &vimc_pix_map_list[i];
+	}
+	return NULL;
+}
+
+int vimc_propagate_frame(struct media_pad *src, const void *frame)
+{
+	struct media_link *link;
+
+	if (!(src->flags & MEDIA_PAD_FL_SOURCE))
+		return -EINVAL;
+
+	/* Send this frame to all sink pads that are direct linked */
+	list_for_each_entry(link, &src->entity->links, list) {
+		if (link->source == src &&
+		    (link->flags & MEDIA_LNK_FL_ENABLED)) {
+			struct vimc_ent_device *ved = NULL;
+			struct media_entity *entity = link->sink->entity;
+
+			if (is_media_entity_v4l2_subdev(entity)) {
+				struct v4l2_subdev *sd =
+					container_of(entity, struct v4l2_subdev,
+						     entity);
+				ved = v4l2_get_subdevdata(sd);
+			} else if (is_media_entity_v4l2_video_device(entity)) {
+				struct video_device *vdev =
+					container_of(entity,
+						     struct video_device,
+						     entity);
+				ved = video_get_drvdata(vdev);
+			}
+			if (ved && ved->process_frame)
+				ved->process_frame(ved, link->sink, frame);
+		}
+	}
+
+	return 0;
+}
+
+static void vimc_device_unregister(struct vimc_device *vimc)
+{
+	unsigned int i;
+
+	media_device_unregister(&vimc->mdev);
+	/* Cleanup (only initialized) entities */
+	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
+		if (vimc->ved[i] && vimc->ved[i]->destroy)
+			vimc->ved[i]->destroy(vimc->ved[i]);
+
+		vimc->ved[i] = NULL;
+	}
+	v4l2_device_unregister(&vimc->v4l2_dev);
+	media_device_cleanup(&vimc->mdev);
+}
+
+/* Helper function to allocate and initialize pads */
+struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag)
+{
+	struct media_pad *pads;
+	unsigned int i;
+
+	/* Allocate memory for the pads */
+	pads = kcalloc(num_pads, sizeof(*pads), GFP_KERNEL);
+	if (!pads)
+		return ERR_PTR(-ENOMEM);
+
+	/* Initialize the pads */
+	for (i = 0; i < num_pads; i++) {
+		pads[i].index = i;
+		pads[i].flags = pads_flag[i];
+	}
+
+	return pads;
+}
+
+/*
+ * TODO: remove this function when all the
+ * entities specific code are implemented
+ */
+static void vimc_raw_destroy(struct vimc_ent_device *ved)
+{
+	media_device_unregister_entity(ved->ent);
+
+	media_entity_cleanup(ved->ent);
+
+	vimc_pads_cleanup(ved->pads);
+
+	kfree(ved->ent);
+
+	kfree(ved);
+}
+
+/*
+ * TODO: remove this function when all the
+ * entities specific code are implemented
+ */
+static struct vimc_ent_device *vimc_raw_create(struct v4l2_device *v4l2_dev,
+					       const char *const name,
+					       u16 num_pads,
+					       const unsigned long *pads_flag)
+{
+	struct vimc_ent_device *ved;
+	int ret;
+
+	/* Allocate the main ved struct */
+	ved = kzalloc(sizeof(*ved), GFP_KERNEL);
+	if (!ved)
+		return ERR_PTR(-ENOMEM);
+
+	/* Allocate the media entity */
+	ved->ent = kzalloc(sizeof(*ved->ent), GFP_KERNEL);
+	if (!ved->ent) {
+		ret = -ENOMEM;
+		goto err_free_ved;
+	}
+
+	/* Allocate the pads */
+	ved->pads = vimc_pads_init(num_pads, pads_flag);
+	if (IS_ERR(ved->pads)) {
+		ret = PTR_ERR(ved->pads);
+		goto err_free_ent;
+	}
+
+	/* Initialize the media entity */
+	ved->ent->name = name;
+	ved->ent->function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
+	ret = media_entity_pads_init(ved->ent, num_pads, ved->pads);
+	if (ret)
+		goto err_cleanup_pads;
+
+	/* Register the media entity */
+	ret = media_device_register_entity(v4l2_dev->mdev, ved->ent);
+	if (ret)
+		goto err_cleanup_entity;
+
+	/* Fill out the destroy function and return */
+	ved->destroy = vimc_raw_destroy;
+	return ved;
+
+err_cleanup_entity:
+	media_entity_cleanup(ved->ent);
+err_cleanup_pads:
+	vimc_pads_cleanup(ved->pads);
+err_free_ent:
+	kfree(ved->ent);
+err_free_ved:
+	kfree(ved);
+
+	return ERR_PTR(ret);
+}
+
+static int vimc_device_register(struct vimc_device *vimc)
+{
+	unsigned int i;
+	int ret;
+
+	/* Allocate memory for the vimc_ent_devices pointers */
+	vimc->ved = devm_kcalloc(vimc->mdev.dev, vimc->pipe_cfg->num_ents,
+				 sizeof(*vimc->ved), GFP_KERNEL);
+	if (!vimc->ved)
+		return -ENOMEM;
+
+	/* Link the media device within the v4l2_device */
+	vimc->v4l2_dev.mdev = &vimc->mdev;
+
+	/* Register the v4l2 struct */
+	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);
+		return ret;
+	}
+
+	/* Initialize entities */
+	for (i = 0; i < vimc->pipe_cfg->num_ents; i++) {
+		struct vimc_ent_device *(*create_func)(struct v4l2_device *,
+						       const char *const,
+						       u16,
+						       const unsigned long *);
+
+		/* Register the specific node */
+		switch (vimc->pipe_cfg->ents[i].node) {
+		case VIMC_ENT_NODE_SENSOR:
+			create_func = vimc_sen_create;
+			break;
+
+		case VIMC_ENT_NODE_CAPTURE:
+			create_func = vimc_cap_create;
+			break;
+
+		/* TODO: Instantiate the specific topology node */
+		case VIMC_ENT_NODE_INPUT:
+		case VIMC_ENT_NODE_DEBAYER:
+		case VIMC_ENT_NODE_SCALER:
+		default:
+			/*
+			 * TODO: remove this when all the entities specific
+			 * code are implemented
+			 */
+			create_func = vimc_raw_create;
+			break;
+		}
+
+		vimc->ved[i] = create_func(&vimc->v4l2_dev,
+					   vimc->pipe_cfg->ents[i].name,
+					   vimc->pipe_cfg->ents[i].pads_qty,
+					   vimc->pipe_cfg->ents[i].pads_flag);
+		if (IS_ERR(vimc->ved[i])) {
+			ret = PTR_ERR(vimc->ved[i]);
+			vimc->ved[i] = NULL;
+			goto err;
+		}
+	}
+
+	/* 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];
+
+		ret = media_create_pad_link(vimc->ved[link->src_ent]->ent,
+					    link->src_pad,
+					    vimc->ved[link->sink_ent]->ent,
+					    link->sink_pad,
+					    link->flags);
+		if (ret)
+			goto err;
+	}
+
+	/* Register the media device */
+	ret = media_device_register(&vimc->mdev);
+	if (ret) {
+		dev_err(vimc->mdev.dev,
+			"media device register failed (err=%d)\n", ret);
+		return ret;
+	}
+
+	/* 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);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	/* Destroy the so far created topology */
+	vimc_device_unregister(vimc);
+
+	return ret;
+}
+
+static int vimc_probe(struct platform_device *pdev)
+{
+	struct vimc_device *vimc;
+	int ret;
+
+	/* Prepare the vimc topology structure */
+
+	/* Allocate memory for the vimc structure */
+	vimc = kzalloc(sizeof(*vimc), GFP_KERNEL);
+	if (!vimc)
+		return -ENOMEM;
+
+	/* Set the pipeline configuration struct */
+	vimc->pipe_cfg = &pipe_cfg;
+
+	/* Initialize media device */
+	strlcpy(vimc->mdev.model, VIMC_MDEV_MODEL_NAME,
+		sizeof(vimc->mdev.model));
+	vimc->mdev.dev = &pdev->dev;
+	media_device_init(&vimc->mdev);
+
+	/* Create vimc topology */
+	ret = vimc_device_register(vimc);
+	if (ret) {
+		dev_err(vimc->mdev.dev,
+			"vimc device registration failed (err=%d)\n", ret);
+		kfree(vimc);
+		return ret;
+	}
+
+	/* Link the topology object with the platform device object */
+	platform_set_drvdata(pdev, vimc);
+
+	return 0;
+}
+
+static int vimc_remove(struct platform_device *pdev)
+{
+	struct vimc_device *vimc = platform_get_drvdata(pdev);
+
+	/* Destroy all the topology */
+	vimc_device_unregister(vimc);
+	kfree(vimc);
+
+	return 0;
+}
+
+static void vimc_dev_release(struct device *dev)
+{
+}
+
+static struct platform_device vimc_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,
+	},
+};
+
+static int __init vimc_init(void)
+{
+	int ret;
+
+	ret = platform_device_register(&vimc_pdev);
+	if (ret) {
+		dev_err(&vimc_pdev.dev,
+			"platform device registration failed (err=%d)\n", ret);
+		return ret;
+	}
+
+	ret = platform_driver_register(&vimc_pdrv);
+	if (ret) {
+		dev_err(&vimc_pdev.dev,
+			"platform driver registration failed (err=%d)\n", ret);
+
+		platform_device_unregister(&vimc_pdev);
+	}
+
+	return ret;
+}
+
+static void __exit vimc_exit(void)
+{
+	platform_driver_unregister(&vimc_pdrv);
+
+	platform_device_unregister(&vimc_pdev);
+}
+
+module_init(vimc_init);
+module_exit(vimc_exit);
+
+MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC)");
+MODULE_AUTHOR("Helen Fornazier <helen.fornazier@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/vimc/vimc-core.h b/drivers/media/platform/vimc/vimc-core.h
new file mode 100644
index 0000000..4525d23
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-core.h
@@ -0,0 +1,112 @@
+/*
+ * vimc-core.h Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIMC_CORE_H_
+#define _VIMC_CORE_H_
+
+#include <linux/slab.h>
+#include <media/v4l2-device.h>
+
+/**
+ * struct vimc_pix_map - maps media bus code with v4l2 pixel format
+ *
+ * @code:		media bus format code defined by MEDIA_BUS_FMT_* macros
+ * @bbp:		number of bytes each pixel occupies
+ * @pixelformat:	pixel format devined by V4L2_PIX_FMT_* macros
+ *
+ * Struct which matches the MEDIA_BUS_FMT_* codes with the corresponding
+ * V4L2_PIX_FMT_* fourcc pixelformat and its bytes per pixel (bpp)
+ */
+struct vimc_pix_map {
+	unsigned int code;
+	unsigned int bpp;
+	u32 pixelformat;
+};
+
+/**
+ * struct vimc_ent_device - core struct that represents a node in the topology
+ *
+ * @ent:		the pointer to struct media_entity for the node
+ * @pads:		the list of pads of the node
+ * @destroy:		callback to destroy the node
+ * @process_frame:	callback send a frame to that node
+ *
+ * Each node of the topology must create a vimc_ent_device struct. Depending on
+ * the node it will be of an instance of v4l2_subdev or video_device struct
+ * where both contains a struct media_entity.
+ * Those structures should embedded the vimc_ent_device struct through
+ * v4l2_set_subdevdata() and video_set_drvdata() respectivaly, allowing the
+ * vimc_ent_device struct to be retrieved from the corresponding struct
+ * media_entity
+ */
+struct vimc_ent_device {
+	struct media_entity *ent;
+	struct media_pad *pads;
+	void (*destroy)(struct vimc_ent_device *);
+	void (*process_frame)(struct vimc_ent_device *ved,
+			      struct media_pad *sink, const void *frame);
+};
+
+/**
+ * vimc_propagate_frame - propagate a frame through the topology
+ *
+ * @src:	the source pad where the frame is being originated
+ * @frame:	the frame to be propagated
+ *
+ * This function will call the process_frame callback from the vimc_ent_device
+ * struct of the nodes directly connected to the @src pad
+ */
+int vimc_propagate_frame(struct media_pad *src, const void *frame);
+
+/**
+ * vimc_pads_init - initialize pads
+ *
+ * @num_pads:	number of pads to initialize
+ * @pads_flags:	flags to use in each pad
+ *
+ * Helper functions to allocate/initialize pads
+ */
+struct media_pad *vimc_pads_init(u16 num_pads,
+				 const unsigned long *pads_flag);
+
+/**
+ * vimc_pads_cleanup - free pads
+ *
+ * @pads: pointer to the pads
+ *
+ * Helper function to free the pads initialized with vimc_pads_init
+ */
+static inline void vimc_pads_cleanup(struct media_pad *pads)
+{
+	kfree(pads);
+}
+
+/**
+ * vimc_pix_map_by_code - get vimc_pix_map struct by media bus code
+ *
+ * @code:		media bus format code defined by MEDIA_BUS_FMT_* macros
+ */
+const struct vimc_pix_map *vimc_pix_map_by_code(u32 code);
+
+/**
+ * vimc_pix_map_by_pixelformat - get vimc_pix_map struct by v4l2 pixel format
+ *
+ * @pixelformat:	pixel format devined by V4L2_PIX_FMT_* macros
+ */
+const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat);
+
+#endif
diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c
new file mode 100644
index 0000000..591f6a4
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-sensor.c
@@ -0,0 +1,276 @@
+/*
+ * vimc-sensor.c Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/freezer.h>
+#include <linux/kthread.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/vmalloc.h>
+#include <media/v4l2-subdev.h>
+
+#include "vimc-sensor.h"
+
+struct vimc_sen_device {
+	struct vimc_ent_device ved;
+	struct v4l2_subdev sd;
+	struct task_struct *kthread_sen;
+	u8 *frame;
+	/* The active format */
+	struct v4l2_mbus_framefmt mbus_format;
+	int frame_size;
+};
+
+static int vimc_sen_enum_mbus_code(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_pad_config *cfg,
+				   struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct vimc_sen_device *vsen =
+				container_of(sd, struct vimc_sen_device, sd);
+
+	/* TODO: Add support for other codes */
+	if (code->index)
+		return -EINVAL;
+
+	code->code = vsen->mbus_format.code;
+
+	return 0;
+}
+
+static int vimc_sen_enum_frame_size(struct v4l2_subdev *sd,
+				    struct v4l2_subdev_pad_config *cfg,
+				    struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct vimc_sen_device *vsen =
+				container_of(sd, struct vimc_sen_device, sd);
+
+	/* TODO: Add support to other formats */
+	if (fse->index)
+		return -EINVAL;
+
+	/* TODO: Add support for other codes */
+	if (fse->code != vsen->mbus_format.code)
+		return -EINVAL;
+
+	fse->min_width = vsen->mbus_format.width;
+	fse->max_width = vsen->mbus_format.width;
+	fse->min_height = vsen->mbus_format.height;
+	fse->max_height = vsen->mbus_format.height;
+
+	return 0;
+}
+
+static int vimc_sen_get_fmt(struct v4l2_subdev *sd,
+			    struct v4l2_subdev_pad_config *cfg,
+			    struct v4l2_subdev_format *format)
+{
+	struct vimc_sen_device *vsen =
+				container_of(sd, struct vimc_sen_device, sd);
+
+	format->format = vsen->mbus_format;
+
+	return 0;
+}
+
+static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = {
+	.enum_mbus_code		= vimc_sen_enum_mbus_code,
+	.enum_frame_size	= vimc_sen_enum_frame_size,
+	.get_fmt		= vimc_sen_get_fmt,
+	/* TODO: Add support to other formats */
+	.set_fmt		= vimc_sen_get_fmt,
+};
+
+/* media operations */
+static const struct media_entity_operations vimc_sen_mops = {
+	.link_validate = v4l2_subdev_link_validate,
+};
+
+static int vimc_thread_sen(void *data)
+{
+	struct vimc_sen_device *vsen = data;
+	unsigned int i;
+
+	set_freezable();
+	set_current_state(TASK_UNINTERRUPTIBLE);
+
+	for (;;) {
+		try_to_freeze();
+		if (kthread_should_stop())
+			break;
+
+		memset(vsen->frame, 100, vsen->frame_size);
+
+		/* Send the frame to all source pads */
+		for (i = 0; i < vsen->sd.entity.num_pads; i++)
+			vimc_propagate_frame(&vsen->sd.entity.pads[i],
+					     vsen->frame);
+
+		/* 60 frames per second */
+		schedule_timeout(HZ/60);
+	}
+
+	return 0;
+}
+
+static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct vimc_sen_device *vsen =
+				container_of(sd, struct vimc_sen_device, sd);
+	int ret;
+
+	if (enable) {
+		const struct vimc_pix_map *vpix;
+
+		if (vsen->kthread_sen)
+			return -EINVAL;
+
+		/* Calculate the frame size */
+		vpix = vimc_pix_map_by_code(vsen->mbus_format.code);
+		vsen->frame_size = vsen->mbus_format.width * vpix->bpp *
+				   vsen->mbus_format.height;
+
+		/*
+		 * Allocate the frame buffer. Use vmalloc to be able to
+		 * allocate a large amount of memory
+		 */
+		vsen->frame = vmalloc(vsen->frame_size);
+		if (!vsen->frame)
+			return -ENOMEM;
+
+		/* Initialize the image generator thread */
+		vsen->kthread_sen = kthread_run(vimc_thread_sen, vsen, "%s-sen",
+						vsen->sd.v4l2_dev->name);
+		if (IS_ERR(vsen->kthread_sen)) {
+			dev_err(vsen->sd.v4l2_dev->dev,
+				"%s: kernel_thread() failed\n",	vsen->sd.name);
+			vfree(vsen->frame);
+			vsen->frame = NULL;
+			return PTR_ERR(vsen->kthread_sen);
+		}
+	} else {
+		if (!vsen->kthread_sen)
+			return -EINVAL;
+
+		/* Stop image generator */
+		ret = kthread_stop(vsen->kthread_sen);
+		vsen->kthread_sen = NULL;
+
+		vfree(vsen->frame);
+		vsen->frame = NULL;
+		return ret;
+	}
+
+	return 0;
+}
+
+struct v4l2_subdev_video_ops vimc_sen_video_ops = {
+	.s_stream = vimc_sen_s_stream,
+};
+
+static const struct v4l2_subdev_ops vimc_sen_ops = {
+	.pad = &vimc_sen_pad_ops,
+	.video = &vimc_sen_video_ops,
+};
+
+static void vimc_sen_destroy(struct vimc_ent_device *ved)
+{
+	struct vimc_sen_device *vsen =
+				container_of(ved, struct vimc_sen_device, ved);
+
+	v4l2_device_unregister_subdev(&vsen->sd);
+	media_entity_cleanup(ved->ent);
+	kfree(vsen);
+}
+
+struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag)
+{
+	struct vimc_sen_device *vsen;
+	unsigned int i;
+	int ret;
+
+	/* NOTE: a sensor node may be created with more then one pad */
+	if (!name || !num_pads || !pads_flag)
+		return ERR_PTR(-EINVAL);
+
+	/* check if all pads are sources */
+	for (i = 0; i < num_pads; i++)
+		if (!(pads_flag[i] & MEDIA_PAD_FL_SOURCE))
+			return ERR_PTR(-EINVAL);
+
+	/* Allocate the vsen struct */
+	vsen = kzalloc(sizeof(*vsen), GFP_KERNEL);
+	if (!vsen)
+		return ERR_PTR(-ENOMEM);
+
+	/* Allocate the pads */
+	vsen->ved.pads = vimc_pads_init(num_pads, pads_flag);
+	if (IS_ERR(vsen->ved.pads)) {
+		ret = PTR_ERR(vsen->ved.pads);
+		goto err_free_vsen;
+	}
+
+	/* Fill the vimc_ent_device struct */
+	vsen->ved.destroy = vimc_sen_destroy;
+	vsen->ved.ent = &vsen->sd.entity;
+
+	/* Initialize the subdev */
+	v4l2_subdev_init(&vsen->sd, &vimc_sen_ops);
+	vsen->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+	vsen->sd.entity.ops = &vimc_sen_mops;
+	vsen->sd.owner = THIS_MODULE;
+	strlcpy(vsen->sd.name, name, sizeof(vsen->sd.name));
+	v4l2_set_subdevdata(&vsen->sd, &vsen->ved);
+
+	/* Expose this subdev to user space */
+	vsen->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	/* Initialize the media entity */
+	ret = media_entity_pads_init(&vsen->sd.entity,
+				     num_pads, vsen->ved.pads);
+	if (ret)
+		goto err_clean_pads;
+
+	/* Set the active frame format (this is hardcoded for now) */
+	vsen->mbus_format.width = 640;
+	vsen->mbus_format.height = 480;
+	vsen->mbus_format.code = MEDIA_BUS_FMT_RGB888_1X24;
+	vsen->mbus_format.field = V4L2_FIELD_NONE;
+	vsen->mbus_format.colorspace = V4L2_COLORSPACE_SRGB;
+	vsen->mbus_format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
+	vsen->mbus_format.xfer_func = V4L2_XFER_FUNC_SRGB;
+
+	/* Register the subdev with the v4l2 and the media framework */
+	ret = v4l2_device_register_subdev(v4l2_dev, &vsen->sd);
+	if (ret) {
+		dev_err(vsen->sd.v4l2_dev->dev,
+			"%s: subdev register failed (err=%d)\n",
+			vsen->sd.name, ret);
+		goto err_clean_m_ent;
+	}
+
+	return &vsen->ved;
+
+err_clean_m_ent:
+	media_entity_cleanup(&vsen->sd.entity);
+err_clean_pads:
+	vimc_pads_cleanup(vsen->ved.pads);
+err_free_vsen:
+	kfree(vsen);
+
+	return ERR_PTR(ret);
+}
diff --git a/drivers/media/platform/vimc/vimc-sensor.h b/drivers/media/platform/vimc/vimc-sensor.h
new file mode 100644
index 0000000..505310e
--- /dev/null
+++ b/drivers/media/platform/vimc/vimc-sensor.h
@@ -0,0 +1,28 @@
+/*
+ * vimc-sensor.h Virtual Media Controller Driver
+ *
+ * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _VIMC_SENSOR_H_
+#define _VIMC_SENSOR_H_
+
+#include "vimc-core.h"
+
+struct vimc_ent_device *vimc_sen_create(struct v4l2_device *v4l2_dev,
+					const char *const name,
+					u16 num_pads,
+					const unsigned long *pads_flag);
+
+#endif
-- 
2.7.4

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

end of thread, other threads:[~2017-04-07 17:55 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-05-31 15:02 [PATCH v4] [media] vimc: Virtual Media Controller core, capture and sensor Helen Koike
2016-06-12  1:59 ` kbuild test robot
2016-06-12  2:11 ` kbuild test robot
2016-07-01 12:39 ` Hans Verkuil
2016-08-17 22:08   ` Helen Koike
2016-08-17 22:09   ` [PATCH v5] " Helen Koike
2016-08-22 10:57     ` Hans Verkuil
2016-09-04 20:02       ` [PATCH v6] " Helen Koike
2016-09-06  7:33         ` Hans Verkuil
2016-09-12 12:53           ` [PATCH] [media] MAINTAINERS: add vimc entry Helen Koike
2017-01-10 19:54         ` [PATCH v6] [media] vimc: Virtual Media Controller core, capture and sensor Laurent Pinchart
2017-01-11  1:30           ` Helen Koike
2017-03-10 13:08             ` Hans Verkuil
2017-03-10 13:42               ` Helen Koike
2017-03-25 17:11                 ` [PATCH v7] " Helen Koike
2017-03-25 23:04                   ` Helen Koike
2017-03-26 13:31                   ` Sakari Ailus
2017-03-27 15:19                     ` Helen Koike
2017-03-27 18:09                       ` Mauro Carvalho Chehab
2017-03-28 10:00                         ` Hans Verkuil
2017-03-28 11:38                           ` Mauro Carvalho Chehab
2017-03-28 20:37                             ` Sakari Ailus
2017-03-29  7:49                               ` Hans Verkuil
2017-03-29  8:46                                 ` Sakari Ailus
     [not found]                               ` <CAKQmDh9QoW7qnai=i68HBBbkLBa+Ni5K7WKeYDLONjYeyhHH0A@mail.gmail.com>
2017-03-29  8:02                                 ` Hans Verkuil
2017-03-28 14:23                           ` Sakari Ailus
2017-03-28 15:25                             ` Mauro Carvalho Chehab
2017-03-28 20:39                               ` Sakari Ailus
2017-03-29  7:39                             ` Hans Verkuil
2017-03-27  9:00                   ` Hans Verkuil
2017-03-27 13:33                     ` [PATCH v8] " Helen Koike
2017-04-03 22:16                       ` [PATCH v9] " Helen Koike
2017-04-06 14:29                         ` Helen Koike
2017-04-07  9:54                         ` Hans Verkuil
2017-04-07 17:55                           ` [PATCH v10] " Helen Koike
2017-01-25 13:03         ` [PATCH v6] " Sakari Ailus
2017-01-25 15:31           ` Mauro Carvalho Chehab
2017-03-24 22:11           ` Helen Koike
2017-03-26 13:25             ` Sakari Ailus
2016-09-04 20:05       ` [PATCH v5] " Helen Koike
2016-09-05  9:01         ` Hans Verkuil

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.