Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4a29b709 authored by Helen Fornazier's avatar Helen Fornazier Committed by Mauro Carvalho Chehab
Browse files

[media] vimc: Subdevices as modules



Change the core structure for adding subdevices in the topology.
Instead of calling the specific create function for each subdevice,
inject a child platform_device with the driver's name.
Each type of node in the topology (sensor, capture, debayer, scaler)
will register a platform_driver with the corresponding name through the
component subsystem.
Implementing a new subdevice type doesn't require vimc-core to be altered.

This facilitates future implementation of dynamic entities, where
hotpluging an entity in the topology is just a matter of
registering/unregistering a platform_device in the system.
It also facilitates other implementations of different nodes without
touching the core code and remove the need of a header file for each
type of node.

Signed-off-by: default avatarHelen Koike <helen.koike@collabora.com>
Signed-off-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@s-opensource.com>
parent 535d296f
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
vimc-objs := vimc-core.o vimc-capture.o vimc-common.o vimc-sensor.o
vimc-objs := vimc-core.o
vimc_capture-objs := vimc-capture.o
vimc_common-objs := vimc-common.o
vimc_sensor-objs := vimc-sensor.o

obj-$(CONFIG_VIDEO_VIMC) += vimc.o
obj-$(CONFIG_VIDEO_VIMC) += vimc.o vimc_capture.o vimc_common.o vimc_sensor.o
+69 −30
Original line number Diff line number Diff line
@@ -15,15 +15,21 @@
 *
 */

#include <linux/component.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-core.h>
#include <media/videobuf2-vmalloc.h>

#include "vimc-capture.h"
#include "vimc-common.h"

#define VIMC_CAP_DRV_NAME "vimc-capture"

struct vimc_cap_device {
	struct vimc_ent_device ved;
	struct video_device vdev;
	struct device *dev;
	struct v4l2_pix_format format;
	struct vb2_queue queue;
	struct list_head buf_list;
@@ -131,7 +137,7 @@ static int vimc_cap_s_fmt_vid_cap(struct file *file, void *priv,

	vimc_cap_try_fmt_vid_cap(file, priv, f);

	dev_dbg(vcap->vdev.v4l2_dev->dev, "%s: format update: "
	dev_dbg(vcap->dev, "%s: format update: "
		"old:%dx%d (0x%x, %d, %d, %d, %d) "
		"new:%dx%d (0x%x, %d, %d, %d, %d)\n", vcap->vdev.name,
		/* old */
@@ -309,8 +315,7 @@ static int vimc_cap_buffer_prepare(struct vb2_buffer *vb)
	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",
		dev_err(vcap->dev, "%s: buffer too small (%lu < %lu)\n",
			vcap->vdev.name, vb2_plane_size(vb, 0), size);
		return -EINVAL;
	}
@@ -335,8 +340,10 @@ static const struct media_entity_operations vimc_cap_mops = {
	.link_validate		= vimc_link_validate,
};

static void vimc_cap_destroy(struct vimc_ent_device *ved)
static void vimc_cap_comp_unbind(struct device *comp, struct device *master,
				 void *master_data)
{
	struct vimc_ent_device *ved = dev_get_drvdata(comp);
	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
						    ved);

@@ -385,42 +392,35 @@ static void vimc_cap_process_frame(struct vimc_ent_device *ved,
	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)
static int vimc_cap_comp_bind(struct device *comp, struct device *master,
			      void *master_data)
{
	struct v4l2_device *v4l2_dev = master_data;
	struct vimc_platform_data *pdata = comp->platform_data;
	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);
		return -ENOMEM;

	/* Allocate the pads */
	vcap->ved.pads = vimc_pads_init(num_pads, pads_flag);
	vcap->ved.pads =
		vimc_pads_init(1, (const unsigned long[1]) {MEDIA_PAD_FL_SINK});
	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.name = pdata->entity_name;
	vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L;
	ret = media_entity_pads_init(&vcap->vdev.entity,
				     num_pads, vcap->ved.pads);
				     1, vcap->ved.pads);
	if (ret)
		goto err_clean_pads;

@@ -441,9 +441,8 @@ struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,

	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);
		dev_err(comp, "%s: vb2 queue init failed (err=%d)\n",
			pdata->entity_name, ret);
		goto err_clean_m_ent;
	}

@@ -459,10 +458,11 @@ struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
				 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;
	vcap->ved.vdev_get_format = vimc_cap_get_format;
	dev_set_drvdata(comp, &vcap->ved);
	vcap->dev = comp;

	/* Initialize the video_device struct */
	vdev = &vcap->vdev;
@@ -475,19 +475,18 @@ struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
	vdev->queue = q;
	vdev->v4l2_dev = v4l2_dev;
	vdev->vfl_dir = VFL_DIR_RX;
	strlcpy(vdev->name, name, sizeof(vdev->name));
	strlcpy(vdev->name, pdata->entity_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",
		dev_err(comp, "%s: video register failed (err=%d)\n",
			vcap->vdev.name, ret);
		goto err_release_queue;
	}

	return &vcap->ved;
	return 0;

err_release_queue:
	vb2_queue_release(q);
@@ -498,5 +497,45 @@ struct vimc_ent_device *vimc_cap_create(struct v4l2_device *v4l2_dev,
err_free_vcap:
	kfree(vcap);

	return ERR_PTR(ret);
	return ret;
}

static const struct component_ops vimc_cap_comp_ops = {
	.bind = vimc_cap_comp_bind,
	.unbind = vimc_cap_comp_unbind,
};

static int vimc_cap_probe(struct platform_device *pdev)
{
	return component_add(&pdev->dev, &vimc_cap_comp_ops);
}

static int vimc_cap_remove(struct platform_device *pdev)
{
	component_del(&pdev->dev, &vimc_cap_comp_ops);

	return 0;
}

static struct platform_driver vimc_cap_pdrv = {
	.probe		= vimc_cap_probe,
	.remove		= vimc_cap_remove,
	.driver		= {
		.name	= VIMC_CAP_DRV_NAME,
	},
};

static const struct platform_device_id vimc_cap_driver_ids[] = {
	{
		.name           = VIMC_CAP_DRV_NAME,
	},
	{ }
};

module_platform_driver(vimc_cap_pdrv);

MODULE_DEVICE_TABLE(platform, vimc_cap_driver_ids);

MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Capture");
MODULE_AUTHOR("Helen Mae Koike Fornazier <helen.fornazier@gmail.com>");
MODULE_LICENSE("GPL");
+0 −28
Original line number Diff line number Diff line
/*
 * 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-common.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
+19 −19
Original line number Diff line number Diff line
@@ -15,6 +15,9 @@
 *
 */

#include <linux/init.h>
#include <linux/module.h>

#include "vimc-common.h"

static const struct vimc_pix_map vimc_pix_map_list[] = {
@@ -151,6 +154,7 @@ const struct vimc_pix_map *vimc_pix_map_by_index(unsigned int i)

	return &vimc_pix_map_list[i];
}
EXPORT_SYMBOL_GPL(vimc_pix_map_by_index);

const struct vimc_pix_map *vimc_pix_map_by_code(u32 code)
{
@@ -162,6 +166,7 @@ const struct vimc_pix_map *vimc_pix_map_by_code(u32 code)
	}
	return NULL;
}
EXPORT_SYMBOL_GPL(vimc_pix_map_by_code);

const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat)
{
@@ -173,6 +178,7 @@ const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat)
	}
	return NULL;
}
EXPORT_SYMBOL_GPL(vimc_pix_map_by_pixelformat);

int vimc_propagate_frame(struct media_pad *src, const void *frame)
{
@@ -207,6 +213,7 @@ int vimc_propagate_frame(struct media_pad *src, const void *frame)

	return 0;
}
EXPORT_SYMBOL_GPL(vimc_propagate_frame);

/* Helper function to allocate and initialize pads */
struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag)
@@ -227,6 +234,7 @@ struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag)

	return pads;
}
EXPORT_SYMBOL_GPL(vimc_pads_init);

int vimc_pipeline_s_stream(struct media_entity *ent, int enable)
{
@@ -242,14 +250,8 @@ int vimc_pipeline_s_stream(struct media_entity *ent, int enable)
		/* Start the stream in the subdevice direct connected */
		pad = media_entity_remote_pad(&ent->pads[i]);

		/*
		 * if this is a raw node from vimc-core, then 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)
			continue;
		if (!is_media_entity_v4l2_subdev(pad->entity))
			return -EINVAL;

		sd = media_entity_to_v4l2_subdev(pad->entity);
		ret = v4l2_subdev_call(sd, video, s_stream, enable);
@@ -259,6 +261,7 @@ int vimc_pipeline_s_stream(struct media_entity *ent, int enable)

	return 0;
}
EXPORT_SYMBOL_GPL(vimc_pipeline_s_stream);

static int vimc_get_mbus_format(struct media_pad *pad,
				struct v4l2_subdev_format *fmt)
@@ -301,14 +304,6 @@ int vimc_link_validate(struct media_link *link)
	struct v4l2_subdev_format source_fmt, sink_fmt;
	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;

	ret = vimc_get_mbus_format(link->source, &source_fmt);
	if (ret)
		return ret;
@@ -378,6 +373,7 @@ int vimc_link_validate(struct media_link *link)

	return 0;
}
EXPORT_SYMBOL_GPL(vimc_link_validate);

static const struct media_entity_operations vimc_ent_sd_mops = {
	.link_validate = vimc_link_validate,
@@ -390,8 +386,7 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved,
			 u32 function,
			 u16 num_pads,
			 const unsigned long *pads_flag,
			 const struct v4l2_subdev_ops *sd_ops,
			 void (*sd_destroy)(struct vimc_ent_device *))
			 const struct v4l2_subdev_ops *sd_ops)
{
	int ret;

@@ -401,7 +396,6 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved,
		return PTR_ERR(ved->pads);

	/* Fill the vimc_ent_device struct */
	ved->destroy = sd_destroy;
	ved->ent = &sd->entity;

	/* Initialize the subdev */
@@ -437,6 +431,7 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved,
	vimc_pads_cleanup(ved->pads);
	return ret;
}
EXPORT_SYMBOL_GPL(vimc_ent_sd_register);

void vimc_ent_sd_unregister(struct vimc_ent_device *ved, struct v4l2_subdev *sd)
{
@@ -444,3 +439,8 @@ void vimc_ent_sd_unregister(struct vimc_ent_device *ved, struct v4l2_subdev *sd)
	media_entity_cleanup(ved->ent);
	vimc_pads_cleanup(ved->pads);
}
EXPORT_SYMBOL_GPL(vimc_ent_sd_unregister);

MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Common");
MODULE_AUTHOR("Helen Koike <helen.fornazier@gmail.com>");
MODULE_LICENSE("GPL");
+20 −9
Original line number Diff line number Diff line
/*
 * vimc-ccommon.h Virtual Media Controller Driver
 * vimc-common.h Virtual Media Controller Driver
 *
 * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
 *
@@ -53,6 +53,21 @@ do { \
		(fmt)->xfer_func = V4L2_XFER_FUNC_DEFAULT;		\
} while (0)

/**
 * struct vimc_platform_data - platform data to components
 *
 * @entity_name:	The name of the entity to be created
 *
 * Board setup code will often provide additional information using the device's
 * platform_data field to hold additional information.
 * When injecting a new platform_device in the component system the core needs
 * to provide to the corresponding submodules the name of the entity that should
 * be used when registering the subdevice in the Media Controller system.
 */
struct vimc_platform_data {
	char entity_name[32];
};

/**
 * struct vimc_pix_map - maps media bus code with v4l2 pixel format
 *
@@ -74,7 +89,6 @@ struct vimc_pix_map {
 *
 * @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
 * @vdev_get_format:	callback that returns the current format a pad, used
 *			only when is_media_entity_v4l2_video_device(ent) returns
@@ -91,7 +105,6 @@ struct vimc_pix_map {
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);
	void (*vdev_get_format)(struct vimc_ent_device *ved,
@@ -176,7 +189,6 @@ const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat);
 * @num_pads:	number of pads to initialize
 * @pads_flag:	flags to use in each pad
 * @sd_ops:	pointer to &struct v4l2_subdev_ops.
 * @sd_destroy:	callback to destroy the node
 *
 * Helper function initialize and register the struct vimc_ent_device and struct
 * v4l2_subdev which represents a subdev node in the topology
@@ -188,14 +200,13 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved,
			 u32 function,
			 u16 num_pads,
			 const unsigned long *pads_flag,
			 const struct v4l2_subdev_ops *sd_ops,
			 void (*sd_destroy)(struct vimc_ent_device *));
			 const struct v4l2_subdev_ops *sd_ops);

/**
 * vimc_ent_sd_register - initialize and register a subdev node
 * vimc_ent_sd_unregister - cleanup and unregister a subdev node
 *
 * @ved:	the vimc_ent_device struct to be initialize
 * @sd:		the v4l2_subdev struct to be initialize and registered
 * @ved:	the vimc_ent_device struct to be cleaned up
 * @sd:		the v4l2_subdev struct to be unregistered
 *
 * Helper function cleanup and unregister the struct vimc_ent_device and struct
 * v4l2_subdev which represents a subdev node in the topology
Loading