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

Commit be2fff65 authored by Ezequiel Garcia's avatar Ezequiel Garcia Committed by Mauro Carvalho Chehab
Browse files

media: add helpers for memory-to-memory media controller



A memory-to-memory pipeline device consists in three
entities: two DMA engine and one video processing entities.
The DMA engine entities are linked to a V4L interface.

This commit add a new v4l2_m2m_{un}register_media_controller
API to register this topology.

For instance, a typical mem2mem device topology would
look like this:

Device topology
- entity 1: source (1 pad, 1 link)
            type Node subtype V4L flags 0
	pad0: Source
		-> "proc":1 [ENABLED,IMMUTABLE]

- entity 3: proc (2 pads, 2 links)
            type Node subtype Unknown flags 0
	pad0: Source
		-> "sink":0 [ENABLED,IMMUTABLE]
	pad1: Sink
		<- "source":0 [ENABLED,IMMUTABLE]

- entity 6: sink (1 pad, 1 link)
            type Node subtype V4L flags 0
	pad0: Sink
		<- "proc":0 [ENABLED,IMMUTABLE]

[hans.verkuil@cisco.com: mark interface links as IMMUTABLE]

Suggested-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: default avatarEzequiel Garcia <ezequiel@collabora.com>
Signed-off-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab+samsung@kernel.org>
parent 4d1e4545
Loading
Loading
Loading
Loading
+8 −5
Original line number Original line Diff line number Diff line
@@ -202,7 +202,7 @@ static void v4l2_device_release(struct device *cd)
	mutex_unlock(&videodev_lock);
	mutex_unlock(&videodev_lock);


#if defined(CONFIG_MEDIA_CONTROLLER)
#if defined(CONFIG_MEDIA_CONTROLLER)
	if (v4l2_dev->mdev) {
	if (v4l2_dev->mdev && vdev->vfl_dir != VFL_DIR_M2M) {
		/* Remove interfaces and interface links */
		/* Remove interfaces and interface links */
		media_devnode_remove(vdev->intf_devnode);
		media_devnode_remove(vdev->intf_devnode);
		if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN)
		if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN)
@@ -733,19 +733,22 @@ static void determine_valid_ioctls(struct video_device *vdev)
			BASE_VIDIOC_PRIVATE);
			BASE_VIDIOC_PRIVATE);
}
}


static int video_register_media_controller(struct video_device *vdev, int type)
static int video_register_media_controller(struct video_device *vdev)
{
{
#if defined(CONFIG_MEDIA_CONTROLLER)
#if defined(CONFIG_MEDIA_CONTROLLER)
	u32 intf_type;
	u32 intf_type;
	int ret;
	int ret;


	if (!vdev->v4l2_dev->mdev)
	/* Memory-to-memory devices are more complex and use
	 * their own function to register its mc entities.
	 */
	if (!vdev->v4l2_dev->mdev || vdev->vfl_dir == VFL_DIR_M2M)
		return 0;
		return 0;


	vdev->entity.obj_type = MEDIA_ENTITY_TYPE_VIDEO_DEVICE;
	vdev->entity.obj_type = MEDIA_ENTITY_TYPE_VIDEO_DEVICE;
	vdev->entity.function = MEDIA_ENT_F_UNKNOWN;
	vdev->entity.function = MEDIA_ENT_F_UNKNOWN;


	switch (type) {
	switch (vdev->vfl_type) {
	case VFL_TYPE_GRABBER:
	case VFL_TYPE_GRABBER:
		intf_type = MEDIA_INTF_T_V4L_VIDEO;
		intf_type = MEDIA_INTF_T_V4L_VIDEO;
		vdev->entity.function = MEDIA_ENT_F_IO_V4L;
		vdev->entity.function = MEDIA_ENT_F_IO_V4L;
@@ -994,7 +997,7 @@ int __video_register_device(struct video_device *vdev,
	v4l2_device_get(vdev->v4l2_dev);
	v4l2_device_get(vdev->v4l2_dev);


	/* Part 5: Register the entity. */
	/* Part 5: Register the entity. */
	ret = video_register_media_controller(vdev, type);
	ret = video_register_media_controller(vdev);


	/* Part 6: Activate this minor. The char device can now be used. */
	/* Part 6: Activate this minor. The char device can now be used. */
	set_bit(V4L2_FL_REGISTERED, &vdev->flags);
	set_bit(V4L2_FL_REGISTERED, &vdev->flags);
+190 −0
Original line number Original line Diff line number Diff line
@@ -17,9 +17,11 @@
#include <linux/sched.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/slab.h>


#include <media/media-device.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-v4l2.h>
#include <media/v4l2-mem2mem.h>
#include <media/v4l2-mem2mem.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>
#include <media/v4l2-event.h>


@@ -50,6 +52,17 @@ module_param(debug, bool, 0644);
 * offsets but for different queues */
 * offsets but for different queues */
#define DST_QUEUE_OFF_BASE	(1 << 30)
#define DST_QUEUE_OFF_BASE	(1 << 30)


enum v4l2_m2m_entity_type {
	MEM2MEM_ENT_TYPE_SOURCE,
	MEM2MEM_ENT_TYPE_SINK,
	MEM2MEM_ENT_TYPE_PROC
};

static const char * const m2m_entity_name[] = {
	"source",
	"sink",
	"proc"
};


/**
/**
 * struct v4l2_m2m_dev - per-device context
 * struct v4l2_m2m_dev - per-device context
@@ -60,6 +73,15 @@ module_param(debug, bool, 0644);
 */
 */
struct v4l2_m2m_dev {
struct v4l2_m2m_dev {
	struct v4l2_m2m_ctx	*curr_ctx;
	struct v4l2_m2m_ctx	*curr_ctx;
#ifdef CONFIG_MEDIA_CONTROLLER
	struct media_entity	*source;
	struct media_pad	source_pad;
	struct media_entity	sink;
	struct media_pad	sink_pad;
	struct media_entity	proc;
	struct media_pad	proc_pads[2];
	struct media_intf_devnode *intf_devnode;
#endif


	struct list_head	job_queue;
	struct list_head	job_queue;
	spinlock_t		job_spinlock;
	spinlock_t		job_spinlock;
@@ -594,6 +616,174 @@ int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
}
}
EXPORT_SYMBOL(v4l2_m2m_mmap);
EXPORT_SYMBOL(v4l2_m2m_mmap);


#if defined(CONFIG_MEDIA_CONTROLLER)
void v4l2_m2m_unregister_media_controller(struct v4l2_m2m_dev *m2m_dev)
{
	media_remove_intf_links(&m2m_dev->intf_devnode->intf);
	media_devnode_remove(m2m_dev->intf_devnode);

	media_entity_remove_links(m2m_dev->source);
	media_entity_remove_links(&m2m_dev->sink);
	media_entity_remove_links(&m2m_dev->proc);
	media_device_unregister_entity(m2m_dev->source);
	media_device_unregister_entity(&m2m_dev->sink);
	media_device_unregister_entity(&m2m_dev->proc);
	kfree(m2m_dev->source->name);
	kfree(m2m_dev->sink.name);
	kfree(m2m_dev->proc.name);
}
EXPORT_SYMBOL_GPL(v4l2_m2m_unregister_media_controller);

static int v4l2_m2m_register_entity(struct media_device *mdev,
	struct v4l2_m2m_dev *m2m_dev, enum v4l2_m2m_entity_type type,
	struct video_device *vdev, int function)
{
	struct media_entity *entity;
	struct media_pad *pads;
	char *name;
	unsigned int len;
	int num_pads;
	int ret;

	switch (type) {
	case MEM2MEM_ENT_TYPE_SOURCE:
		entity = m2m_dev->source;
		pads = &m2m_dev->source_pad;
		pads[0].flags = MEDIA_PAD_FL_SOURCE;
		num_pads = 1;
		break;
	case MEM2MEM_ENT_TYPE_SINK:
		entity = &m2m_dev->sink;
		pads = &m2m_dev->sink_pad;
		pads[0].flags = MEDIA_PAD_FL_SINK;
		num_pads = 1;
		break;
	case MEM2MEM_ENT_TYPE_PROC:
		entity = &m2m_dev->proc;
		pads = m2m_dev->proc_pads;
		pads[0].flags = MEDIA_PAD_FL_SINK;
		pads[1].flags = MEDIA_PAD_FL_SOURCE;
		num_pads = 2;
		break;
	default:
		return -EINVAL;
	}

	entity->obj_type = MEDIA_ENTITY_TYPE_BASE;
	if (type != MEM2MEM_ENT_TYPE_PROC) {
		entity->info.dev.major = VIDEO_MAJOR;
		entity->info.dev.minor = vdev->minor;
	}
	len = strlen(vdev->name) + 2 + strlen(m2m_entity_name[type]);
	name = kmalloc(len, GFP_KERNEL);
	if (!name)
		return -ENOMEM;
	snprintf(name, len, "%s-%s", vdev->name, m2m_entity_name[type]);
	entity->name = name;
	entity->function = function;

	ret = media_entity_pads_init(entity, num_pads, pads);
	if (ret)
		return ret;
	ret = media_device_register_entity(mdev, entity);
	if (ret)
		return ret;

	return 0;
}

int v4l2_m2m_register_media_controller(struct v4l2_m2m_dev *m2m_dev,
		struct video_device *vdev, int function)
{
	struct media_device *mdev = vdev->v4l2_dev->mdev;
	struct media_link *link;
	int ret;

	if (!mdev)
		return 0;

	/* A memory-to-memory device consists in two
	 * DMA engine and one video processing entities.
	 * The DMA engine entities are linked to a V4L interface
	 */

	/* Create the three entities with their pads */
	m2m_dev->source = &vdev->entity;
	ret = v4l2_m2m_register_entity(mdev, m2m_dev,
			MEM2MEM_ENT_TYPE_SOURCE, vdev, MEDIA_ENT_F_IO_V4L);
	if (ret)
		return ret;
	ret = v4l2_m2m_register_entity(mdev, m2m_dev,
			MEM2MEM_ENT_TYPE_PROC, vdev, function);
	if (ret)
		goto err_rel_entity0;
	ret = v4l2_m2m_register_entity(mdev, m2m_dev,
			MEM2MEM_ENT_TYPE_SINK, vdev, MEDIA_ENT_F_IO_V4L);
	if (ret)
		goto err_rel_entity1;

	/* Connect the three entities */
	ret = media_create_pad_link(m2m_dev->source, 0, &m2m_dev->proc, 1,
			MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
	if (ret)
		goto err_rel_entity2;

	ret = media_create_pad_link(&m2m_dev->proc, 0, &m2m_dev->sink, 0,
			MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
	if (ret)
		goto err_rm_links0;

	/* Create video interface */
	m2m_dev->intf_devnode = media_devnode_create(mdev,
			MEDIA_INTF_T_V4L_VIDEO, 0,
			VIDEO_MAJOR, vdev->minor);
	if (!m2m_dev->intf_devnode) {
		ret = -ENOMEM;
		goto err_rm_links1;
	}

	/* Connect the two DMA engines to the interface */
	link = media_create_intf_link(m2m_dev->source,
			&m2m_dev->intf_devnode->intf,
			MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
	if (!link) {
		ret = -ENOMEM;
		goto err_rm_devnode;
	}

	link = media_create_intf_link(&m2m_dev->sink,
			&m2m_dev->intf_devnode->intf,
			MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
	if (!link) {
		ret = -ENOMEM;
		goto err_rm_intf_link;
	}
	return 0;

err_rm_intf_link:
	media_remove_intf_links(&m2m_dev->intf_devnode->intf);
err_rm_devnode:
	media_devnode_remove(m2m_dev->intf_devnode);
err_rm_links1:
	media_entity_remove_links(&m2m_dev->sink);
err_rm_links0:
	media_entity_remove_links(&m2m_dev->proc);
	media_entity_remove_links(m2m_dev->source);
err_rel_entity2:
	media_device_unregister_entity(&m2m_dev->proc);
	kfree(m2m_dev->proc.name);
err_rel_entity1:
	media_device_unregister_entity(&m2m_dev->sink);
	kfree(m2m_dev->sink.name);
err_rel_entity0:
	media_device_unregister_entity(m2m_dev->source);
	kfree(m2m_dev->source->name);
	return ret;
	return 0;
}
EXPORT_SYMBOL_GPL(v4l2_m2m_register_media_controller);
#endif

struct v4l2_m2m_dev *v4l2_m2m_init(const struct v4l2_m2m_ops *m2m_ops)
struct v4l2_m2m_dev *v4l2_m2m_init(const struct v4l2_m2m_ops *m2m_ops)
{
{
	struct v4l2_m2m_dev *m2m_dev;
	struct v4l2_m2m_dev *m2m_dev;
+19 −0
Original line number Original line Diff line number Diff line
@@ -47,6 +47,7 @@ struct v4l2_m2m_ops {
	void (*job_abort)(void *priv);
	void (*job_abort)(void *priv);
};
};


struct video_device;
struct v4l2_m2m_dev;
struct v4l2_m2m_dev;


/**
/**
@@ -322,6 +323,24 @@ int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
 */
 */
struct v4l2_m2m_dev *v4l2_m2m_init(const struct v4l2_m2m_ops *m2m_ops);
struct v4l2_m2m_dev *v4l2_m2m_init(const struct v4l2_m2m_ops *m2m_ops);


#if defined(CONFIG_MEDIA_CONTROLLER)
void v4l2_m2m_unregister_media_controller(struct v4l2_m2m_dev *m2m_dev);
int v4l2_m2m_register_media_controller(struct v4l2_m2m_dev *m2m_dev,
			struct video_device *vdev, int function);
#else
static inline void
v4l2_m2m_unregister_media_controller(struct v4l2_m2m_dev *m2m_dev)
{
}

static inline int
v4l2_m2m_register_media_controller(struct v4l2_m2m_dev *m2m_dev,
		struct video_device *vdev, int function)
{
	return 0;
}
#endif

/**
/**
 * v4l2_m2m_release() - cleans up and frees a m2m_dev structure
 * v4l2_m2m_release() - cleans up and frees a m2m_dev structure
 *
 *