草庐IT

camera驱动&系统流程整理

荣敢飞 2023-04-08 原文

**

camera驱动&系统流程整理

**

根据上面要求进行深度整理(主要针对msm8909 高通平台)

目录
一 camera 驱动需要弄明白 2
1 需要获取到的资料 2
2 点亮过程、dtsi 中需要添加什么 2
3 读取摄像头 ID 2
4 驱动分层 2
5 V4L2 架构,video* 节点进行读取 2
二 camera系统需要弄明白 2
1 mm_camera层详细分析---- 画流程图 2
2 dev/video*的读取 2
3 HAL层流程——预览、拍照、录像 画图分析 2
4 导致预览、拍照、录像 花屏、黑屏的原因有哪些,举例说明 2
5 framework 层的工作、AIDL相关、进程间的通信 2
6 算法的移植过程,与APP层的交互 画图分析 2
7 清晰度算法 —— 画图分析 2

一 camera 驱动需要弄明白

1 需要获取到的资料
Sensor datasheet、原理图、模组规格书 三份资料
datasheet和原理图可以知道sensor电源(AVDD、IOVDD、DVDD)、控制讯号(reset、POWERDOWN)、通信方式(I2C通信、CLK、DATA线)、数据通信(MIPI、CLK、lane数)、其他信号(sensor_ID_PIN、MCLK、GND)
原理图和模组规格书可以知道——调焦马达(AF_VDD、AF_EN、I2C、GND)和闪光灯(I2C、GPIO)支持

2 点亮过程、dtsi 中需要添加什么

从下面代码可以知道,分为两大部分——soc 和 i2c部分,其中 soc中是关于闪光灯支持,i2c中添加 马达、eeprom 和sensor 三部分
包含供电配置、GPIO添加、I2C地址这些填进来

&soc {

flash_SY7803:flashlight {
	compatible = "qcom,leds-gpio-flash";
	status = "okay";
	pinctrl-names = "flash_default";
	pinctrl-0 = <&SY7803_default>;
	qcom,flash-en = <&msm_gpio 93 0>;
	qcom,flash-now = <&msm_gpio 32 0>;
	qcom,op-seq = "flash_en", "flash_now";
	qcom,torch-seq-val = <1 0>;
	qcom,flash-seq-val = <0 1>;
	linux,name = "flashlight";
	linux,default-trigger = "flashlight-trigger";
	};
led_flash0: qcom,camera-led-flash {
	cell-index = <0>;
	compatible = "qcom,camera-led-flash";
	qcom,flash-type = <3>;
	qcom,flash-source = <&flash_SY7803>;
	qcom,torch-source = <&flash_SY7803>;
};

};

&i2c_3 {

actuator0: qcom,actuator@0 {
	cell-index = <0>;
	reg = <0x18>;
	compatible = "qcom,actuator";
	qcom,cci-master = <0>;
	cam_vaf-supply = <&pm8909_l8>;
	qcom,cam-vreg-name = "cam_vaf";
	qcom,cam-vreg-type = <0>;
	qcom,cam-vreg-min-voltage = <2850000>;
	qcom,cam-vreg-max-voltage = <2900000>;
	qcom,cam-vreg-op-mode = <80000>;

	pinctrl-names = "cam_sn_default", "cam_sn_suspend";
	pinctrl-0 = <&cam_infrared_led_default
		&cam_step_motor_cw_default
		&cam_step_motor_en_default
		&cam_step_motor_clk_default>;
	pinctrl-1 = <&cam_infrared_led_sleep 
		&cam_step_motor_cw_sleep
		&cam_step_motor_en_sleep
		&cam_step_motor_clk_sleep>;
	//qcom,gpio-led = <&msm_gpio 95 0>;
	qcom,gpio-moto-cw = <&msm_gpio 16 0>;
	qcom,gpio-moto-en = <&msm_gpio 17 0>;
	qcom,gpio-moto-clk = <&msm_gpio 31 0>;
			
};

eeprom0: qcom,eeprom@6d{
	cell-index = <0>;
	reg = <0x6d>;
	qcom,eeprom-name = "wdsen_ov5670";
	compatible = "qcom,eeprom";
	qcom,slave-addr = <0x6d>;
		clocks = <&clock_gcc clk_mclk0_clk_src>,
		<&clock_gcc clk_gcc_camss_mclk0_clk>;
	clock-names = "cam_src_clk", "cam_clk";
};

qcom,camera@0 {
	cell-index = <0>;
	compatible = "qcom,camera";
	reg = <0x2>;
//	qcom,special-support-sensors="ov5670_wdsen";	
	qcom,eeprom-src = <&eeprom0>;
	qcom,csiphy-sd-index = <0>;
	qcom,csid-sd-index = <0>;
	qcom,mount-angle = <180>;
	qcom,actuator-src = <&actuator0>;
	qcom,led-flash-src = <&led_flash0>;
	cam_vana-supply = <&pm8909_l17>;
	cam_vio-supply = <&pm8909_l6>;
	cam_vdig-supply = <&pm8909_l2>;
	cam_vaf-supply = <&pm8909_l8>;
	qcom,cam-vreg-name = "cam_vio", "cam_vana","cam_vdig",
		"cam_vaf";
	qcom,cam-vreg-type = <1 0 0 0>;
	qcom,cam-vreg-min-voltage = <0 2800000 1200000 2850000>;
	qcom,cam-vreg-max-voltage = <0 2850000 1200000 2900000>;
	qcom,cam-vreg-op-mode = <0 80000 80000 100000>;
	pinctrl-names = "cam_default", "cam_suspend";
	pinctrl-0 = <&cam_sensor_mclk0_default
		&cam_sensor_rear_default>;
	pinctrl-1 = <&cam_sensor_mclk0_sleep &cam_sensor_rear_sleep>;
	gpios = <&msm_gpio 26 0>,
		<&msm_gpio 35 0>,
		<&msm_gpio 34 0>;
	qcom,gpio-reset = <1>;
	qcom,gpio-standby = <2>;
	qcom,gpio-req-tbl-num = <0 1 2>;
	qcom,gpio-req-tbl-flags = <1 0 0>;
	qcom,gpio-req-tbl-label = "CAMIF_MCLK",
		"CAM_RESET1",
		"CAM_STANDBY";
	qcom,sensor-position = <0>;
	qcom,sensor-mode = <0>;
	qcom,cci-master = <0>;
	status = "ok";
	clocks = <&clock_gcc clk_mclk0_clk_src>,
		<&clock_gcc clk_gcc_camss_mclk0_clk>;
	clock-names = "cam_src_clk", "cam_clk";
};

qcom,camera@1 {
	cell-index = <1>;
	compatible = "qcom,camera";
	reg = <0x1>;
//	qcom,special-support-sensors="ov2680_wdsen";
	qcom,csiphy-sd-index = <0>;
	qcom,csid-sd-index = <0>;
	qcom,mount-angle = <180>;
	cam_vana-supply = <&pm8909_l17>;
	cam_vio-supply = <&pm8909_l6>;
	qcom,cam-vreg-name = "cam_vio", "cam_vana";
	qcom,cam-vreg-type = <1 0>;
	qcom,cam-vreg-min-voltage = <0 2800000>;
	qcom,cam-vreg-max-voltage = <0 2850000>;
	qcom,cam-vreg-op-mode = <0 80000>;
	pinctrl-names = "cam_default", "cam_suspend";
	pinctrl-0 = <&cam_sensor_mclk1_default &cam_sensor_front_default>;
	pinctrl-1 = <&cam_sensor_mclk1_sleep &cam_sensor_front_sleep>;
	gpios = <&msm_gpio 27 0>,
		<&msm_gpio 28 0>,
		<&msm_gpio 33 0>;
	qcom,gpio-reset = <1>;
	qcom,gpio-standby = <2>;
	qcom,gpio-req-tbl-num = <0 1 2>;
	qcom,gpio-req-tbl-flags = <1 0 0>;
	qcom,gpio-req-tbl-label = "CAMIF_MCLK",
		"CAM_RESET",
		"CAM_STANDBY";
	qcom,cci-master = <0>;
	status = "ok";
	clocks = <&clock_gcc clk_mclk1_clk_src>,
		<&clock_gcc clk_gcc_camss_mclk1_clk>;
	clock-names = "cam_src_clk", "cam_clk";
};

};

3 读取摄像头 ID
Qcom,slave-id = <0x20 0x0002 0x8179> 包含i2c地址、设备ID地址和预计的ID读取值
4 驱动分层
1)Msm_sensor_power_setting结构体,设置上电时序及电压
2)Eeprom 信息
3)有AF需要添加actuator 节点信息

第二就是vendor 下面的代码,也分为两部分,
一个是 sensor_libs目录下的sensor具体设定,主要是实现V4L2结构,进行V4L2的设备注册、I2C设备注册
https://blog.csdn.net/u014517638/article/details/73349385

另一个就是 ISP 效果文件

Vendor 就是高通自己实现的 daemon 进程,用于和kernel 以及hal 层进行通讯的框架代码,另一部分就是效果代码,在此目录下有 media-controller,server-tuning,server-imaging,我们需要关注的是 mediacontroller 目录,整个树形结构如下:

5 V4L2 架构,video* 节点进行读取
分为两部分,一个是 系统 V4L2代码,一个是具体sensor 驱动实现v4l2子设备相关结构体的代码,然后 在mm_camera 就会通过 dev/video*来 open 读取到 sensor 相关操作的
有关驱动中的 buffer 是怎么传到应用程序的请看
https://blog.csdn.net/yanbixing123/article/details/52294305

二 camera系统需要弄明白

1 mm_camera层详细分析---- 画流程图

https://www.jianshu.com/p/1baad2a5281d
Mm_camera主要用到5个文件

Mm_camera_interface.c 这是暴露给HAL 层调用的接口
Mm_camera.c 这是中枢部分
Mm_camera_channel.c 这是mm层 channel (通道)
Mm_camera_stream.c 这是mm层stream (流)
Mm_camera_thread.c 这是任务调度的关键部分

这里面使用的C语言写的,可以相互引用,实际上也是相互引用的(通过结构体),通过调用对应文件里方法要传参对应 结构体 的方法可以完成类似面向对象的组织方式

对应成员的具体代表意义可以看 mm_camera.h 头文件说明

下面具体说一下相关 ****.c 文件
A: mm_camera_interface.c
这里通过mm_camera_ops_t 这个结构体来暴露给 HAL 层调用,主要是注册函数指针与函数的映射关系,
static mm_camera_ops_t mm_camera_ops = {
.query_capability = mm_camera_intf_query_capability,
.register_event_notify = mm_camera_intf_register_event_notify,
.close_camera = mm_camera_intf_close,
.set_parms = mm_camera_intf_set_parms,
.get_parms = mm_camera_intf_get_parms,
.do_auto_focus = mm_camera_intf_do_auto_focus,
.cancel_auto_focus = mm_camera_intf_cancel_auto_focus,
.prepare_snapshot = mm_camera_intf_prepare_snapshot,
.start_zsl_snapshot = mm_camera_intf_start_zsl_snapshot,
.stop_zsl_snapshot = mm_camera_intf_stop_zsl_snapshot,
.map_buf = mm_camera_intf_map_buf,
.unmap_buf = mm_camera_intf_unmap_buf,
.add_channel = mm_camera_intf_add_channel,
.delete_channel = mm_camera_intf_del_channel,
.get_bundle_info = mm_camera_intf_get_bundle_info,
.add_stream = mm_camera_intf_add_stream,
.link_stream = mm_camera_intf_link_stream,
.delete_stream = mm_camera_intf_del_stream,
.config_stream = mm_camera_intf_config_stream,
.qbuf = mm_camera_intf_qbuf,
.get_queued_buf_count = mm_camera_intf_get_queued_buf_count,
.map_stream_buf = mm_camera_intf_map_stream_buf,
.unmap_stream_buf = mm_camera_intf_unmap_stream_buf,
.set_stream_parms = mm_camera_intf_set_stream_parms,
.get_stream_parms = mm_camera_intf_get_stream_parms,
.start_channel = mm_camera_intf_start_channel,
.stop_channel = mm_camera_intf_stop_channel,
.request_super_buf = mm_camera_intf_request_super_buf,
.cancel_super_buf_request = mm_camera_intf_cancel_super_buf_request,
.flush_super_buf_queue = mm_camera_intf_flush_super_buf_queue,
.configure_notify_mode = mm_camera_intf_configure_notify_mode,
.process_advanced_capture = mm_camera_intf_process_advanced_capture
};
左边是暴露给HAL 层调用的,右边是在自己里面定义的函数, 注意调用每个函数都要传入camera的句柄 camera_handle(cameraopen 的时候生成的 int 数字),通过mm_camera_util_get_camera_by_handler 获取对应的 mm_camera 结构体(一个手机有多个camera 设备),其中 interface里的每个方法也是调用mm_camera里对应的方法

这里面 Open camera
mm_camera_vtbl_t * camera_open(uint8_t camera_idx)
按相机索引打开相机 index

HAL3 打开接口
int32_t camera_open(uint8_t camera_idx, mm_camera_vtbl_t **camera_vtbl)

B: mm_camera.c
这个文件主要是产生和管理 channel, 然后配置管理 stream ,然后也定义了一些事件回调来处理事件,直接扫描到 dev/video**
Mm_camera和mm_channel 的沟通方式是调用 mm_channel 的状态机方法
在open camera 时候会开启 mm_thread.c 里面定义的两个线程:
Cmd_thread 和 poll_thread

Poll_thread 是一个很关键的线程,最后获取数据也是靠这个线程,但是开启这个线程不用来处理数据的,而是用来处理 kernel 返回的 event, 比如相机挂了这类问题
kernel的event 会通过poll_thread 回调 mm_camera_event_notify 传达到 mm_camera层,然后mm_camera会给 cmd_thread 发送相应命令,然后回调 mm_camera_dispatch_app_event,最后会回调到 HAL 层 QCamera3HardInterface的 camEvtHandle方法

int32_t mm_camera_open(mm_camera_obj_t *my_obj)
{

const char *temp_dev_name = mm_camera_util_get_dev_name(my_obj->my_hdl);

strlcpy(t_devname, temp_dev_name, sizeof(t_devname));
snprintf(dev_name, sizeof(dev_name), “/dev/%s”,t_devname );
sscanf(dev_name, “/dev/video%d”, &cam_idx);
CDBG_ERROR(“%s: dev name = %s, cam_idx = %d”, func, dev_name, cam_idx);

CDBG(“%s : Launch evt Thread in Cam Open”,func);
snprintf(my_obj->evt_thread.threadName, THREAD_NAME_SIZE, “CAM_Dispatch”);
mm_camera_cmd_thread_launch(&my_obj->evt_thread,
mm_camera_dispatch_app_event,
(void *)my_obj);

/* launch event poll thread
 * we will add evt fd into event poll thread upon user first register for evt */
CDBG("%s : Launch evt Poll Thread in Cam Open", __func__);
snprintf(my_obj->evt_thread.threadName, THREAD_NAME_SIZE, "CAM_Poll");
mm_camera_poll_thread_launch(&my_obj->evt_poll_thread,
                             MM_CAMERA_POLL_TYPE_EVT);
mm_camera_evt_sub(my_obj, TRUE);

C:mm_camera_channel

D: mm_camera_stream

E: mm_camera_thread

最后用流程图进行进行完整的 mm_camera 总结:

2 dev/video*的读取

Mm_camera.c 中在 open camera 中调用
int32_t mm_camera_open(mm_camera_obj_t *my_obj)
{

strlcpy(t_devname, temp_dev_name, sizeof(t_devname));
snprintf(dev_name, sizeof(dev_name), “/dev/%s”,t_devname );
sscanf(dev_name, “/dev/video%d”, &cam_idx);
CDBG_ERROR(“%s: dev name = %s, cam_idx = %d”, func, dev_name, cam_idx);

3 HAL层流程——预览、拍照、录像 画图分析

A: 预览参考
https://www.jianshu.com/p/02772c83dc35

B: 拍照参考
https://www.jianshu.com/p/a70bf4620087

4 导致预览、拍照、录像 花屏、黑屏的原因有哪些,举例说明
A: 打开闪退,调用到马达时候闪退重启,
B:
5 framework 层的工作、AIDL相关、进程间的通信

Camera 从 framework到HAL
Camera2 数据流从framework到Hal源码分析
https://www.jianshu.com/p/ecb1be82e6a8

6 算法的移植过程,与APP层的交互 画图分析

7 清晰度算法 —— 画图分析

有关camera驱动&系统流程整理的更多相关文章

  1. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

    我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

  2. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

    我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

  3. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  4. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

  5. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

    为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

  6. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  7. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  8. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  9. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

  10. ruby - 无法让 RSpec 工作—— 'require' : cannot load such file - 2

    我花了三天的时间用头撞墙,试图弄清楚为什么简单的“rake”不能通过我的规范文件。如果您遇到这种情况:任何文件夹路径中都不要有空格!。严重地。事实上,从现在开始,您命名的任何内容都没有空格。这是我的控制台输出:(在/Users/*****/Desktop/LearningRuby/learn_ruby)$rake/Users/*******/Desktop/LearningRuby/learn_ruby/00_hello/hello_spec.rb:116:in`require':cannotloadsuchfile--hello(LoadError) 最佳

随机推荐