基于RV1126平台imx291分析 --- open及media graph分析

Linux v4l2架构学习总链接

基于RV1126平台imx291分析 --- 先研究media再分析应用调用文章中一开始就被media相关的吓退了。

一星期的时间去研究media相关知识,现在终于可以动手了

补充:

写完了这篇文章后,感觉open相关的其实可以忽略,这里也没有什么多余的open操作,唯一的v4l2_fh_open,vivi open中也分析了

这里更多的是media grpah,通过media graph可以遍历到每一个entity,这里主要学习如何遍历就可以了

rkcif_fh_open()

static int rkcif_fh_open(struct file *filp)
{
	struct video_device *vdev = video_devdata(filp);
	struct rkcif_vdev_node *vnode = vdev_to_node(vdev);
	struct rkcif_stream *stream = to_rkcif_stream(vnode);
	struct rkcif_device *cifdev = stream->cifdev;
	int ret;

	....

	/* Make sure active sensor is valid before .set_fmt() */
	ret = rkcif_update_sensor_info(stream);
	if (ret < 0) {
		v4l2_err(vdev,
			 "update sensor info failed %d\n",
			 ret);
		return ret;
	}
        ...
}

rkcif_fh_open() -> rkcif_update_sensor_info()

int rkcif_update_sensor_info(struct rkcif_stream *stream)
{
	struct rkcif_sensor_info *sensor, *terminal_sensor;
	struct v4l2_subdev *sensor_sd;
	int ret = 0;

        /*
         * 这里的sensor_sd是mipi csi的subdev
         */

	sensor_sd = get_remote_sensor(stream, NULL);
	if (!sensor_sd)
		return -ENODEV;
        ...
}

rkcif_fh_open() -> rkcif_update_sensor_info() -> get_remote_sensor()

struct media_pad *media_entity_remote_pad(const struct media_pad *pad)
{
	struct media_link *link;

        /*
         * 注意这里的links链表上link和backlink
         * 对于rkcif_mipi来说,没有source pad
         * 所以不会有link,只有backlink
         * 也就是说link->sink == pad
         * 返回的link->source 则是mipi csi 的source pad
         */

	list_for_each_entry(link, &pad->entity->links, list) {
		if (!(link->flags & MEDIA_LNK_FL_ENABLED))
			continue;

		if (link->source == pad)
			return link->sink;

		if (link->sink == pad)
			return link->source;
	}

	return NULL;

}

static struct v4l2_subdev *get_remote_sensor(struct rkcif_stream *stream, u16 *index)
{
	struct media_pad *local, *remote;
	struct media_entity *sensor_me;


        /*
         * rkcif_mipi 会产生4个video节点,每个video阶段对应的entity只有1个pad
         */

	local = &stream->vnode.vdev.entity.pads[0];
	if (!local)
		return NULL;
	remote = media_entity_remote_pad(local);
	if (!remote)
		return NULL;

	if (index)
		*index = remote->index;

        /*
         * 上面知道remote是mipi csi的source pad
         * 所以sensor_me就是 mipi csi 的 entity
         */

	sensor_me = remote->entity;

        /*
         * 知道了sensor_me就是 mipi csi 的 entity
         * 那么这里返回的就是mipi csi的subdev
         */

	return media_entity_to_v4l2_subdev(sensor_me);
}

回到rkcif_update_sensor_info继续分析

rkcif_fh_open() -> rkcif_update_sensor_info() -> sd_to_sensor()

int rkcif_update_sensor_info(struct rkcif_stream *stream)
{
	struct rkcif_sensor_info *sensor, *terminal_sensor;
	struct v4l2_subdev *sensor_sd;
	int ret = 0;

	...

	sensor = sd_to_sensor(stream->cifdev, sensor_sd);
	if (!sensor) {
		v4l2_err(&stream->cifdev->v4l2_dev, "get sensor for updating failed!\n");
		return -ENODEV;
	}
        ...
}

static struct rkcif_sensor_info *sd_to_sensor(struct rkcif_device *dev,
					      struct v4l2_subdev *sd)
{
	u32 i;

        /*
         * sensor[] 是怎么来的
         * 具体可以看前面的分析。notify->bound函数
         * 基于RV1126平台imx291分析 --- media部件连接 一
         * https://blog.csdn.net/ldl617/article/details/115720600
         */

	for (i = 0; i < dev->num_sensors; ++i)
		if (dev->sensors[i].sd == sd)
			return &dev->sensors[i];

	if (i == dev->num_sensors) {
		for (i = 0; i < dev->num_sensors; ++i) {
			if (dev->sensors[i].mbus.type == V4L2_MBUS_CCP2)
				return &dev->lvds_subdev.sensor_self;
		}
	}

	return NULL;
}

回到rkcif_update_sensor_info继续分析

rkcif_fh_open() -> rkcif_update_sensor_info()

int rkcif_update_sensor_info(struct rkcif_stream *stream)
{
        ...

        /*
         * sensor->sd是mipi csi的subdev
         */

	ret = v4l2_subdev_call(sensor->sd, video, g_mbus_config,
			       &sensor->mbus);
	if (ret && ret != -ENOIOCTLCMD)
		return ret;

        /*
         * 保存到active_sensor
         */

        stream->cifdev->active_sensor = sensor;
        ...
}

rkcif_fh_open() -> rkcif_update_sensor_info() -> g_mbus_config...

对于g_mbus_config肯定是一系列的调用。因为这里的3个subdev驱动都有这个代码,下面看看是如何依次调用到

首先是mipi csi的g_mbus_config

static struct v4l2_subdev *get_remote_sensor(struct v4l2_subdev *sd)
{
	struct media_pad *local, *remote;
	struct media_entity *sensor_me;

        /*
         * media_entity_remote_pad 和 media_entity_to_v4l2_subdev
         * 上面都分析过,这里就直接口述不再贴代码
         * 之前说过每个entity都可能会有link和backlink
         * 对于这里为什么区sink pad而不是source pad,这里说一下原因
         * -------------------方向--------------------> 
         * imx291 -> mipi csi phy -> mipi csi -> rkcif_mipi
         * 首先要知道我们的目的是向后寻找,也就是找mipi csi phy 
         * 而不是向前找rkcif_mipi
         * 所以这里是sink pad的时候,当前entity的link是不会满足的
         * 只有backlink才满足
         * 只有这样返回的remote的值才是mipi csi phy的source pad
         */

	local = &sd->entity.pads[RK_CSI2_PAD_SINK];
	remote = media_entity_remote_pad(local);
	if (!remote) {
		v4l2_warn(sd, "No link between dphy and sensor\n");
		return NULL;
	}

       
        /*
         * 这里的代码是bug吗?
         * 直接使用remote->entity不香吗?
         * sensor_me是mipi csi phy的entity
         */

	sensor_me = media_entity_remote_pad(local)->entity;

        /*
         * 这里返回的是mipi csi phy的subdev
         */
	return media_entity_to_v4l2_subdev(sensor_me);
}

static int csi2_g_mbus_config(struct v4l2_subdev *sd,
			      struct v4l2_mbus_config *mbus)
{
        /*
         * 得到了mipi csi phy的subdev
         */
        
	struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
	int ret;

        /*
         * 这里会调用mipi csi phy的g_mbus_config
         */

    	ret = v4l2_subdev_call(sensor_sd, video, g_mbus_config, mbus);
	if (ret)
		return ret;

	return 0;
}

接着是mipi csi phy的g_mbus_config

static int mipidphy_g_mbus_config(struct v4l2_subdev *sd,
				  struct v4l2_mbus_config *config)
{
	struct mipidphy_priv *priv = to_dphy_priv(sd);
        
        /*
         * 这里的分析同上
         * 对应的sensor_sd是imx291的subdev
         */

	struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
	struct mipidphy_sensor *sensor;

	if (!sensor_sd)
		return -ENODEV;
        
        /*
         * 这里要看 rockchip_mipidphy_notifier_bound
         * 基于RV1126平台imx291分析 --- media部件连接 三
         * https://blog.csdn.net/ldl617/article/details/115726576
         */

	sensor = sd_to_sensor(priv, sensor_sd);
    
        /*
         * 主意这里传入的是sd,而不是sensor
         * mipidphy_update_sensor_mbus中会再进行一次上面的操作
         */
	mipidphy_update_sensor_mbus(sd);
	*config = sensor->mbus;

	return 0;
}

static int mipidphy_update_sensor_mbus(struct v4l2_subdev *sd)
{
	struct mipidphy_priv *priv = to_dphy_priv(sd);
	struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
	struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
	struct v4l2_mbus_config mbus;
	int ret;

        /*
         * 调用imx291到g_mbus_config
         */

	ret = v4l2_subdev_call(sensor_sd, video, g_mbus_config, &mbus);
	if (ret)
		return ret;
        ...
}

最后是调用imx291到g_mbus_config。

int rkcif_update_sensor_info(struct rkcif_stream *stream)
{
	...


        /*
         * 准备填充terminal_sensor
         */        


	terminal_sensor = &stream->cifdev->terminal_sensor;
	get_remote_terminal_sensor(stream, &terminal_sensor->sd);
	if (terminal_sensor->sd) {
		ret = v4l2_subdev_call(terminal_sensor->sd, video, g_mbus_config,
				       &terminal_sensor->mbus);
		if (ret && ret != -ENOIOCTLCMD)
			return ret;
	}
        ...
}

rkcif_fh_open() -> rkcif_update_sensor_info() -> get_remote_terminal_sensor()

static void stack_push(struct media_graph *graph,
		       struct media_entity *entity)
{
	if (graph->top == MEDIA_ENTITY_ENUM_MAX_DEPTH - 1) {
		WARN_ON(1);
		return;
	}
        

        /*
         * top值加1
         * link这里可能是link也可能是backlink
         */

	graph->top++;
	graph->stack[graph->top].link = entity->links.next;
	graph->stack[graph->top].entity = entity;
}

void media_graph_walk_start(struct media_graph *graph,
			    struct media_entity *entity)
{
	media_entity_enum_zero(&graph->ent_enum);
	media_entity_enum_set(&graph->ent_enum, entity);

        
        /*
         * 这里就是entity总数加1操作的原因
         * stack[0].entity = NULL
         */

	graph->top = 0;
	graph->stack[graph->top].entity = NULL;
	stack_push(graph, entity);
	dev_dbg(entity->graph_obj.mdev->dev,
		"begin graph walk at '%s'\n", entity->name);
}

static void get_remote_terminal_sensor(struct rkcif_stream *stream,
				       struct v4l2_subdev **sensor_sd)
{
	struct media_graph graph;
	struct media_entity *entity = &stream->vnode.vdev.entity;
	struct media_device *mdev = entity->graph_obj.mdev;
	int ret;

	/* Walk the graph to locate sensor nodes. */
	mutex_lock(&mdev->graph_mutex);

        
        /*
         * media_graph_walk_init
         * 之前分析过这个函数
         * 主要作用就是 graph.ent_enum.bmap申请一块内存空间
         * 这块空间的大小计算方式
         * 1. size = mdev->entity_internal_idx_max + 1 所有的entity数量加1
         *       加1的目的是要保存一个NULL用作top,后面分析会看到
         * 2. size = ALLGN(size, BITS_PER_LONG)
         * 3. size = (size/BITS_PER_LONG) * sizeof(long)
         */

	ret = media_graph_walk_init(&graph, mdev);
	if (ret) {
		mutex_unlock(&mdev->graph_mutex);
		*sensor_sd = NULL;
		return;
	}

	media_graph_walk_start(&graph, entity);
	...
}

上面执行后,会得到这样一个效果图

接着分析  rkcif_fh_open() -> rkcif_update_sensor_info() -> get_remote_terminal_sensor()

#define link_top(en)	((en)->stack[(en)->top].link)
#define stack_top(en)	((en)->stack[(en)->top].entity)

static struct media_entity *
media_entity_other(struct media_entity *entity, struct media_link *link)
{
        printk(KERN_CRIT "entity->name 0 is %s\n", entity->name);
        if (link->is_backlink)
                printk(KERN_CRIT "is backlink!\n");
        else
                printk(KERN_CRIT "is link!\n");
        if (link->source->entity == entity) {
                printk(KERN_CRIT "entity->name 1 is %s\n", link->sink->entity->name);
                return link->sink->entity;
        } else {
                printk(KERN_CRIT "entity->name 2 is %s\n", link->source->entity->name);
                return link->source->entity;
        }
}

static void media_graph_walk_iter(struct media_graph *graph)
{
	struct media_entity *entity = stack_top(graph);
	struct media_link *link;
	struct media_entity *next;

        
        /*
         * 转换得到media_link
         * link或者backlink都有可能
         */


	link = list_entry(link_top(graph), typeof(*link), list);

	/* The link is not enabled so we do not follow. */

        /*
         * 处理标志位没有ENABLED的link
         */

	if (!(link->flags & MEDIA_LNK_FL_ENABLED)) {

                /*
                 * 找到下一个link
                 */

		link_top(graph) = link_top(graph)->next;
		dev_dbg(entity->graph_obj.mdev->dev,
			"walk: skipping disabled link '%s':%u -> '%s':%u\n",
			link->source->entity->name, link->source->index,
			link->sink->entity->name, link->sink->index);
		return;
	}

	/* Get the entity in the other end of the link . */

        /*
         * 解析link或者backlink
         * 这里打算加些log方便分析,见上面
         */

	next = media_entity_other(entity, link);

	/* Has the entity already been visited? */

        /*
         * 判断entity是否被访问过
         */        

	if (media_entity_enum_test_and_set(&graph->ent_enum, next)) {
		link_top(graph) = link_top(graph)->next;
		dev_dbg(entity->graph_obj.mdev->dev,
			"walk: skipping entity '%s' (already seen)\n",
			next->name);
		return;
	}

	/* Push the new entity to stack and start over. */
        
        /*
         * 找到下一个link
         */

	link_top(graph) = link_top(graph)->next;

        /* 对应的link和entity再入栈 */


	stack_push(graph, next);
	dev_dbg(entity->graph_obj.mdev->dev, "walk: pushing '%s' on stack\n",
		next->name);
}

struct media_entity *media_graph_walk_next(struct media_graph *graph)
{
	struct media_entity *entity;

	if (stack_top(graph) == NULL)
		return NULL;

	/*
	 * Depth first search. Push entity to stack and continue from
	 * top of the stack until no more entities on the level can be
	 * found.
	 */
        
        /*
         * link_top(graph) 是links链表上的成员
         * stack_top(graph)->links 是links链表的链表头
         * 
         */

	while (link_top(graph) != &stack_top(graph)->links)
		media_graph_walk_iter(graph);

        /*
         * 最底层的就是我们需要的
         */

	entity = stack_pop(graph);
	dev_dbg(entity->graph_obj.mdev->dev,
		"walk: returning entity '%s'\n", entity->name);

	return entity;
}

static void get_remote_terminal_sensor(struct rkcif_stream *stream,
				       struct v4l2_subdev **sensor_sd)
{
	...
        
        /*
         * 这里千辛万苦的找到了imx291的subdev
         * 但是我们之前已经找到过imx291了,这里为什么还要再换一种方法呢?
         * 原因是之前的方法是rockchip的,没有通用性,
         * 这里是内核的,是通用的
         */


	while ((entity = media_graph_walk_next(&graph))) {
                
                /*
                 * 找到MEDIA_ENT_F_CAM_SENSOR标志的entity 退出
                 * 如果没有的话,这里会变成死循环吗?
                 */

		if (entity->function == MEDIA_ENT_F_CAM_SENSOR)
			break;
	}
        
        mutex_unlock(&mdev->graph_mutex);
	media_graph_walk_cleanup(&graph);

	if (entity)


                /*
                 * 通过entity找到subdev
                 */


		*sensor_sd = media_entity_to_v4l2_subdev(entity);
	else
		*sensor_sd = NULL;
}

media_entity_other加了log,应用执行open,底层log如下

[17666.981635] rkcif rkcif_mipi_lvds: begin graph walk at 'stream_cif_mipi_id0'
[17666.981641] entity->name 0 is stream_cif_mipi_id0
[17666.981660] is backlink!
[17666.981670] entity->name 2 is rockchip-mipi-csi2
[17666.981684] rkcif rkcif_mipi_lvds: walk: pushing 'rockchip-mipi-csi2' on stack
[17666.981686] entity->name 0 is rockchip-mipi-csi2
[17666.981696] is backlink!
[17666.981703] entity->name 2 is rockchip-mipi-dphy-rx
[17666.981715] rkcif rkcif_mipi_lvds: walk: pushing 'rockchip-mipi-dphy-rx' on stack
[17666.981718] entity->name 0 is rockchip-mipi-dphy-rx
[17666.981727] is link!
[17666.981735] entity->name 1 is rockchip-mipi-csi2
[17666.981747] rkcif rkcif_mipi_lvds: walk: skipping entity 'rockchip-mipi-csi2' (already seen)
[17666.981749] entity->name 0 is rockchip-mipi-dphy-rx
[17666.981760] is backlink!
[17666.981768] entity->name 2 is m01_f_imx291 1-001a
[17666.981780] rkcif rkcif_mipi_lvds: walk: pushing 'm01_f_imx291 1-001a' on stack
[17666.981783] entity->name 0 is m01_f_imx291 1-001a
[17666.981789] is link!
[17666.981796] entity->name 1 is rockchip-mipi-dphy-rx
[17666.981807] rkcif rkcif_mipi_lvds: walk: skipping entity 'rockchip-mipi-dphy-rx' (already seen)
[17666.981811] rkcif rkcif_mipi_lvds: walk: returning entity 'm01_f_imx291 1-001a'

对应的效果图如下

继续分析  rkcif_fh_open()

static int rkcif_fh_open(struct file *filp)
{
        ...

	ret = v4l2_fh_open(filp);
	if (!ret) {
		ret = v4l2_pipeline_pm_use(&vnode->vdev.entity, 1);
		if (ret < 0)
			vb2_fop_release(filp);
	}
        ...
}

对于v4l2_pipeline_pm_use下一遍文章分析

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dianlong_lee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值