rk3399调试ov2659(camera模块@dvp接口)–源码分析

rk3399调试ov2659(camera模块@dvp接口)–源码分析  之前整理的“rockchipsensorcore框架”和rkisp下的v4l2框架有点像,只不过v4l2框架有点大(而且不支持摄像头热插拔)。其实接触越多Linux子系统越发觉得这些子系统处理思想大同小异。   这种"核…

大家好,又见面了,我是你们的朋友全栈君。

版权声明:本文为博主原创文章,转载请注明出处:https://blog.csdn.net/huang_165/article/details/86217004

    之前整理的“rockchip sensor core框架”和rkisp下的v4l2框架有点像,只不过v4l2框架有点大(而且不支持摄像头热插拔)。其实接触越多Linux子系统越发觉得这些子系统处理思想大同小异。
     这种”核心思想”就是将”同类设备(soc/外设)的同种属性、内核资源管理”整理出一个”核心层”,达到求同存异、松耦合、分层管理的目的。

v4l2简单框图:


rk3399调试ov2659(camera模块@dvp接口)--源码分析

从V4L2简单框图可以看出,V4L2是一个字符设备,而V4L2的大部分功能都是通过设备文件的ioctl导出的。

一般来说,摄像头驱动需要实现与向核心层提交下面十几个ioctl接口
VIDIOC_REQBUFS:分配内存
VIDIOC_QUERYCAP:查询驱动功能
VIDIOC_ENUM_FMT:获取当前驱动支持的视频格式
VIDIOC_S_FMT:设置当前驱动的频捕获格式
VIDIOC_G_FMT:读取当前驱动的频捕获格式
VIDIOC_TRY_FMT:验证当前驱动的显示格式
VIDIOC_CROPCAP:查询驱动的修剪能力
VIDIOC_S_CROP:设置视频信号的边框
VIDIOC_G_CROP:读取视频信号的边框
VIDIOC_QBUF:把数据放回缓存队列
VIDIOC_DQBUF:把数据从缓存中读取出来
VIDIOC_STREAMON:开始图像捕获
VIDIOC_STREAMOFF:结束图像捕获

摄像头驱动-ov2659源码分析:

rk3399调试ov2659(camera模块@dvp接口)--源码分析

      先写个断言“核心层和摄像头设备驱动是通过check_camera_id机制来确认具体摄像头driver的”,在《框架分析》证明。

我们分析下:下面的1,2,3步看看应用层、核心层、摄像头如何配合起来的。
1.应用层分配内存–VIDIOC_REQBUFS
2.应用层使能摄像头开始图像捕获–VIDIOC_STREAMON
3.应用层把捕获到的图像从缓存中读取出来–VIDIOC_DQBUF

框架分析:
     由于应用层操作的是v4l2设备(/dev/videox),这里选video0(该通道用于抓图像)。

现在,从设备树开始,分析video0是如何构建起来的。
rk3399-linux.dtsi:     compatible = "rockchip,rk3399-cif-isp";在驱动目录下查找rockchip,rk3399-cif-isp
在media/platform/rk-isp10/cif_isp10_v4l2.c-->cif_isp10_v4l2_of_match找到。所以,我这个rk3399 sdk版本下摄像头走rk-isp v4l2框架。
cif_isp10_v4l2_drv_probe
	-->match = of_match_node(cif_isp10_v4l2_of_match, node); 找到设备树上的cif_isp1: cif_isp@ff920000节点,该节点内容见附录。
	-->cif_isp10_create 构建ISP
		-->cif_isp10_pltfrm_soc_init 初始化ISP
		-->cif_isp10_img_srcs_init 初始化图像源设备(摄像头)
			-->cif_isp10_pltfrm_get_img_src_device 查找ISP10下cif接口的图像源设备
				-->phandle = of_get_property(node, "rockchip,camera-modules-attached", &size); 
			//根据该节点内容可知,这里就通过rockchip,camera-modules-attached找到camera4了
				-->client = of_find_i2c_device_by_node(camera_list_node);
				-->img_src_array[num_cameras] = cif_isp10_img_src_to_img_src(&client->dev, &(cif_isp10_dev->soc_cfg));
					-->cif_isp10_img_src_ops[i].ops.to_img_src
						-->cif_isp10_img_src_v4l2_i2c_subdev_to_img_src //至此核心层就能和摄像头设备驱动绑定了
							-->i2c_get_clientdata
							--> v4l2_subdev_call(subdev, core, ioctl, PLTFRM_CIFCAM_ATTACH, (void *)soc_cfg);
								-->ov_camera_module_ioctl-->ov_camera_module_init
												-->ov_camera_module_attach-->"custom->check_camera_id(cam_mod)"
			//将从phandle遍历出来的i2c设备中找到符合条件的client,并将client和当前ISP绑定。

	-->cif_isp10_v4l2_register_video_device
		-->vdev->ioctl_ops = ioctl_ops; 使用最后一个参数作为和上层交互的ioctl
		-->video_register_device 在这里将/dev/videox注册上
	-->g_cif_isp10_v4l2_dev[g_cif_isp10_v4l2_dev_cnt] =cif_isp10_v4l2_dev; //将设置、注册好的ISP加入核心层中。

cif_isp10_v4l2_register_video_device最后一个参数:cif_isp10_v4l2_sp_ioctlops,是提供给上层用的,我们等下要分析的1,2,3点将用到。cif_isp10_v4l2_sp_ioctlops原型见附录

看到cif_isp10_v4l2_sp_ioctlops原型一系列ioctl函数,我们对上层的调用就很清晰了:

第1步的VIDIOC_REQBUFS就调用.vidioc_reqbufs
第2步的VIDIOC_STREAMON就调用.vidioc_streamon
第3步的VIDIOC_DQBUF就调用.vidioc_dqbuf
      1,3步和摄像头的设备驱动没什么关系,有兴趣的读者自行分析。
      我分析第2步,看.vidioc_streamon如何调用到设备驱动的,也就是ov2659_custom_config.start_streaming
要解决这个问题,其实是要知道核心层是如何寻找到摄像头设备驱动的。

“框架分析”我们知道,rkisp组织的v4l2框架通过设备树rockchip,camera-modules-attached属性绑定具体摄像头硬件,通过check_camera_id机制来确认具体摄像头driver。


    关于第2步顺序跟踪下cif_isp10_v4l2_sp_ioctlops.vidioc_streamon

cif_isp10_v4l2_streamon
	-->cif_isp10_v4l2_streamon
		-->cif_isp10_start
			-->cif_isp10_img_src_ioctl
				-->img_src->ops->ioctl(img_src->img_src, cmd, arg); 
			而img_src:cif_isp10_img_src_ops[]是一个全局常量数组,其ops.ioctl字段为cif_isp10_img_src_v4l2_subdev_ioctl
					-->cif_isp10_img_src_v4l2_subdev_ioctl
						-->v4l2_subdev_call(subdev,core,ioctl,cmd,arg);//v4l2_subdev_call原型见附录

可以看出,cif_isp10_v4l2_streamon最终会调用subdev下的ioctl.ioctl这里的subdev就是摄像头驱动ov2659.sd

     总结:因为摄像头(subdev)和核心层(rkisp)是通过设备树cif_isp1节点下的rockchip,camera-modules-attached属性、check_camera_id机制绑定。所以,摄像头是不支持“严格意义热插拔”的。其实,分析的难点也是知道核心层(rkisp)、摄像头(subdev)是如何绑定。
     最后,分享一个调试经验。在对源码调用关系把握不好时,可以故意做一个编译错误、运行错误、运行打印等信息来帮助调试分析。看别人分析源码有时候会有点接不上,这时候最好是自己也分析一遍,分析多了就有一些想法,对一些关键组件看名字也能猜到一些调用关系。


附录:

v4l2_subdev_call原型:

#define v4l2_subdev_call(sd, o, f, args...)				\
	(!(sd) ? -ENODEV : (((sd)->ops->o && (sd)->ops->o->f) ?	\
		(sd)->ops->o->f((sd) , ##args) : -ENOIOCTLCMD))

cif_isp10_v4l2_sp_ioctlops原型:

const struct v4l2_ioctl_ops cif_isp10_v4l2_sp_ioctlops = {
	.vidioc_reqbufs = cif_isp10_v4l2_reqbufs,
	.vidioc_querybuf = cif_isp10_v4l2_querybuf,
	.vidioc_create_bufs = vb2_ioctl_create_bufs,
	.vidioc_qbuf = cif_isp10_v4l2_qbuf,
	.vidioc_dqbuf = cif_isp10_v4l2_dqbuf,
	.vidioc_streamon = cif_isp10_v4l2_streamon,
	.vidioc_streamoff = cif_isp10_v4l2_streamoff,
	...
	.vidioc_enum_input = v4l2_enum_input,
	.vidioc_g_ctrl = v4l2_g_ctrl,
	.vidioc_s_ctrl = cif_isp10_v4l2_s_ctrl,
	.vidioc_s_fmt_vid_cap = cif_isp10_v4l2_s_fmt,
	.vidioc_g_fmt_vid_cap = cif_isp10_v4l2_g_fmt,
	...
	.vidioc_s_ext_ctrls = v4l2_s_ext_ctrls,
	.vidioc_enum_fmt_vid_cap = v4l2_enum_fmt_cap,
	.vidioc_enum_framesizes = cif_isp10_v4l2_enum_framesizes,
	...
};

设备树:

cif_isp1: cif_isp@ff920000 {
		compatible = "rockchip,rk3399-cif-isp";
		rockchip,grf = <&grf>;
		reg = <0x0 0xff920000 0x0 0x4000>, <0x0 0xff968000 0x0 0x8000>;
		reg-names = "register", "dsihost-register";
		clocks =
			<&cru ACLK_ISP1_NOC>, <&cru ACLK_ISP1_WRAPPER>,
			<&cru HCLK_ISP1_NOC>, <&cru HCLK_ISP1_WRAPPER>,
			<&cru SCLK_ISP1>, <&cru PCLK_ISP1_WRAPPER>,
			<&cru SCLK_DPHY_TX1RX1_CFG>,
			<&cru PCLK_MIPI_DSI1>, <&cru SCLK_MIPIDPHY_CFG>,
			<&cru SCLK_CIF_OUT>, <&cru SCLK_CIF_OUT>,
			<&cru SCLK_MIPIDPHY_REF>;
		clock-names =
			"aclk_isp1_noc", "aclk_isp1_wrapper",
			"hclk_isp1_noc", "hclk_isp1_wrapper",
			"clk_isp1", "pclkin_isp1",
			"pclk_dphytxrx",
			"pclk_mipi_dsi","mipi_dphy_cfg",
			"clk_cif_out", "clk_cif_pll",
			"pclk_dphy_ref";
		interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH 0>;
		interrupt-names = "cif_isp10_irq";
		power-domains = <&power RK3399_PD_ISP1>;
		rockchip,isp,iommu-enable = <1>;
		iommus = <&isp1_mmu>;
		status = "okay";
};
&cif_isp1 {		//cif_isp1指定了和camera4绑定(在上一篇《rk3399调试ov2659(camera模块@dvp接口)--移植过程》有介绍)
	rockchip,camera-modules-attached = <&camera4>;
	status = "okay";
};

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/133443.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(1)


相关推荐

  • 平凡的推送广告_朋友圈推送的广告怎么查找

    平凡的推送广告_朋友圈推送的广告怎么查找智能手机的广泛使用,使广告行业有了新一轮的冲击,并随之产生了新的广告模式之一——推送广告模式。什么是推送广告?    推送广告就是在通知栏显示出来的广告信息。这是一种新的移动广告业务,通过推送功能来实现营销目的,当然,APP开发者也通过它来获取了盈利。与传统的Banner广告条相比,推送广告可以与APP分离,既不占用屏幕内容,也不会对用户在体验APP过程中产生任何影响,因为它本身就

  • 排序-冒泡排序

    排序-冒泡排序排序算法之【冒泡排序】在写代码之前我们需要对冒泡排序有一个逻辑上的理解:即什么是冒泡排序呢?冒泡排序是排序算法的其中一种,该排序的逻辑理解起来较为容易,理解上可以有两种方式,一种中正向的思维,一种是逆向的思维,什么意思呢?所谓的正向思维就是从前往后,从左往右,从上到下。那么逆向思维呢就正好与之相反。下面来说一正向思维下的冒泡排序:…

  • 跟我一起数据挖掘(20)——网站日志挖掘

    跟我一起数据挖掘(20)——网站日志挖掘收集web日志的目的Web日志挖掘是指采用数据挖掘技术,对站点用户访问Web服务器过程中产生的日志数据进行分析处理,从而发现Web用户的访问模式和兴趣爱好等,这些信息对站点建设潜在有用的可理解的未知信息和知识,用于分析站点的被访问情况,辅助站点管理和决策支持等。1、以改进web站点设计为目标,通过挖掘用户聚类和用户的频繁访问路径,修改站点的页面之间的链接关系,以适应用户的访问习惯…

  • 211逆袭浙大-计算机及相关衍生专业保研之路纪实(深度长文,收藏了)

    211逆袭浙大-计算机及相关衍生专业保研之路纪实(深度长文,收藏了)面试过N个院校,最后上岸浙江大学软件学院

  • mysql中字符串转数字「建议收藏」

    mysql中字符串转数字「建议收藏」mysql中字符串在进行计算或排序的时候转数字比如以字符串111为例,方法一:SELECTCAST(‘111’ASSIGNED);方法二:SELECTCONVERT(‘111’,SIGNED);或者SELECTCONVERT(‘111’,decimal(10,5));方法三:SELECT‘111’+0;…

  • 微信公众号平台图片上传失败不知道是哪张_看公众号的同时回微信

    微信公众号平台图片上传失败不知道是哪张_看公众号的同时回微信获取图片链接:http://file.api.weixin.qq.com/cgi-bin/media/get?access_token={0}&media_id={1}如今会提示错误{&qu

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号