rv1126 架构如下
前面分析subdev的时候,是从imx291开始的,这里分析media部件,换个方向,从rkcif_mipi开始
这里有一部分内容需要去看subdev的分析
rkcif_mipi对于media的操作有2个地方
第一处就是注册video的时候
video_register_device()-> video_register_media_controller()
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;
case VFL_TYPE_VBI:
intf_type = MEDIA_INTF_T_V4L_VBI;
vdev->entity.function = MEDIA_ENT_F_IO_VBI;
break;
case VFL_TYPE_SDR:
intf_type = MEDIA_INTF_T_V4L_SWRADIO;
vdev->entity.function = MEDIA_ENT_F_IO_SWRADIO;
break;
case VFL_TYPE_TOUCH:
intf_type = MEDIA_INTF_T_V4L_TOUCH;
vdev->entity.function = MEDIA_ENT_F_IO_V4L;
break;
case VFL_TYPE_RADIO:
intf_type = MEDIA_INTF_T_V4L_RADIO;
/*
* Radio doesn't have an entity at the V4L2 side to represent
* radio input or output. Instead, the audio input/output goes
* via either physical wires or ALSA.
*/
break;
case VFL_TYPE_SUBDEV:
intf_type = MEDIA_INTF_T_V4L_SUBDEV;
/* Entity will be created via v4l2_device_register_subdev() */
break;
default:
return 0;
}
if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN) {
/*
* entity.name 赋值
*/
vdev->entity.name = vdev->name;
/* Needed just for backward compatibility with legacy MC API */
/*
* 这2句没看明白具体要做什么,后面遇到再说吧
*/
vdev->entity.info.dev.major = VIDEO_MAJOR;
vdev->entity.info.dev.minor = vdev->minor;
/*
* 可不可以认为这个函数的作用就是就entity注册到media中
* 或者说entity关联到media中
* 具体分析在下面
*/
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;
}
}
....
#endif
return 0;
}
video_register_device()-> video_register_media_controller() -> media_device_register_entity()
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 */
WARN_ON(entity->graph_obj.mdev != NULL);
/*
* entity通过graph_obj.medv关联到media
* 初始化links个数为0
* 其中links是所有的link数量包括前向和反向已经使能是失能的
* backlinks记录的是反向的links数量
* 那么正向的就是 links - backlinks ?
*/
entity->graph_obj.mdev = mdev;
INIT_LIST_HEAD(&entity->links);
entity->num_links = 0;
entity->num_backlinks = 0;
/*
* 这里是得到一个编号
* internal_idx是entity内部唯一编号,这个编号
*/
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);
/*
* media中记录已分配的最大编号
*/
mdev->entity_internal_idx_max =
max(mdev->entity_internal_idx_max, entity->internal_idx);
/* Initialize media_gobj embedded at the entity */
/*
* 将media通过graph_obj挂载到medv->entities链表上
* 详细看下面的分析
*/
media_gobj_create(mdev, MEDIA_GRAPH_ENTITY, &entity->graph_obj);
/* Initialize objects at the pads */
/*
* 根据当前这个情景分析,num_pads为0
* 这里先不分析
*/
for (i = 0; i < entity->num_pads; i++)
media_gobj_create(mdev, MEDIA_GRAPH_PAD,
&entity->pads[i].graph_obj);
/*
* 根据当前这个情景分析,medv->entity_notifier链表上为空
* 这里先不分析
*/
/* invoke entity_notify callbacks */
list_for_each_entry_safe(notify, next, &mdev->entity_notify, list)
notify->notify(entity, notify->notify_data);
/*
* pm是电源相关的
* 详细分析在下面
*/
if (mdev->entity_internal_idx_max
>= mdev->pm_count_walk.ent_enum.idx_max) {
struct media_graph new = { .top = 0 };
/*
* Initialise the new graph walk before cleaning up
* the old one in order not to spoil the graph walk
* object of the media device if graph walk init fails.
*/
ret = media_graph_walk_init(&new, mdev);
if (ret) {
mutex_unlock(&mdev->graph_mutex);
return ret;
}
/*
* 在media_graph_walk_init重新分配了一个空间,地址记录在new中
* media_graph_walk_cleanup将之前申请的内存空间释放
* 释放的原因是,之前的不够用了
*/
media_graph_walk_cleanup(&mdev->pm_count_walk);
/*
* 更新新的pm_count_walk
* 具体的用法后面遇到在分析
*/
mdev->pm_count_walk = new;
}
mutex_unlock(&mdev->graph_mutex);
return 0;
}
video_register_device()-> video_register_media_controller() -> media_device_register_entity() -> media_gobj_create()
void media_gobj_create(struct media_device *mdev,
enum media_gobj_type type,
struct media_gobj *gobj)
{
BUG_ON(!mdev);
/*
* gobj是&entity->graph_obj
* 对于gobj,都有一个自己的ID
*/
gobj->mdev = mdev;
/* Create a per-type unique object ID */
gobj->id = media_gobj_gen_id(type, ++mdev->id);
/*
* 对于entity
* 挂载到medv->entities链表上
* 对于pad
* 挂载到medv->pads链表上
* 对于link
* 挂载到medv->links链表上
* 对于interface
* 挂载到medv->interface链表上
*
* 对于整个media来说,应该会有很多的entity,pad,link
* 都会被挂载到media的相关链表上
*/
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;
}
/*
* 用于存储图拓扑版本的单调计数器
* 每次拓扑更改时都应增加
* 拓扑到底是个什么东西呢?增加一个link也算改变拓扑???
*/
mdev->topology_version++;
dev_dbg_obj(__func__, gobj);
}
video_register_device()-> video_register_media_controller() -> media_device_register_entity() -> media_gobj_walk_init()
__must_check int media_graph_walk_init(
struct media_graph *graph, struct media_device *mdev)
{
return media_entity_enum_init(&graph->ent_enum, mdev);
}
static inline __must_check int media_entity_enum_init(
struct media_entity_enum *ent_enum, struct media_device *mdev)
{
return __media_entity_enum_init(ent_enum,
mdev->entity_internal_idx_max + 1);
}
__must_check int __media_entity_enum_init(struct media_entity_enum *ent_enum,
int idx_max)
{
/*
* 加设idx_max值是1
* 64位系统的话,BITS_PER_LONG 为64
* 那么ALIGN后 idx_max值就是64
*/
idx_max = ALIGN(idx_max, BITS_PER_LONG);
/*
* idx_max/ BITS_PER_LONG 计算出有几个long的大小
* ent_enum->bmap作用是什么???
*/
ent_enum->bmap = kcalloc(idx_max / BITS_PER_LONG, sizeof(long),
GFP_KERNEL);
if (!ent_enum->bmap)
return -ENOMEM;
bitmap_zero(ent_enum->bmap, idx_max);
/*
* 记录idx_max的值
*/
ent_enum->idx_max = idx_max;
return 0;
}
可以看到注册video节点的时候,调用了media_device_register_entity,会将对于的entity链接到media的entities链表,同时会初始化media的pm_count_walk,电源相关的东西
所以后面如果其他地方调用了media_device_register_entity,同样会做上面的事情。
第二部分对entity添加pad等部件
rkcif_register_stream_vdev()
static int rkcif_register_stream_vdev(struct rkcif_stream *stream,
bool is_multi_input)
{
...
node->pad.flags = MEDIA_PAD_FL_SINK;
ret = media_entity_pads_init(&vdev->entity, 1, &node->pad);
if (ret < 0)
goto unreg;
...
}
可以看到只有1个pad,且是一个sink pad
rkcif_register_stream_vdev() -> media_entity_pads_init()
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;
/*
* entity中记录pad个数及pad地址
*/
entity->num_pads = num_pads;
entity->pads = pads;
/*
* rkcif_mipi是有mdev的
* 这也是为什么要从rkcif_mipi开始分析
* 因为imx291 mipi-csi等是没有mdev的
* 下面的部分代码就会执行不了
*/
if (mdev)
mutex_lock(&mdev->graph_mutex);
for (i = 0; i < num_pads; i++) {
/*
* 初始化每个pad
* 记录所属的entity,这样entity与pad就是你中有我我中有你了
*/
pads[i].entity = entity;
pads[i].index = i;
if (mdev)
/*
* media_gobj_create之前分析过了
* 主要做4件事
* 1. pad通过graph_obj记录medv
* 2. pad通过graph_obj获取自己的id
* 3. pad链接到mdev->pads链表上
* 4. medv->topology_version++
*/
media_gobj_create(mdev, MEDIA_GRAPH_PAD,
&entity->pads[i].graph_obj);
}
if (mdev)
mutex_unlock(&mdev->graph_mutex);
return 0;
}
可以看到第二部分操作就是给entity添加一个pad,并将pad链接到media pads链表上。
当前的media相关操作就这些
看到这里会疑惑,这就完了?rkcif_mipi的entity和mipi csi的entity怎么连接的?
这里之所以不分析,是因为现在分析的会很突兀,有些不容易理解,后面会再次通过subdev注册的过程找到相关的代码。
画了一个示意图,主意rkcif_mipi会注册4个video节点,之前的分析只是分析一个。