完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
扫一扫,分享给好友
一星期的时间去研究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 %dn", 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.sd == sd) return &dev->sensors; if (i == dev->num_sensors) { for (i = 0; i < dev->num_sensors; ++i) { if (dev->sensors.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 sensorn"); 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 %sn", 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 %sn", link->sink->entity->name); return link->sink->entity; } else { printk(KERN_CRIT "entity->name 2 is %sn", 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':%un", 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 stackn", 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); } ... } |
|
|
|
你正在撰写答案
如果你是对答案或其他答案精选点评或询问,请使用“评论”功能。
基于米尔瑞芯微RK3576核心板/开发板的人脸疲劳检测应用方案
678 浏览 0 评论
920 浏览 1 评论
814 浏览 1 评论
2030 浏览 1 评论
3275 浏览 1 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-25 04:25 , Processed in 0.519319 second(s), Total 44, Slave 36 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号