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";
};

 

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

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

(1)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 初窥Linux 之 我最常用的20条命令

    玩过Linux的人都会知道,Linux中的命令的确是非常多,但是玩过Linux的人也从来不会因为Linux的命令如此之多而烦恼,因为我们只需要掌握我们最常用的命令就可以了。每个人玩Linux的目的都不同,所以他们常用的命令也就差异非常大,而我主要是用Linux进行C/C++和shell程序编写的,所以常用到的命令可以就会跟一个管理Linux系统的人有所不同。因为不想在使用是总是东查西找,所以在此总

    2022年4月4日
    35
  • idea 创建properties配置文件

    idea 创建properties配置文件我们在j2ee当中,连接数据库的时候经常会用到properties配置文件,我们原来在eclipse或者myeclipse当中会在src文件夹目录下创建一个properties文件。然后用如下代码去加载配置文件InputStreamin=PropertiesDemo.class.getClassLoader().getResourceAsStream(“database.properti

    2025年6月29日
    5
  • 女生学java软件开发怎么样?就业前景如何?

    女生学java软件开发怎么样?就业前景如何?学java目前现状是男生多于女生,从事java工作的也是男生多于女生,那么这种现状是说女生学java不好找工作吗?​  一、女生适合从事java吗?  在很多人的潜意识里,认为女生是不适合从事java工作的,因为他们觉得从事java工作的人逻辑性要相当的好,并且专业操作水平要高,而女生往往在这方面比较弱。其实这只是一种偏见,就像古代人觉得女子不该干涉朝政一样,女生也是适合从事java工作的,并且还能发挥自己的优势把java工作做得更好。  二、女生学java好找工作吗?  1.现在的女生

    2022年7月9日
    28
  • Python2.3-原理之语句和语法

    Python2.3-原理之语句和语法

    2021年9月7日
    56
  • vue点击复制_js获取复制的内容

    vue点击复制_js获取复制的内容Vue中配合clipboard.js实现点击按钮复制内容

    2022年4月21日
    94
  • 基于BP神经网络PID控制+Simulink仿真

    基于BP神经网络PID控制+Simulink仿真最近在学习电机的智能控制,上周学习了基于单神经元的PID控制,这周研究基于BP神经网络的PID控制。神经网络具有任意非线性表达能力,可以通过对系统性能的学习来实现具有最佳组合的PID控制。利用BP神经网络可以建立参数Kp,Ki,Kd自整定的PID控制器。基于BP神经网络的PID控制系统结构框图如下图所示:控制器由两部分组成:经典增量式PID控制器;BP神经网络…

    2022年5月30日
    44

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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