工作:
kernel\drivers\media\platform\rockchip\cif\dev.c
static int rkcif_plat_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
struct device_node *node = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct rkcif_device *cif_dev;
const struct rkcif_match_data *data;
int ret;
...
rkcif_plat_init(cif_dev, node, data->inf_id);
}
int rkcif_plat_init(struct rkcif_device *cif_dev, struct device_node *node, int inf_id)
{
struct device *dev = cif_dev->dev;
struct v4l2_device *v4l2_dev;
int ret;
...
/* create & register platefom subdev (from of_node) */
ret = rkcif_register_platform_subdevs(cif_dev);
}
static int rkcif_register_platform_subdevs(struct rkcif_device *cif_dev)
{
...
ret = rkcif_register_stream_vdevs(cif_dev, stream_num, false);
...
}
/*注册 rkcif_device ISP平台设备的四个流 。 video节点
参数1 struct rkcif_device ISP平台设备
参数2 4
参数3 true
*/
int rkcif_register_stream_vdevs(struct rkcif_device *dev,
int stream_num,
bool is_multi_input)
{
struct rkcif_stream *stream;
int i, j, ret;
//注册 rkcif_device ISP平台设备的四个流 。分别对应四个video节点
for (i = 0; i < stream_num; i++) {
/*
+---struct rkcif_device {
| struct rkcif_stream stream[RKCIF_MULTI_STREAMS_NUM];
+-----------struct rkcif_device *cifdev;
*/
stream = &dev->stream[i];
stream->cifdev = dev;//每个流 都属于 rkcif_device
/*
初始化,注册四个 video_device 设备。 用于创建和管理V4L2设备的结构
参数1 rkcif 某个流 rkcif_stream
struct rkcif_device {
struct rkcif_stream stream[i]
*/
ret = rkcif_register_stream_vdev(stream, is_multi_input);
if (ret < 0)
goto err;
}
...
}
/*
参数1 rkcif 某个流 rkcif_stream
+---struct rkcif_device {
| struct rkcif_stream stream[RKCIF_MULTI_STREAMS_NUM];
+-----------struct rkcif_device *cifdev;
*/
static int rkcif_register_stream_vdev(struct rkcif_stream *stream,
bool is_multi_input)
{
/*
+---struct rkcif_device {
| struct rkcif_stream stream[RKCIF_MULTI_STREAMS_NUM];
+-----------struct rkcif_device *cifdev;
struct v4l2_device v4l2_dev;
*/
struct rkcif_device *dev = stream->cifdev;
struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
// struct video_device 用于创建和管理V4L2设备的结构
/*
+---struct rkcif_device {
| struct rkcif_stream stream[RKCIF_MULTI_STREAMS_NUM];
+-----------struct rkcif_device *cifdev;
struct rkcif_vdev_node vnode;
struct video_device vdev;
struct v4l2_device *v4l2_dev;
struct v4l2_device v4l2_dev;
*/
struct video_device *vdev = &stream->vnode.vdev;
struct rkcif_vdev_node *node;
int ret = 0;
char *vdev_name;
...
//tp2855_read_reg(client, 0x34, &chip_id);
/*
初始化,注册四个 video_device 设备。 用于创建和管理V4L2设备的结构
+---struct rkcif_device {
| struct rkcif_stream stream[RKCIF_MULTI_STREAMS_NUM];
+-----------struct rkcif_device *cifdev;
struct rkcif_vdev_node vnode;
struct video_device vdev;
const struct v4l2_ioctl_ops *ioctl_ops = rkcif_v4l2_ioctl_ops
const struct v4l2_file_operations *fops = rkcif_fops
struct v4l2_device *v4l2_dev;---+
struct v4l2_device v4l2_dev;-----------+
*/
//vdev_name = CIF_VIDEODEVICE_NAME;//"stream_cif"
//如 "stream_cif"
strlcpy(vdev->name, vdev_name, sizeof(vdev->name));
node = vdev_to_node(vdev);
mutex_init(&node->vlock);
vdev->ioctl_ops = &rkcif_v4l2_ioctl_ops;
vdev->release = video_device_release_empty;
vdev->fops = &rkcif_fops;
vdev->minor = -1;
vdev->v4l2_dev = v4l2_dev;
vdev->lock = &node->vlock;
vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
V4L2_CAP_STREAMING;
video_set_drvdata(vdev, stream);
vdev->vfl_dir = VFL_DIR_RX;
node->pad.flags = MEDIA_PAD_FL_SINK;
rkcif_init_vb2_queue(&node->buf_queue, stream,
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
vdev->queue = &node->buf_queue;
//注册
/*
参数
+---struct rkcif_device {
| struct rkcif_stream stream[RKCIF_MULTI_STREAMS_NUM];
+-----------struct rkcif_device *cifdev;
struct rkcif_vdev_node vnode;
struct video_device vdev;
*/
ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
if (ret < 0) {
v4l2_err(v4l2_dev,
"video_register_device failed with error %d\n", ret);
return ret;
}
video_register_device() 工作
/*
绑定 多媒体设备media_device---------硬件设备模块entity------------硬件设备端口 pads
struct rkcif_device
struct rkcif_stream
struct rkcif_vdev_node vnode;
struct video_device vdev;
struct media_entity entity;
struct media_pad pad;
*/
ret = media_entity_pads_init(&vdev->entity, 1, &node->pad);
return 0;
unreg:
video_unregister_device(vdev);
return ret;
}
media_entity_pads_init() 工作
所以整理后 每个流 都有如下关系状态
分析:
static inline int __must_check video_register_device(struct video_device *vdev,
enum vfl_devnode_type type,
int nr)
{
/*
参数
+---struct rkcif_device {
| struct rkcif_stream stream[RKCIF_MULTI_STREAMS_NUM];
+-----------struct rkcif_device *cifdev;
struct rkcif_vdev_node vnode;
struct video_device vdev;
VFL_TYPE_GRABBER
-1
1
+---struct rkcif_device {
| struct rkcif_stream stream[RKCIF_MULTI_STREAMS_NUM];
+-----------struct rkcif_device *cifdev;
struct rkcif_vdev_node vnode;
struct video_device vdev;
const struct v4l2_file_operations *fops = rkcif_fops
struct module *owner;//指向拥有这个结构的模块的指针,理解为 vdev 地址
*/
return __video_register_device(vdev, type, nr, 1, vdev->fops->owner);
}
kernel\drivers\media\v4l2-core\v4l2-dev.c
static struct video_device *video_devices[VIDEO_NUM_DEVICES];
int __video_register_device(struct video_device *vdev,
enum vfl_devnode_type type,
int nr, int warn_if_nr_in_use,
struct module *owner)
{
int i = 0;
int ret;
int minor_offset = 0;
int minor_cnt = VIDEO_NUM_DEVICES;
const char *name_base;
...
//设备名 相关
switch (type) {
case VFL_TYPE_GRABBER:
name_base = "video";
break;
...
}
...
//struct video_device 设备号 相关
/*
全局 video_devices[] 数组
+---struct rkcif_device { |
| struct rkcif_stream stream[RKCIF_MULTI_STREAMS_NUM]; | 存储,下标:minor
+-----------struct rkcif_device *cifdev; |
struct rkcif_vdev_node vnode; |
struct video_device vdev;--------------------------------------------+
int minor;
*/
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
switch (type) {
case VFL_TYPE_GRABBER:
minor_offset = 0;
minor_cnt = 64;
break;
...
}
#endif
/* Pick a device node number */
mutex_lock(&videodev_lock);
nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt);
...
vdev->minor = i + minor_offset;
vdev->num = nr;//video 设备号
/* Should not happen since we thought this minor was free */
if (WARN_ON(video_devices[vdev->minor])) {
mutex_unlock(&videodev_lock);
pr_err("video_device not empty!\n");
return -ENFILE;
}
devnode_set(vdev);
vdev->index = get_index(vdev);
//全局 video_devices[] 数组
/*
以 video_device 以minor 为数组下标 !!! 保存当前 video_device 设备
*/
video_devices[vdev->minor] = vdev;
...
//字符设备 相关
/*
全局 video_devices[] 数组
|
+---struct rkcif_device { |
| struct rkcif_stream stream[RKCIF_MULTI_STREAMS_NUM]; |
+-----------struct rkcif_device *cifdev; | 存储,下标:minor
struct rkcif_vdev_node vnode; |
struct video_device vdev; |
int minor;------------------------------------------------+
const struct v4l2_file_operations *fops = rkcif_fops
//指向拥有这个结构的模块的指针
struct module *owner;---------------------------------+
//sysfs name : videoX |
struct cdev *cdev; |
const struct file_operations *ops = v4l2_fops |
struct module *owner;-----------------------------+
*/
vdev->cdev = cdev_alloc();
vdev->cdev->ops = &v4l2_fops;
vdev->cdev->owner = owner;//??
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
...
//注册 video_device中的字符设备 到 sysfs
/* Part 4: register the device with sysfs */
vdev->dev.class = &video_class;
vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
vdev->dev.parent = vdev->dev_parent;
//videoX
dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
ret = device_register(&vdev->dev);
if (ret < 0) {
pr_err("%s: device_register failed\n", __func__);
goto cleanup;
}
...
//至此
/*
全局 video_devices[] 数组
+---struct rkcif_device { |
| struct rkcif_stream stream[RKCIF_MULTI_STREAMS_NUM]; | 存储,下标:minor
+-----------struct rkcif_device *cifdev; |
struct rkcif_vdev_node vnode; |
struct video_device vdev;--------------------------------------------+
int minor;
const struct v4l2_file_operations *fops = rkcif_fops
//指向拥有这个结构的模块的指针
struct module *owner;---------------------------------+
//sysfs name : videoX |
struct cdev *cdev; |
const struct file_operations *ops = v4l2_fops |
struct module *owner;-----------------------------+
*/
//注册 entity
/* Part 5: Register the entity. */
ret = video_register_media_controller(vdev);
...
return ret;
}
EXPORT_SYMBOL(__video_register_device);
kernel\drivers\media\v4l2-core\v4l2-dev.c
video_register_media_controller()工作:
/*
参数
+---struct rkcif_device {
| struct rkcif_stream stream[RKCIF_MULTI_STREAMS_NUM];
+-----------struct rkcif_device *cifdev;
struct rkcif_vdev_node vnode;
struct video_device vdev;
*/
static int video_register_media_controller(struct video_device *vdev)
{
#if defined(CONFIG_MEDIA_CONTROLLER)
u32 intf_type;
int ret;
/* 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 可以认为就是rkcif_mipi的entity
obj_type表示当前entity的类型:
对于video 就是 MEDIA_ENTITY_TYPE_VIDEO_DEVICE
对于subdev 则是 MEDIA_ENTITY_TYPE_V4L2_SUBDEV
function的值 :
对于video - VFL_TYPE_GRABBER
值为 MEDIA_ENT_F_IO_V4L
对于subdev,就需要看情况而定,驱动自己设置
比如subdev是个sensor,则值为 MEDIA_ENT_F_CAM_SENSOR
比如subdev是个flash,则值为 MEDIA_ENT_F_FLASH
比如subdev是个连接部件,则值为 MEDA_ENT_F_VID_IF_BRIDGE
比如...
*/
vdev->entity.obj_type = MEDIA_ENTITY_TYPE_VIDEO_DEVICE;
vdev->entity.function = MEDIA_ENT_F_UNKNOWN;
switch (vdev->vfl_type) {
case VFL_TYPE_GRABBER:
intf_type = MEDIA_INTF_T_V4L_VIDEO;
vdev->entity.function = MEDIA_ENT_F_IO_V4L;
break;
...
}
// MEDIA_ENT_F_IO_V4L
if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN) {
//如 "stream_cif"
/*
+---struct rkcif_device {
| struct rkcif_stream stream[RKCIF_MULTI_STREAMS_NUM];
+-----------struct rkcif_device *cifdev;
struct rkcif_vdev_node vnode;
struct video_device vdev;
char name[32];--------------------+
struct media_entity entity; | 如 "stream_cif"
const char *name; ------------+
*/
vdev->entity.name = vdev->name;
/* Needed just for backward compatibility with legacy MC API */
vdev->entity.info.dev.major = VIDEO_MAJOR;
vdev->entity.info.dev.minor = vdev->minor;
/*
1
+---struct rkcif_device {
| struct rkcif_stream stream[RKCIF_MULTI_STREAMS_NUM];
+-----------struct rkcif_device *cifdev;
struct rkcif_vdev_node vnode;
struct video_device vdev;
+---------------struct v4l2_device *v4l2_dev;
+---struct v4l2_device v4l2_dev;
struct media_device *mdev;
2
+---struct rkcif_device {
| struct rkcif_stream stream[RKCIF_MULTI_STREAMS_NUM];
+-----------struct rkcif_device *cifdev;
struct rkcif_vdev_node vnode;
struct video_device vdev;
struct media_entity entity;
+---------------struct v4l2_device *v4l2_dev;
+---struct v4l2_device v4l2_dev;
struct media_device *mdev;
*/
ret = media_device_register_entity(vdev->v4l2_dev->mdev,
&vdev->entity);
if (ret < 0) {
pr_warn("%s: media_device_register_entity failed\n",
__func__);
return ret;
}
}
.....
/* FIXME: how to create the other interface links? */
#endif
return 0;
}
\kernel\drivers\media\media-device.c
media_device_register_entity() 工作
/**
向多媒体设备 注册一个 模块entity
* media_device_register_entity - Register an entity with a media device
* @mdev: The media device
* @entity: The entity
*/
/*
1
+---struct rkcif_device {
| struct rkcif_stream stream[RKCIF_MULTI_STREAMS_NUM];
+-----------struct rkcif_device *cifdev;
struct rkcif_vdev_node vnode;
struct video_device vdev;
+---------------struct v4l2_device *v4l2_dev;
+---struct v4l2_device v4l2_dev;
struct media_device *mdev;
2
+---struct rkcif_device {
| struct rkcif_stream stream[RKCIF_MULTI_STREAMS_NUM];
+-----------struct rkcif_device *cifdev;
struct rkcif_vdev_node vnode;
struct video_device vdev;
struct media_entity entity;
+---------------struct v4l2_device *v4l2_dev;
+---struct v4l2_device v4l2_dev;
struct media_device *mdev;
*/
int __must_check media_device_register_entity(struct media_device *mdev,
struct media_entity *entity)
{
struct media_entity_notify *notify, *next;
unsigned int i;
int ret;
if (entity->function == MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN ||
entity->function == MEDIA_ENT_F_UNKNOWN)
dev_warn(mdev->dev,
"Entity type for entity %s was not initialized!\n",
entity->name);
/* Warn if we apparently re-register an entity */
/*
+---struct rkcif_device {
| struct rkcif_stream stream[RKCIF_MULTI_STREAMS_NUM];
+-----------struct rkcif_device *cifdev;
struct rkcif_vdev_node vnode;
struct video_device vdev;
struct media_entity entity;
struct media_gobj graph_obj;
struct media_device *mdev;----+
+---------------struct v4l2_device *v4l2_dev; |
+---struct v4l2_device v4l2_dev; |
struct media_device *mdev;--------------------+
*/
WARN_ON(entity->graph_obj.mdev != NULL);
entity->graph_obj.mdev = mdev;
INIT_LIST_HEAD(&entity->links);
entity->num_links = 0;
entity->num_backlinks = 0;
ret = ida_alloc_min(&mdev->entity_internal_idx, 1, GFP_KERNEL);
if (ret < 0)
return ret;
entity->internal_idx = ret;
mutex_lock(&mdev->graph_mutex);
mdev->entity_internal_idx_max =
max(mdev->entity_internal_idx_max, entity->internal_idx);
/* Initialize media_gobj embedded at the entity */
/*
1
+---struct rkcif_device {
| struct rkcif_stream stream[RKCIF_MULTI_STREAMS_NUM];
+-----------struct rkcif_device *cifdev;
struct rkcif_vdev_node vnode;
struct video_device vdev;
+---------------struct v4l2_device *v4l2_dev;
+---struct v4l2_device v4l2_dev;
struct media_device *mdev;
3
+---struct rkcif_device {
| struct rkcif_stream stream[RKCIF_MULTI_STREAMS_NUM];
+-----------struct rkcif_device *cifdev;
struct rkcif_vdev_node vnode;
struct video_device vdev;
struct media_entity entity;
struct media_gobj graph_obj;
工作:
+---struct rkcif_device {
| struct rkcif_stream stream[RKCIF_MULTI_STREAMS_NUM];
+-----------struct rkcif_device *cifdev;
struct rkcif_vdev_node vnode;
struct video_device vdev;
struct media_entity entity;//当前 video 模块
struct media_gobj graph_obj;
struct list_head list;-------+
+---------------struct v4l2_device *v4l2_dev; |
+---struct v4l2_device v4l2_dev; | 挂载到多媒体的模块entities链表
//多媒体设备 |
struct media_device *mdev; |
struct list_head entities; ----------------+
*/
media_gobj_create(mdev, MEDIA_GRAPH_ENTITY, &entity->graph_obj);
/*
工作:
+---struct rkcif_device {
| struct rkcif_stream stream[RKCIF_MULTI_STREAMS_NUM];
+-----------struct rkcif_device *cifdev;
struct rkcif_vdev_node vnode;
struct video_device vdev;
struct media_entity entity;//当前 video 模块
struct media_pad pads[i];//包含的 pad
struct media_gobj graph_obj;
struct list_head list;---+
|
+---------------struct v4l2_device *v4l2_dev; |
+---struct v4l2_device v4l2_dev; | 挂载到多媒体的模块pads链表
//多媒体设备 |
struct media_device *mdev; |
struct list_head pads; --------------------+
*/
/* Initialize objects at the pads */
for (i = 0; i < entity->num_pads; i++)
media_gobj_create(mdev, MEDIA_GRAPH_PAD,
&entity->pads[i].graph_obj);
...
return 0;
}
EXPORT_SYMBOL_GPL(media_device_register_entity);
/*
1
+---struct rkcif_device {
| struct rkcif_stream stream[RKCIF_MULTI_STREAMS_NUM];
+-----------struct rkcif_device *cifdev;
struct rkcif_vdev_node vnode;
struct video_device vdev;
+---------------struct v4l2_device *v4l2_dev;
+---struct v4l2_device v4l2_dev;
struct media_device *mdev;
+---struct rkcif_device {
| struct rkcif_stream stream[RKCIF_MULTI_STREAMS_NUM];
+-----------struct rkcif_device *cifdev;
struct rkcif_vdev_node vnode;
struct video_device vdev;
struct media_entity entity;
struct media_gobj graph_obj;
*/
void media_gobj_create(struct media_device *mdev,
enum media_gobj_type type,
struct media_gobj *gobj)
{
BUG_ON(!mdev);
gobj->mdev = mdev;
/* Create a per-type unique object ID */
gobj->id = media_gobj_gen_id(type, ++mdev->id);
/*
+---struct rkcif_device {
| struct rkcif_stream stream[RKCIF_MULTI_STREAMS_NUM];
+-----------struct rkcif_device *cifdev;
struct rkcif_vdev_node vnode;
struct video_device vdev;
struct media_entity entity;
struct media_gobj graph_obj;
struct list_head list;-------+
+---------------struct v4l2_device *v4l2_dev; |
+---struct v4l2_device v4l2_dev; | 挂载到多媒体的模块entities链表
struct media_device *mdev; |
struct list_head entities; ----------------+
*/
switch (type) {
case MEDIA_GRAPH_ENTITY://!!!
list_add_tail(&gobj->list, &mdev->entities);
break;
case MEDIA_GRAPH_PAD:
list_add_tail(&gobj->list, &mdev->pads);
break;
case MEDIA_GRAPH_LINK:
list_add_tail(&gobj->list, &mdev->links);
break;
case MEDIA_GRAPH_INTF_DEVNODE:
list_add_tail(&gobj->list, &mdev->interfaces);
break;
}
mdev->topology_version++;
dev_dbg_obj(__func__, gobj);
}
/*
struct rkcif_device
struct rkcif_stream
struct rkcif_vdev_node vnode;
struct video_device vdev;
struct media_entity entity;
struct media_pad pad;
num_pads==1
工作:
struct rkcif_device
struct rkcif_stream
struct rkcif_vdev_node vnode;
struct video_device vdev;
+-----------struct media_entity entity;
| struct media_gobj graph_obj;
绑定 entity -- pad | struct media_device *mdev;--+
| +-----------struct media_pad *pads;//1个pad |
| +---struct media_pad pad; |
+-----------struct media_entity *entity; |
struct media_gobj graph_obj |
struct media_device *mdev;------+ 绑定 多媒体 -- entity
struct list_head list;--+ | 绑定 多媒体 -- pad
struct v4l2_device v4l2_dev; | |
//多媒体设备 | |
struct media_device *mdev;-------------|----+
struct list_head entities; |
struct list_head pads;------------+
*/
int media_entity_pads_init(struct media_entity *entity, u16 num_pads,
struct media_pad *pads)
{
struct media_device *mdev = entity->graph_obj.mdev;
unsigned int i;
if (num_pads >= MEDIA_ENTITY_MAX_PADS)
return -E2BIG;
/*
struct rkcif_device
struct rkcif_stream
struct rkcif_vdev_node vnode;
struct video_device vdev;
+-----------struct media_entity entity;
| struct media_gobj graph_obj;
绑定 entity--pad | struct media_device *mdev;
| +-----------struct media_pad *pads;
| +---struct media_pad pad;
+-----------struct media_entity *entity;
*/
entity->num_pads = num_pads;
entity->pads = pads;
if (mdev)
mutex_lock(&mdev->graph_mutex);
// num_pads == 1
for (i = 0; i < num_pads; i++) {
pads[i].entity = entity;
pads[i].index = i;
if (mdev){
/*
工作
struct rkcif_device
struct rkcif_stream
struct rkcif_vdev_node vnode;
struct video_device vdev;
+-----------struct media_entity entity;
| struct media_gobj graph_obj;
绑定 entity -- pad | struct media_device *mdev;--+
| +-----------struct media_pad *pads;//1个pad |
| +---struct media_pad pad; |
+-----------struct media_entity *entity; |
struct media_gobj graph_obj |
struct media_device *mdev;------+ 绑定 多媒体 -- entity
struct list_head list;--+ | 绑定 多媒体 -- pad
struct v4l2_device v4l2_dev; | |
//多媒体设备 | |
struct media_device *mdev;-------------|----+
struct list_head entities; |
struct list_head pads;------------+
*/
media_gobj_create(mdev, MEDIA_GRAPH_PAD,
&entity->pads[i].graph_obj);
}
}
if (mdev)
mutex_unlock(&mdev->graph_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(media_entity_pads_init);