完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
dts源码如下
rkcif_mipi_lvds: rkcif_mipi_lvds { compatible = "rockchip,rkcif-mipi-lvds"; ... status = "okay"; port { cif_mipi_in: endpoint { remote-endpoint = <&mipi_csi2_output>; data-lanes = <1 2 3 4>; }; }; }; 开始分析驱动代码 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; ... cif_dev = devm_kzalloc(dev, sizeof(*cif_dev), GFP_KERNEL); if (!cif_dev) return -ENOMEM; dev_set_drvdata(dev, cif_dev); cif_dev->dev = dev; ... ret = rkcif_plat_init(cif_dev, node, data->inf_id); if (ret) { rkcif_detach_hw(cif_dev); return ret; } ... return 0; } rkcif_plat_probe -> rkcif_plat_init 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; cif_dev->pipe.open = rkcif_pipeline_open; cif_dev->pipe.close = rkcif_pipeline_close; cif_dev->pipe.set_stream = rkcif_pipeline_set_stream; cif_dev->isr_hdl = rkcif_irq_handler; /* * stream和cif_dev进行关联,知道就行 */ rkcif_stream_init(cif_dev, RKCIF_STREAM_MIPI_ID0); rkcif_stream_init(cif_dev, RKCIF_STREAM_MIPI_ID1); rkcif_stream_init(cif_dev, RKCIF_STREAM_MIPI_ID2); rkcif_stream_init(cif_dev, RKCIF_STREAM_MIPI_ID3); ... v4l2_dev = &cif_dev->v4l2_dev; ret = v4l2_device_register(cif_dev->dev, &cif_dev->v4l2_dev); if (ret < 0) return ret; /* * media暂时不分析 */ media_device_init(&cif_dev->media_dev); ret = media_device_register(&cif_dev->media_dev); if (ret < 0) { v4l2_err(v4l2_dev, "Failed to register media device: %dn", ret); goto err_unreg_v4l2_dev; } /* create & register platefom subdev (from of_node) */ ret = rkcif_register_platform_subdevs(cif_dev); if (ret < 0) goto err_unreg_media_dev; ... mutex_lock(&rkcif_dev_mutex); list_add_tail(&cif_dev->list, &rkcif_device_list); mutex_unlock(&rkcif_dev_mutex); return 0; } rkcif_plat_probe -> rkcif_plat_init -> rkcif_stream_init 这个看一下就行,用于stream和dev的分析 void rkcif_stream_init(struct rkcif_device *dev, u32 id) { struct rkcif_stream *stream = &dev->stream[id]; struct v4l2_pix_format_mplane pixm; int i; stream->id = id; stream->cifdev = dev; /* Set default format */ pixm.pixelformat = V4L2_PIX_FMT_NV12; pixm.width = RKCIF_DEFAULT_WIDTH; pixm.height = RKCIF_DEFAULT_HEIGHT; /* * stream->cif_fmt_out = fmt; * stream->pixm = pixm; */ rkcif_set_fmt(stream, &pixm, false); ... } rkcif_plat_probe -> rkcif_plat_init -> rkcif_stream_init -> rkcif_register_platform_subdevs static int rkcif_register_platform_subdevs(struct rkcif_device *cif_dev) { int stream_num = 0, ret; /* * RKCIF_MAX_STREAM_MIPI 值为4 * 会注册4个video节点 */ stream_num = RKCIF_MAX_STREAM_MIPI; ret = rkcif_register_stream_vdevs(cif_dev, stream_num, true); if (ret < 0) { dev_err(cif_dev->dev, "cif register stream[%d] failed!n", stream_num); return -EINVAL; } ret = cif_subdev_notifier(cif_dev); if (ret < 0) { v4l2_err(&cif_dev->v4l2_dev, "Failed to register subdev notifier(%d)n", ret); goto err_unreg_stream_vdev; } return 0; } rkcif_plat_probe -> rkcif_plat_init -> rkcif_stream_init -> rkcif_register_platform_subdevs -> rkcif_register_stream_vdevs 这部分代码是注册video节点,和vivi驱动大同小异 int rkcif_register_stream_vdevs(struct rkcif_device *dev, int stream_num, bool is_multi_input) { struct rkcif_stream *stream; int i, j, ret; for (i = 0; i < stream_num; i++) { stream = &dev->stream; stream->cifdev = dev; ret = rkcif_register_stream_vdev(stream, is_multi_input); if (ret < 0) goto err; } return 0; err: for (j = 0; j < i; j++) { stream = &dev->stream[j]; rkcif_unregister_stream_vdev(stream); } return ret; } static int rkcif_register_stream_vdev(struct rkcif_stream *stream, bool is_multi_input) { struct rkcif_device *dev = stream->cifdev; struct v4l2_device *v4l2_dev = &dev->v4l2_dev; struct video_device *vdev = &stream->vnode.vdev; struct rkcif_vdev_node *node; int ret = 0; char *vdev_name; if (is_multi_input) { switch (stream->id) { case RKCIF_STREAM_MIPI_ID0: vdev_name = CIF_MIPI_ID0_VDEV_NAME; break; case RKCIF_STREAM_MIPI_ID1: vdev_name = CIF_MIPI_ID1_VDEV_NAME; break; case RKCIF_STREAM_MIPI_ID2: vdev_name = CIF_MIPI_ID2_VDEV_NAME; break; case RKCIF_STREAM_MIPI_ID3: vdev_name = CIF_MIPI_ID3_VDEV_NAME; break; case RKCIF_STREAM_DVP: vdev_name = CIF_DVP_VDEV_NAME; break; default: v4l2_err(v4l2_dev, "Invalid streamn"); goto unreg; } } else { vdev_name = CIF_VIDEODEVICE_NAME; } 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; ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); if (ret < 0) { v4l2_err(v4l2_dev, "video_register_device failed with error %dn", ret); return ret; } ret = media_entity_pads_init(&vdev->entity, 1, &node->pad); if (ret < 0) goto unreg; return 0; unreg: video_unregister_device(vdev); return ret; } 列举一下video的一些信息 同样的ops还是v4l2_fops 要注意的是:
和vivi相比,不仅仅支持MMAP还支持DMABUF,另外device_caps类型是V4L2_CAP_VIDEO_CAPTURE_MPLANE,多平面视频格式,vivi中这种格式我们忽略了没有分析 rkcif_plat_probe -> rkcif_plat_init -> rkcif_stream_init -> rkcif_register_platform_subdevs -> rkcif_register_stream_vdevs -> cif_subdev_notifier static int cif_subdev_notifier(struct rkcif_device *cif_dev) { struct v4l2_async_notifier *ntf = &cif_dev->notifier; struct device *dev = cif_dev->dev; int ret; /* * v4l2_async_notifier_parse_fwnode_endpoints和 * v4l2_async_notifier_parse_fwnode_endpoints_by_port类似 * 区别是has_port这个参数是false,而不是true * true的时候会去看port到reg值,获取port号及id号 * 由于当前对应的dts没有reg * 所以使用false * 这样的结果是 rkcif_mipi的notifier->subdevs[0] 存储的数据 * 指向mipi csi dts的节点 * 回调函数 rkcif_fwnode_parse 这里不分析 */ ret = v4l2_async_notifier_parse_fwnode_endpoints( dev, ntf, sizeof(struct rkcif_async_subdev), rkcif_fwnode_parse); if (ret < 0) return ret; if (!ntf->num_subdevs) return -ENODEV; /* no endpoint */ ntf->ops = &subdev_notifier_ops; return v4l2_async_notifier_register(&cif_dev->v4l2_dev, ntf); } int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, struct v4l2_async_notifier *notifier) { int ret; if (WARN_ON(!v4l2_dev || notifier->sd)) return -EINVAL; notifier->v4l2_dev = v4l2_dev; ret = __v4l2_async_notifier_register(notifier); if (ret) notifier->v4l2_dev = NULL; return ret; } 对于__v4l2_async_notifier_register看下面的分析 一定要看v4l2_async_subdev_notifier_register的分析,否则会不理解怎么注册subdev 看完上面的分析可以知道 所有的subdev现在都挂载在v4l2_dev->subdevs上,现在分析notifier->ops->complete static int subdev_notifier_complete(struct v4l2_async_notifier *notifier) { struct rkcif_device *dev; struct rkcif_sensor_info *sensor; int ret, index; dev = container_of(notifier, struct rkcif_device, notifier); ... ret = v4l2_device_register_subdev_nodes(&dev->v4l2_dev); if (ret < 0) goto unregister_lvds; v4l2_info(&dev->v4l2_dev, "Async subdev notifier completedn"); return ret; } int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev) { struct video_device *vdev; struct v4l2_subdev *sd; int err; /* Register a device node for every subdev marked with the * V4L2_SUBDEV_FL_HAS_DEVNODE flag. */ /* * 遍历每个sd */ list_for_each_entry(sd, &v4l2_dev->subdevs, list) { if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE)) continue; if (sd->devnode) continue; vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); if (!vdev) { err = -ENOMEM; goto clean_up; } video_set_drvdata(vdev, sd); strlcpy(vdev->name, sd->name, sizeof(vdev->name)); vdev->v4l2_dev = v4l2_dev; vdev->fops = &v4l2_subdev_fops; vdev->release = v4l2_device_release_subdev_node; vdev->ctrl_handler = sd->ctrl_handler; /* * 注册/dev/v4l-subdev * 不跟进分析,记住重要的ops就可以了 */ err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1, sd->owner); if (err < 0) { kfree(vdev); goto clean_up; } sd->devnode = vdev; } return 0; }} |
|
|
|
你正在撰写答案
如果你是对答案或其他答案精选点评或询问,请使用“评论”功能。
synopsys 的design ware:DW_fpv_div,浮点数除法器,默认32位下,想提升覆盖率(TMAX),如果用功能case去提升覆盖率呢?
796 浏览 1 评论
RK3588 GStreamer调试四路鱼眼摄像头四宫格显示报错
1980 浏览 1 评论
【飞凌嵌入式OK3576-C开发板体验】RKNN神经网络-YOLO图像识别
254 浏览 0 评论
【飞凌嵌入式OK3576-C开发板体验】SSH远程登录网络配置及CAN通讯
1336 浏览 0 评论
2281 浏览 3 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-10-20 07:49 , Processed in 0.607514 second(s), Total 43, Slave 34 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号