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

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

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

v4l2简单框图:


rk3399调试ov2659(camera模块@dvp接口)--源码分析_第1张图片

从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接口)--源码分析_第2张图片

      先写个断言“核心层和摄像头设备驱动是通过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 = ;
		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";
};

 

你可能感兴趣的:(camera)