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: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Ezequiel Garcia <ezequiel@collabora.com> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
This commit is contained in:
parent
4d1e4545a6
commit
be2fff6563
|
@ -202,7 +202,7 @@ static void v4l2_device_release(struct device *cd)
|
|||
mutex_unlock(&videodev_lock);
|
||||
|
||||
#if defined(CONFIG_MEDIA_CONTROLLER)
|
||||
if (v4l2_dev->mdev) {
|
||||
if (v4l2_dev->mdev && vdev->vfl_dir != VFL_DIR_M2M) {
|
||||
/* Remove interfaces and interface links */
|
||||
media_devnode_remove(vdev->intf_devnode);
|
||||
if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN)
|
||||
|
@ -733,19 +733,22 @@ static void determine_valid_ioctls(struct video_device *vdev)
|
|||
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)
|
||||
u32 intf_type;
|
||||
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;
|
||||
|
||||
vdev->entity.obj_type = MEDIA_ENTITY_TYPE_VIDEO_DEVICE;
|
||||
vdev->entity.function = MEDIA_ENT_F_UNKNOWN;
|
||||
|
||||
switch (type) {
|
||||
switch (vdev->vfl_type) {
|
||||
case VFL_TYPE_GRABBER:
|
||||
intf_type = MEDIA_INTF_T_V4L_VIDEO;
|
||||
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);
|
||||
|
||||
/* 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. */
|
||||
set_bit(V4L2_FL_REGISTERED, &vdev->flags);
|
||||
|
|
|
@ -17,9 +17,11 @@
|
|||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <media/media-device.h>
|
||||
#include <media/videobuf2-v4l2.h>
|
||||
#include <media/v4l2-mem2mem.h>
|
||||
#include <media/v4l2-dev.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/v4l2-fh.h>
|
||||
#include <media/v4l2-event.h>
|
||||
|
||||
|
@ -50,6 +52,17 @@ module_param(debug, bool, 0644);
|
|||
* offsets but for different queues */
|
||||
#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
|
||||
|
@ -60,6 +73,15 @@ module_param(debug, bool, 0644);
|
|||
*/
|
||||
struct v4l2_m2m_dev {
|
||||
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;
|
||||
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);
|
||||
|
||||
#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 *m2m_dev;
|
||||
|
|
|
@ -47,6 +47,7 @@ struct v4l2_m2m_ops {
|
|||
void (*job_abort)(void *priv);
|
||||
};
|
||||
|
||||
struct video_device;
|
||||
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);
|
||||
|
||||
#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
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue