基于RV1126平台imx291分析 --- media部件注册 rkcif_mipi

Linux v4l2架构学习总链接

rv1126 架构如下

基于RV1126平台imx291分析 --- media部件注册 rkcif_mipi_第1张图片

前面分析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节点,之前的分析只是分析一个。

基于RV1126平台imx291分析 --- media部件注册 rkcif_mipi_第2张图片

你可能感兴趣的:(#,v4l2,video,实例分析)