**
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 清晰度算法 —— 画图分析