MSM8909 Android7驱动Camera OV5648流程
1、前言
本文将基于Quectel的SC20模块进行OV5648 Camera的点亮,并对其过程进行一个简要介绍,SC20模块是一款基于MSM8909的智能模块,视频输入接口基于MIPI_CSI标准,可以支持两个摄像头,后置摄像头接口最高支持8MP像素的Camera,前置摄像头接口可支持2MP像素的Camera,OV5648 Camera最高像素分辨率为5MP,因此需要采用SC20的后置摄像头接口进行驱动调试。
2、前期准备
在驱动点亮OV5648 Camera之前需要准备好一些基本东西,如下:
- SC20模块硬件基本手册
- 原理图
- OV5648 Datasheet以及摄像头规格书
- OV5648的Camera驱动以及Camera效果驱动文件
- 示波器以及逻辑分析仪
以上东西准备好后,先进行原理的简单分析,并确认硬件在电路上是无误:
通过查看SC20模块硬件基本手册,确定后置摄像头硬件接口,如下所示:
查看后置摄像头硬件接口与摄像头连接的应用参考电路,如下:
接下来,确定原理图后置摄像头连接是否正常,连接如下:
摄像头电源供给:
摄像头控制信号以及数据信号:
硬件原理确认无误后即可进行下一步。
接下来,查看OV5648 Camera的数据手册,并得到一些重要的信息,例如:芯片的一些关键特性、通信接口、上下电时序、重要的寄存器等,在系统启动过程中,OV5648摄像头会有个冷启动过程,在这个过程中会读取Camera芯片的ID,因此必须要通过Camera的数据手册得到这些信息:
首先是,需要芯片的一些关键特性,比如输入电压、分辨率、输入时钟、数据输出格式等,如下:
OV5648芯片的内部架构框图如下:
串行相机控制总线接口(Serial Camera Control Bus Interface)也就是I2C接口,用来控制Sensor的操作,通过数据手册可以知道从机地址以及读写时序,如下:
可以知道,I2C的从机地址为0x36,最后一位为读写位,写寄存器数据时为0x6C,读寄存器数据时为0x6D。
另外,还有就是I2C的时钟速率,这个值一般设置为400KHz,可以从手册中知道,如下:
接下来,则是OV5648的上下电时序控制,这个需要根据自己的实际情况修改,通过Datasheet可以知道,OV5648的上电时序如下所示:
大概的上电时序就是,芯片电源上电后,Reset引脚拉高,如果有Power Down引脚控制的话,同时也要控制,主控输出MCLK时钟,这个时钟一般配置为24MHz,芯片正常工作后,接下来,就是通过I2C进行寄存器的读写操作,进一步完成芯片的初始化。
OV5648的下电时序如下所示:
Camera的上下电时序基本就是这样,不同的sensor基本有一定的差异,要查看相关芯片手册进行分析。
查看Sensor的ID寄存器的地址以及数据,上电开机后,冷启动过程需要读取该Sensor的ID以判断通信过程是否已经正常了,如果该Sensor的ID无法进行读出,则需要检查硬件电路以及上电时序,OV5648的该寄存器如下:
到此,从Datasheet需要得到的关键内容就差不多了,还需要准备好相关的摄像头驱动以及摄像头效果文件,示波器和逻辑分析仪等调试工具。
3、驱动代码添加
(1)添加Kernel Driver
根据实际情况使用的引脚去修改msm8909平台的设备树,文件路径如下:
android7/kernel/arch/arm/boot/dts/qcom/msm8909-camera-sensor-mtp.dtsi
dts文件修改如下:
&tlmm_pinmux { cam_sensor_mclk0 { /* MCLK */ qcom,pins = <&gp 26>; qcom,num-grp-pins = <1>; qcom,pin-func = <1>; label = "cam-sensor-mclk0"; /* active state */ cam_sensor_mclk0_default: default { drive-strength = <2>; /* 2 MA */ bias-disable = <0>; /* No PULL */ }; }; cam_sensor_mclk0_sleep { /* MCLK */ qcom,pins = <&gp 26>; qcom,num-grp-pins = <1>; label = "cam-sensor-mclk0-sleep"; /* suspend state */ cam_sensor_mclk0_sleep: sleep { drive-strength = <2>; /* 2 MA */ bias-pull-down; /* PULL DOWN */ }; }; cam_sensor_rear { /* RESET, STANDBY */ qcom,pins = <&gp 35>, <&gp 34>, <&gp 50>; qcom,num-grp-pins = <3>; qcom,pin-func = <0>; label = "cam-sensor-rear"; /* active state */ cam_sensor_rear_default: default { drive-strength = <2>; /* 2 MA */ bias-disable = <0>; /* No PULL */ }; }; cam_sensor_rear_sleep { /* RESET, STANDBY */ qcom,pins = <&gp 35>, <&gp 34>, <&gp 50>; qcom,num-grp-pins = <3>; qcom,pin-func = <0>; label = "cam-sensor-rear-sleep"; /*suspended state */ cam_sensor_rear_sleep: sleep { drive-strength = <2>; /* 2 MA */ bias-pull-down; /* PULL DOWN */ }; }; }; &i2c_3 { //Rear Camera qcom,camera@0 { cell-index = <0>; compatible = "qcom,camera"; reg = <0x2>; qcom,csiphy-sd-index = <0>; qcom,csid-sd-index = <0>; qcom,mount-angle = <0>; //qcom,actuator-src = <&actuator0>; qcom,led-flash-src = <&led_flash0>; //qcom,eeprom-src = <&eeprom0>; cam_vdig-supply = <&pm8909_l2>; cam_vana-supply = <&pm8909_l17>; cam_vio-supply = <&pm8909_l6>; cam_vaf-supply = <&pm8909_l8>; qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana", "cam_vaf"; qcom,cam-vreg-type = <0 0 0 0>; qcom,cam-vreg-min-voltage = <1200000 0 2800000 2850000>; qcom,cam-vreg-max-voltage = <1200000 0 2850000 2900000>; qcom,cam-vreg-op-mode = <200000 0 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>, //CAM0_CLK <&msm_gpio 35 0>, //CAM0_RST <&msm_gpio 34 0>, //CAM0_PWD <&msm_gpio 50 0>; //Not Used qcom,gpio-reset = <1>; qcom,gpio-standby = <2>; qcom,gpio-vdig = <3>; qcom,gpio-req-tbl-num = <0 1 2 3>; qcom,gpio-req-tbl-flags = <1 0 0 0>; qcom,gpio-req-tbl-label = "CAMIF_MCLK", "CAM_RESET1", "CAM_STANDBY", "CAM_VDIG"; 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"; }; };
需要密切关注的是MCLK、POWD、RESET对应的引脚的pinctrl,I2C接口等。
(2)添加User Space Driver
接下来,添加摄像头Kernel用户空间驱动,这些驱动可以咨询摄像头厂商,文件路径如下:
android7/vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/sensors/sensor_libs/ov5648_8909/ov5648_8909_lib.c、ov5648_8909_lib.h、Android.mk
在修改和配置摄像头用户空间驱动的时候,需要密切一些重要的参数配置,比如Sensor的初始化参数、Sensor的输出参数、Sensor的上下电时序、Camera的ID、从机地址、Sensor的ID等。
static struct msm_camera_sensor_slave_info sensor_slave_info = { /* Camera slot where this camera is mounted */ .camera_id = CAMERA_0, /* sensor slave address */ .slave_addr = 0x6c, /* sensor i2c frequency*/ .i2c_freq_mode = I2C_FAST_MODE, /* sensor address type */ .addr_type = MSM_CAMERA_I2C_WORD_ADDR, /* sensor id info*/ .sensor_id_info = { /* sensor id register address */ .sensor_id_reg_addr = 0x300a, /* sensor id */ .sensor_id = 0x5648, }, /* power up / down setting */ .power_setting_array = { .power_setting = power_setting, .size = ARRAY_SIZE(power_setting), .power_down_setting = power_down_setting, .size_down = ARRAY_SIZE(power_down_setting), }, };
camera_id的值表示的含义为,CAMERA_0表示使用的是后置摄像头接口,CAMERA_1表示使用的是前置摄像头接口。
Sensor的上下电时序需要根据芯片手册进行配置,如下:
static struct msm_sensor_power_setting power_setting[] = { { .seq_type = SENSOR_VREG, .seq_val = CAM_VIO, .config_val = 0, .delay = 0, }, { .seq_type = SENSOR_VREG, .seq_val = CAM_VANA, .config_val = 0, .delay = 0, }, { .seq_type = SENSOR_GPIO, .seq_val = SENSOR_GPIO_VANA, .config_val = GPIO_OUT_HIGH, .delay = 10, }, { .seq_type = SENSOR_GPIO, .seq_val = SENSOR_GPIO_VDIG, .config_val = GPIO_OUT_HIGH, .delay = 10, }, { .seq_type = SENSOR_GPIO, .seq_val = SENSOR_GPIO_VIO, .config_val = GPIO_OUT_HIGH, .delay = 10, }, { .seq_type = SENSOR_VREG, .seq_val = CAM_VDIG, .config_val = 0, .delay = 1, }, { .seq_type = SENSOR_GPIO, .seq_val = SENSOR_GPIO_RESET, .config_val = GPIO_OUT_LOW, .delay = 0, }, { .seq_type = SENSOR_GPIO, .seq_val = SENSOR_GPIO_RESET, .config_val = GPIO_OUT_HIGH, .delay = 10, }, { .seq_type = SENSOR_GPIO, .seq_val = SENSOR_GPIO_STANDBY, .config_val = GPIO_OUT_LOW, .delay = 0, }, { .seq_type = SENSOR_GPIO, .seq_val = SENSOR_GPIO_STANDBY, .config_val = GPIO_OUT_HIGH, .delay = 5, }, { .seq_type = SENSOR_CLK, .seq_val = SENSOR_CAM_MCLK, .config_val = 24000000, .delay = 10, }, { .seq_type = SENSOR_I2C_MUX, .seq_val = 0, .config_val = 0, .delay = 0, }, }; static struct msm_sensor_power_setting power_down_setting[] = { { .seq_type = SENSOR_GPIO, .seq_val = SENSOR_GPIO_VANA, .config_val = GPIO_OUT_LOW, .delay = 0, }, { .seq_type = SENSOR_GPIO, .seq_val = SENSOR_GPIO_VDIG, .config_val = GPIO_OUT_LOW, .delay = 0, }, { .seq_type = SENSOR_GPIO, .seq_val = SENSOR_GPIO_VIO, .config_val = GPIO_OUT_LOW, .delay = 10, }, { .seq_type = SENSOR_GPIO, .seq_val = SENSOR_GPIO_STANDBY, .config_val = GPIO_OUT_LOW, .delay = 0, }, { .seq_type = SENSOR_CLK, .seq_val = SENSOR_CAM_MCLK, .config_val = 0, .delay = 10, }, };
完整的驱动代码如下:
/*============================================================================ Copyright (c) 2015 Qualcomm Technologies, Inc. All Rights Reserved. Qualcomm Technologies Proprietary and Confidential. ============================================================================*/ #ifndef __OV5648_8909_LIB_H__ #define __OV5648_8909_LIB_H__ #include "sensor_lib.h" #define SENSOR_MODEL_NO_OV5648_8909 "ov5648_8909" #define OV5648_8909_LOAD_CHROMATIX(n) \ "libchromatix_"SENSOR_MODEL_NO_OV5648_8909"_"#n".so" static struct msm_camera_i2c_reg_array init_reg_array[] = { /* System Control */ {0x3001, 0x00}, /* SC_CMMN_PAD_OEN1 (I/O Direction) */ {0x3002, 0x00}, /* SC_CMMN_PAD_OEN2 (I/O Direction) */ {0x3011, 0x02}, /* SC_CMMN_PAD_PK (FREX Pin en,io drive strength) */ {0x3017, 0x05}, /* SC_CMMN_MIPI_PHY */ {0x3018, 0x4c}, /* SC_CMMN_MIPI_SC_CTRL (two mipi lane mode,mipi enable) */ {0x301c, 0xd2}, /* debug mode */ {0x3022, 0x00}, /* SC_CMMN_MIPI_SC_CTRL */ {0x3034, 0x1a}, /* SC_CMMN_MIPI_PLL_CTRL0 (mipi_bit_mode:10-bit mode) */ {0x3035, 0x21}, /* SC_CMMN_MIPI_PLL_CTRL1 (system_pll_div,scale_divider_mipi) */ {0x3036, 0x69}, /* SC_CMMN_MIPI_PLL_MULTIPLIER (PLL_multipliter) */ {0x3037, 0x03}, /* SC_CMMN_MIPI_PLL_CTR13 (pll_prediv) */ {0x3038, 0x00}, /* SC_CMMN_MIPI_PLL_DEBUG_OPT */ {0x3039, 0x00}, /* SC_CMMN_MIPI_PLL_CTRL_R (pll_bypass) */ {0x303a, 0x00}, /* SC_CMMN_MIPI_PLLS_CTRL0 (pllS_bypass) */ {0x303b, 0x19}, /* SC_CMMN_MIPI_PLLS_CTRL1 (pllS_multiplier) */ {0x303c, 0x11}, /* SC_CMMN_MIPI_PLLS_CTRL2 (pllS_sys_div) */ {0x303d, 0x30}, /* SC_CMMN_MIPI_PLLS_CTRL3 (pllS_pre_div,plls_div_r,plls_seld5) */ {0x3105, 0x11}, {0x3106, 0x05}, /* SRB CTRL (PLL clock divider,rst_arb,sclk_arb) */ {0x3304, 0x28}, {0x3305, 0x41}, {0x3306, 0x30}, {0x3308, 0x00}, {0x3309, 0xc8}, {0x330a, 0x01}, {0x330b, 0x90}, {0x330c, 0x02}, {0x330d, 0x58}, {0x330e, 0x03}, {0x330f, 0x20}, {0x3300, 0x00}, /* AEC/AGC Control */ {0x3500, 0x00}, /* EXPOSURE (Exposure[19:16]) */ {0x3501, 0x3d}, /* EXPOSURE (Exposure[15:8]) */ {0x3502, 0x00}, /* EXPOSURE (Exposure[7:0]) */ {0x3503, 0x07}, /* MANUAL CTRL (AEC/AGC manual enable) */ {0x350a, 0x00}, /* AGC (AGC real gain output high byte_Gain[9:8]) */ {0x350b, 0x40}, /* AGC (AGC real gain output low byte_Gain[7:0]) */ /* ANALOG_CONTROL */ {0x3601, 0x33}, {0x3602, 0x00}, {0x3611, 0x0e}, {0x3612, 0x2b}, {0x3614, 0x50}, {0x3620, 0x33}, {0x3622, 0x00}, {0x3630, 0xad}, {0x3631, 0x00}, {0x3632, 0x94}, {0x3633, 0x17}, {0x3634, 0x14}, {0x3704, 0xc0}, {0x3705, 0x2a}, {0x3708, 0x66}, {0x3709, 0x52}, {0x370b, 0x23}, {0x370c, 0xcf}, {0x370d, 0x00}, {0x370e, 0x00}, {0x371c, 0x07}, {0x3739, 0xd2}, {0x373c, 0x00}, /* timing control */ {0x3800, 0x00}, /* TIMING_X_ADDR_START (x_addr_start[11:8]) */ {0x3801, 0x00}, /* TIMING_X_ADDR_START (x_addr_start[7:0]) */ {0x3802, 0x00}, /* TIMING_Y_ADDR_START (y_addr_start[11:8]) */ {0x3803, 0x00}, /* TIMING_Y_ADDR_START (y_addr_start[7:0]) */ {0x3804, 0x0a}, /* TIMING_X_ADDR_END (x_addr_end[11:8]) */ {0x3805, 0x3f}, /* TIMING_X_ADDR_END (x_addr_end[7:0]) */ {0x3806, 0x07}, /* TIMING_Y_ADDR_END (y_addr_end[11:8]) */ {0x3807, 0xa3}, /* TIMING_Y_ADDR_END (y_addr_end[7:0]) */ {0x3808, 0x05}, /* TIMING_X_OUTPUT_SIZE (Video output horizontal width[11:8]) */ {0x3809, 0x10}, /* TIMING_X_OUTPUT_SIZE (Video output horizontal width[7:0]) */ {0x380a, 0x03}, /* TIMING_Y_OUTPUT_SIZE (Video output vertical height[11:8]) */ {0x380b, 0xcc}, /* TIMING_Y_OUTPUT_SIZE (Video output vertical height[7:0]) */ {0x380c, 0x0b}, /* TIMING_HTS (Total horizontal size[12:8]) */ {0x380d, 0x00}, /* TIMING_HTS (Total horizontal size[7:0]) */ {0x380e, 0x03}, /* TIMING_VTS (Total vertical size[15:8]) */ {0x380f, 0xe0}, /* TIMING_VTS (Total vertical size[7:0]) */ {0x3810, 0x00}, /* TIMING_ISP_X_WIN (ISP horizontal offset[11:8]) */ {0x3811, 0x08}, /* TIMING_ISP_X_WIN (ISP horizontal offset[7:0]) */ {0x3812, 0x00}, /* TIMING_ISP_Y_WIN (ISP vertical offset[11:8]) */ {0x3813, 0x04}, /* TIMING_ISP_Y_WIN (ISP vertical offset[7:0]) */ {0x3814, 0x31}, /* TIMING_X_INC */ {0x3815, 0x31}, /* TIMING_Y_INC */ {0x3817, 0x00}, /* TIMING_HSYNCST (HSYNC start point[7:0]) */ {0x3820, 0x48},//mirror {0x3821, 0x01},//flip /* {0x3820, 0x4e},//mirror {0x3821, 0x07},//flip */ {0x3826, 0x03}, {0x3829, 0x00}, {0x382b, 0x0b}, {0x3830, 0x00}, {0x3836, 0x00}, {0x3837, 0x00}, {0x3838, 0x00}, {0x3839, 0x04}, {0x383a, 0x00}, {0x383b, 0x01}, /* strobe/frame exposure */ {0x3b00, 0x00}, {0x3b02, 0x08}, {0x3b03, 0x00}, {0x3b04, 0x04}, {0x3b05, 0x00}, {0x3b06, 0x04}, {0x3b07, 0x08}, {0x3b08, 0x00}, {0x3b09, 0x02}, {0x3b0a, 0x04}, {0x3b0b, 0x00}, {0x3b0c, 0x3d}, {0x3f01, 0x0d}, {0x3f0f, 0xf5}, {0x4000, 0x89}, {0x4001, 0x02}, {0x4002, 0x45}, {0x4004, 0x02}, {0x4005, 0x18}, {0x4006, 0x08}, {0x4007, 0x10}, {0x4008, 0x00}, {0x4050, 0x6e}, {0x4051, 0x8f}, {0x4300, 0xf8}, {0x4303, 0xff}, {0x4304, 0x00}, {0x4307, 0xff}, {0x4520, 0x00}, {0x4521, 0x00}, {0x4511, 0x22}, {0x4801, 0x0f}, {0x4814, 0x2a}, {0x481f, 0x3c}, {0x4823, 0x3c}, {0x4826, 0x00}, {0x481b, 0x3c}, {0x4827, 0x32}, {0x4837, 0x18}, {0x4b00, 0x06}, {0x4b01, 0x0a}, {0x4b04, 0x10}, {0x5000, 0xff}, {0x5001, 0x00}, {0x5002, 0x41}, {0x5003, 0x0a}, {0x5004, 0x00}, {0x5043, 0x00}, {0x5013, 0x00}, {0x501f, 0x03}, {0x503d, 0x00}, {0x5780, 0xfc}, {0x5781, 0x1f}, {0x5782, 0x03}, {0x5786, 0x20}, {0x5787, 0x40}, {0x5788, 0x08}, {0x5789, 0x08}, {0x578a, 0x02}, {0x578b, 0x01}, {0x578c, 0x01}, {0x578d, 0x0c}, {0x578e, 0x02}, {0x578f, 0x01}, {0x5790, 0x01}, {0x5a00, 0x08}, {0x5b00, 0x01}, {0x5b01, 0x40}, {0x5b02, 0x00}, {0x5b03, 0xf0}, }; static struct msm_camera_i2c_reg_array res0_reg_array[] = { //capture preview // 2592x1944 15fps 2 lane MIPI 420Mbps/lane {0x3501, 0x7b}, /* EXPOSURE (Exposure[15:8]) */ {0x3502, 0x00}, /* EXPOSURE (Exposure[7:0]) */ {0x3708, 0x63}, /* Analog Control Register */ {0x3709, 0x12}, /* Analog Control Register */ {0x370c, 0xcc}, /* Analog Control Register */ /* timing control */ {0x3800, 0x00}, {0x3801, 0x00}, {0x3802, 0x00}, {0x3803, 0x00}, {0x3804, 0x0a}, {0x3805, 0x3f}, {0x3806, 0x07}, {0x3807, 0xa3}, {0x3808, 0x0a}, {0x3809, 0x20}, {0x380a, 0x07}, {0x380b, 0x98}, {0x380c, 0x0b}, {0x380d, 0x00}, {0x380e, 0x07}, {0x380f, 0xc0}, {0x3810, 0x00}, {0x3811, 0x10}, {0x3812, 0x00}, {0x3813, 0x06}, {0x3814, 0x11}, {0x3815, 0x11}, {0x3817, 0x00}, {0x3820, 0x40}, //mirror {0x3821, 0x01}, //flip /* {0x3820, 0x46}, {0x3821, 0x07}, */ {0x4004, 0x04}, {0x4005, 0x1a}, {0x350b, 0x40}, {0x4837, 0x17}, }; static struct msm_camera_i2c_reg_array res1_reg_array[] = { // 1296x972 30fps 2 lane MIPI 420Mbps/lane {0x3501, 0x3d}, {0x3502, 0x00}, {0x3708, 0x66}, {0x3709, 0x52}, {0x370c, 0xcf}, {0x3800, 0x00}, {0x3801, 0x00}, {0x3802, 0x00}, {0x3803, 0x00}, {0x3804, 0x0a}, {0x3805, 0x3f}, {0x3806, 0x07}, {0x3807, 0xa3}, {0x3808, 0x05}, {0x3809, 0x10}, {0x380a, 0x03}, {0x380b, 0xcc}, {0x380c, 0x0B}, {0x380d, 0x00}, {0x380e, 0x03}, {0x380f, 0xe0}, {0x3810, 0x00}, {0x3811, 0x08}, {0x3812, 0x00}, {0x3813, 0x04}, {0x3814, 0x31}, {0x3815, 0x31}, {0x3817, 0x00}, {0x3820, 0x48}, {0x3821, 0x01}, /* {0x3820, 0x4e}, {0x3821, 0x07}, */ {0x4004, 0x02}, {0x4005, 0x18}, {0x350b, 0x80}, {0x4837, 0x17}, }; static struct msm_camera_i2c_reg_array res2_reg_array[] = { // 1296x972 60fps 2 lane MIPI 420Mbps/lane {0x3501, 0x3d}, {0x3502, 0x00}, {0x3708, 0x66}, {0x3709, 0x52}, {0x370c, 0xcf}, {0x3800, 0x00}, {0x3801, 0x00}, {0x3802, 0x00}, {0x3803, 0x00}, {0x3804, 0x0a}, {0x3805, 0x3f}, {0x3806, 0x07}, {0x3807, 0xa3}, {0x3808, 0x05}, {0x3809, 0x10}, {0x380a, 0x03}, {0x380b, 0xcc}, {0x380c, 0x05}, {0x380d, 0x80}, {0x380e, 0x03}, {0x380f, 0xe0}, {0x3810, 0x00}, {0x3811, 0x08}, {0x3812, 0x00}, {0x3813, 0x04}, {0x3814, 0x31}, {0x3815, 0x31}, {0x3817, 0x00}, {0x3820, 0x08}, {0x3821, 0x01}, /* {0x3820, 0x0e}, {0x3821, 0x07}, */ {0x4004, 0x02}, {0x4005, 0x18}, {0x350b, 0x80}, {0x4837, 0x17}, }; static struct msm_camera_i2c_reg_array res3_reg_array[] = { //video preview // 640x480 90fps 2lane 10Bit(Binning+Skipping) MIPI 420Mbps/lane {0x3501, 0x1e}, {0x3502, 0xc0}, {0x3708, 0x69}, {0x3709, 0x92}, {0x3800, 0x00}, {0x3801, 0x00}, {0x3802, 0x00}, {0x3803, 0x02}, {0x3804, 0x0a}, {0x3805, 0x3f}, {0x3806, 0x07}, {0x3807, 0xa1}, {0x3808, 0x02}, {0x3809, 0x80}, {0x380a, 0x01}, {0x380b, 0xe0}, {0x380c, 0x07}, {0x380d, 0x28}, {0x380e, 0x01}, {0x380f, 0xfc}, {0x3810, 0x00}, {0x3811, 0x08}, {0x3812, 0x00}, {0x3813, 0x04}, {0x3814, 0x71}, {0x3815, 0x53}, {0x3817, 0x00}, {0x3820, 0x48}, {0x3821, 0x01}, /* {0x3820, 0x4e}, {0x3821, 0x07}, */ {0x4004, 0x02}, {0x4005, 0x18}, {0x350b, 0xf0}, {0x4837, 0x17}, }; static struct msm_camera_i2c_reg_array res4_reg_array[] = { // 320x240 120fps 2lane 10Bit(Binning+Skipping) MIPI 420Mbps/lane {0x3501, 0x0f}, {0x3502, 0xb0}, {0x3708, 0x69}, {0x3709, 0xd2}, {0x3800, 0x00}, {0x3801, 0x00}, {0x3802, 0x00}, {0x3803, 0x02}, {0x3804, 0x0a}, {0x3805, 0x3f}, {0x3806, 0x07}, {0x3807, 0xa1}, {0x3808, 0x01}, {0x3809, 0x40}, {0x380a, 0x00}, {0x380b, 0xf0}, {0x380c, 0x0a}, {0x380d, 0x38}, {0x380e, 0x01}, {0x380f, 0x0c}, {0x3810, 0x00}, {0x3811, 0x04}, {0x3812, 0x00}, {0x3813, 0x02}, {0x3814, 0x97}, {0x3815, 0x97}, {0x3817, 0x00}, {0x3820, 0x48}, {0x3821, 0x01}, /* {0x3820, 0x4e}, {0x3821, 0x07}, */ {0x4004, 0x02}, {0x4005, 0x18}, {0x350b, 0xff}, {0x4837, 0x17}, }; #endif
/* ov5648_8909_lib.c * * Copyright (c) 2015 Qualcomm Technologies, Inc. All Rights Reserved. * Qualcomm Technologies Proprietary and Confidential. */ #include <stdio.h> #include "sensor_lib.h" #include "ov5648_8909_lib.h" #define SENSOR_MODEL_NO_OV5648_8909 "ov5648_8909" #define OV5648_8909_LOAD_CHROMATIX(n) \ "libchromatix_"SENSOR_MODEL_NO_OV5648_8909"_"#n".so" #undef DEBUG_INFO #define OV5648_8909_DEBUG #ifdef OV5648_8909_DEBUG #include <utils/Log.h> #define SERR(fmt, args...) \ ALOGE("%s:%d "fmt"\n", __func__, __LINE__, ##args) #define DEBUG_INFO(fmt, args...) SERR(fmt, ##args) #else #define DEBUG_INFO(fmt, args...) do { } while (0) #endif static sensor_lib_t sensor_lib_ptr; static struct msm_sensor_power_setting power_setting[] = { { .seq_type = SENSOR_VREG, .seq_val = CAM_VIO, .config_val = 0, .delay = 0, }, { .seq_type = SENSOR_VREG, .seq_val = CAM_VANA, .config_val = 0, .delay = 0, }, /* { .seq_type = SENSOR_VREG, .seq_val = CAM_VAF, .config_val = 0, .delay = 0, }, { .seq_type = SENSOR_GPIO, .seq_val = SENSOR_GPIO_AF_PWDM, .config_val = GPIO_OUT_LOW, .delay = 1, }, { .seq_type = SENSOR_GPIO, .seq_val = SENSOR_GPIO_AF_PWDM, .config_val = GPIO_OUT_HIGH, .delay = 5, }, */ //luffy add { .seq_type = SENSOR_GPIO, .seq_val = SENSOR_GPIO_VANA, .config_val = GPIO_OUT_HIGH, .delay = 10, }, { .seq_type = SENSOR_GPIO, .seq_val = SENSOR_GPIO_VDIG, .config_val = GPIO_OUT_HIGH, .delay = 10, }, { .seq_type = SENSOR_GPIO, .seq_val = SENSOR_GPIO_VIO, .config_val = GPIO_OUT_HIGH, .delay = 10, }, { .seq_type = SENSOR_VREG, .seq_val = CAM_VDIG, .config_val = 0, .delay = 1, }, { .seq_type = SENSOR_GPIO, .seq_val = SENSOR_GPIO_RESET, .config_val = GPIO_OUT_LOW, .delay = 0, }, { .seq_type = SENSOR_GPIO, .seq_val = SENSOR_GPIO_RESET, .config_val = GPIO_OUT_HIGH, .delay = 10, }, { .seq_type = SENSOR_GPIO, .seq_val = SENSOR_GPIO_STANDBY, .config_val = GPIO_OUT_LOW, .delay = 0, }, { .seq_type = SENSOR_GPIO, .seq_val = SENSOR_GPIO_STANDBY, .config_val = GPIO_OUT_HIGH, .delay = 5, }, { .seq_type = SENSOR_CLK, .seq_val = SENSOR_CAM_MCLK, .config_val = 24000000, .delay = 10, }, { .seq_type = SENSOR_I2C_MUX, .seq_val = 0, .config_val = 0, .delay = 0, }, }; static struct msm_sensor_power_setting power_down_setting[] = { { .seq_type = SENSOR_GPIO, .seq_val = SENSOR_GPIO_VANA, .config_val = GPIO_OUT_LOW, .delay = 0, }, { .seq_type = SENSOR_GPIO, .seq_val = SENSOR_GPIO_VDIG, .config_val = GPIO_OUT_LOW, .delay = 0, }, { .seq_type = SENSOR_GPIO, .seq_val = SENSOR_GPIO_VIO, .config_val = GPIO_OUT_LOW, .delay = 10, }, { .seq_type = SENSOR_GPIO, .seq_val = SENSOR_GPIO_STANDBY, .config_val = GPIO_OUT_LOW, .delay = 0, }, { .seq_type = SENSOR_CLK, .seq_val = SENSOR_CAM_MCLK, .config_val = 0, .delay = 10, }, }; static struct msm_camera_sensor_slave_info sensor_slave_info = { /* Camera slot where this camera is mounted */ .camera_id = CAMERA_0, /* sensor slave address */ .slave_addr = 0x6c, /* sensor i2c frequency*/ .i2c_freq_mode = I2C_FAST_MODE, /* sensor address type */ .addr_type = MSM_CAMERA_I2C_WORD_ADDR, /* sensor id info*/ .sensor_id_info = { /* sensor id register address */ .sensor_id_reg_addr = 0x300a, /* sensor id */ .sensor_id = 0x5648, }, /* power up / down setting */ .power_setting_array = { .power_setting = power_setting, .size = ARRAY_SIZE(power_setting), .power_down_setting = power_down_setting, .size_down = ARRAY_SIZE(power_down_setting), }, }; static struct msm_sensor_init_params sensor_init_params = { .modes_supported = CAMERA_MODE_2D_B, /* sensor position (back or front) */ .position = BACK_CAMERA_B, /* sensor mounting direction */ .sensor_mount_angle = SENSOR_MOUNTANGLE_270, }; static sensor_output_t sensor_output = { /* sensor image format */ .output_format = SENSOR_BAYER, /* sensor data output interface */ .connection_mode = SENSOR_MIPI_CSI, /* sensor data */ .raw_output = SENSOR_10_BIT_DIRECT, }; static struct msm_sensor_output_reg_addr_t output_reg_addr = { .x_output = 0x3808, .y_output = 0x380a, .line_length_pclk = 0x380c, .frame_length_lines = 0x380e, }; static struct msm_sensor_exp_gain_info_t exp_gain_info = { .coarse_int_time_addr = 0x3500, .global_gain_addr = 0x350a, .vert_offset = 4, }; static sensor_aec_data_t aec_info = { .max_gain = 12.0, .max_linecount = 26880, }; static sensor_lens_info_t default_lens_info = { .focal_length = 3.16, .pix_size = 1.4, .f_number = 2.4, .total_f_dist = 1.2, .hor_view_angle = 56.0, .ver_view_angle = 42.0, }; /* mipi_csi interface lane config */ #ifndef VFE_40 static struct csi_lane_params_t csi_lane_params = { .csi_lane_assign = 0x4320, .csi_lane_mask = 0x7, .csi_if = 1, .csid_core = {0}, .csi_phy_sel = 0, }; #else static struct csi_lane_params_t csi_lane_params = { .csi_lane_assign = 0x4320, .csi_lane_mask = 0x7, .csi_if = 1, .csid_core = {0}, .csi_phy_sel = 0, }; #endif static struct msm_camera_i2c_reg_setting init_reg_setting[] = { { .reg_setting = init_reg_array, .size = ARRAY_SIZE(init_reg_array), .addr_type = MSM_CAMERA_I2C_WORD_ADDR, .data_type = MSM_CAMERA_I2C_BYTE_DATA, .delay = 0, }, }; static struct sensor_lib_reg_settings_array init_settings_array = { .reg_settings = init_reg_setting, .size = 1, }; static struct msm_camera_i2c_reg_array start_reg_array[] = { {0x0100, 0x01}, {0x4800, 0x04}, {0x4202, 0x00}, /* streaming on */ }; static struct msm_camera_i2c_reg_setting start_settings = { .reg_setting = start_reg_array, .size = ARRAY_SIZE(start_reg_array), .addr_type = MSM_CAMERA_I2C_WORD_ADDR, .data_type = MSM_CAMERA_I2C_BYTE_DATA, .delay = 10, }; static struct msm_camera_i2c_reg_array stop_reg_array[] = { {0x0100, 0x00}, {0x4800, 0x25}, {0x4202, 0x0f}, /* streaming off*/ }; static struct msm_camera_i2c_reg_setting stop_settings = { .reg_setting = stop_reg_array, .size = ARRAY_SIZE(stop_reg_array), .addr_type = MSM_CAMERA_I2C_WORD_ADDR, .data_type = MSM_CAMERA_I2C_BYTE_DATA, .delay = 10, }; static struct msm_camera_i2c_reg_array groupon_reg_array[] = { {0x3208, 0x00}, }; static struct msm_camera_i2c_reg_setting groupon_settings = { .reg_setting = groupon_reg_array, .size = ARRAY_SIZE(groupon_reg_array), .addr_type = MSM_CAMERA_I2C_WORD_ADDR, .data_type = MSM_CAMERA_I2C_BYTE_DATA, .delay = 0, }; static struct msm_camera_i2c_reg_array groupoff_reg_array[] = { {0x3208, 0x10}, {0x3208, 0xA0}, }; static struct msm_camera_i2c_reg_setting groupoff_settings = { .reg_setting = groupoff_reg_array, .size = ARRAY_SIZE(groupoff_reg_array), .addr_type = MSM_CAMERA_I2C_WORD_ADDR, .data_type = MSM_CAMERA_I2C_BYTE_DATA, .delay = 0, }; static struct msm_camera_csid_vc_cfg ov5648_8909_cid_cfg[] = { {0, CSI_RAW10, CSI_DECODE_10BIT}, {1, CSI_EMBED_DATA, CSI_DECODE_8BIT}, }; static struct msm_camera_csi2_params ov5648_8909_csi_params = { .csid_params = { /* mipi data lane number */ .lane_cnt = 2, .lut_params = { .num_cid = ARRAY_SIZE(ov5648_8909_cid_cfg), .vc_cfg = { &ov5648_8909_cid_cfg[0], &ov5648_8909_cid_cfg[1], }, }, }, /* mipi csi phy config */ .csiphy_params = { .lane_cnt = 2, .settle_cnt = 0x1B, #ifndef VFE_40 .combo_mode = 1, #endif }, }; static struct sensor_pix_fmt_info_t ov5648_8909_pix_fmt0_fourcc[] = { { V4L2_PIX_FMT_SBGGR10 }, }; static struct sensor_pix_fmt_info_t ov5648_8909_pix_fmt1_fourcc[] = { { MSM_V4L2_PIX_FMT_META }, }; static sensor_stream_info_t ov5648_8909_stream_info[] = { {1, &ov5648_8909_cid_cfg[0], ov5648_8909_pix_fmt0_fourcc}, {1, &ov5648_8909_cid_cfg[1], ov5648_8909_pix_fmt1_fourcc}, }; static sensor_stream_info_array_t ov5648_8909_stream_info_array = { .sensor_stream_info = ov5648_8909_stream_info, .size = ARRAY_SIZE(ov5648_8909_stream_info), }; static struct msm_camera_i2c_reg_setting res_settings[] = { { .reg_setting = res0_reg_array, .size = ARRAY_SIZE(res0_reg_array), .addr_type = MSM_CAMERA_I2C_WORD_ADDR, .data_type = MSM_CAMERA_I2C_BYTE_DATA, .delay = 0, }, #if 0 { .reg_setting = res1_reg_array, .size = ARRAY_SIZE(res1_reg_array), .addr_type = MSM_CAMERA_I2C_WORD_ADDR, .data_type = MSM_CAMERA_I2C_BYTE_DATA, .delay = 0, }, { .reg_setting = res2_reg_array, .size = ARRAY_SIZE(res2_reg_array), .addr_type = MSM_CAMERA_I2C_WORD_ADDR, .data_type = MSM_CAMERA_I2C_BYTE_DATA, .delay = 0, }, { .reg_setting = res3_reg_array, .size = ARRAY_SIZE(res3_reg_array), .addr_type = MSM_CAMERA_I2C_WORD_ADDR, .data_type = MSM_CAMERA_I2C_BYTE_DATA, .delay = 0, }, { .reg_setting = res4_reg_array, .size = ARRAY_SIZE(res4_reg_array), .addr_type = MSM_CAMERA_I2C_WORD_ADDR, .data_type = MSM_CAMERA_I2C_BYTE_DATA, .delay = 0, }, #endif }; static struct sensor_lib_reg_settings_array res_settings_array = { .reg_settings = res_settings, .size = ARRAY_SIZE(res_settings), }; static struct msm_camera_csi2_params *csi_params[] = { &ov5648_8909_csi_params, /* RES 0*/ //&ov5648_8909_csi_params, /* RES 1*/ //&ov5648_8909_csi_params, /* RES 2*/ //&ov5648_8909_csi_params, /* RES 3*/ //&ov5648_8909_csi_params, /* RES 4*/ }; static struct sensor_lib_csi_params_array csi_params_array = { .csi2_params = &csi_params[0], .size = ARRAY_SIZE(csi_params), }; static struct sensor_crop_parms_t crop_params[] = { { 0, 0, 0, 0 }, /* RES 0 */ //{ 0, 0, 0, 0 }, /* RES 1 */ //{ 0, 0, 0, 0 }, /* RES 2 */ //{ 0, 0, 0, 0 }, /* RES 3 */ //{ 0, 0, 0, 0 }, /* RES 4 */ }; static struct sensor_lib_crop_params_array crop_params_array = { .crop_params = crop_params, .size = ARRAY_SIZE(crop_params), }; static struct sensor_lib_out_info_t sensor_out_info[] = { { /* full size @ 15 fps */ .x_output = 0xa20, /* 2592 */ .y_output = 0x798, /* 1944 */ .line_length_pclk = 0xb00, /* 2816 */ .frame_length_lines = 0x7c0, /* 1984 */ .vt_pixel_clk = 83800000, .op_pixel_clk = 84000000, .binning_factor = 1, .max_fps = 15.0, .min_fps = 3.75, .mode = SENSOR_DEFAULT_MODE, }, #if 0 { /* 1/4 size @ 30 fps */ .x_output = 0x510, /* 1296 */ .y_output = 0x3cc, /* 972 */ .line_length_pclk = 0xb00, /* 2816 */ .frame_length_lines = 0x3e0, /* 992 */ .vt_pixel_clk = 83800000, .op_pixel_clk = 84000000, .binning_factor = 1, .max_fps = 30.0, .min_fps = 7.5, .mode = SENSOR_DEFAULT_MODE, }, { /* 1/4 size @ 60 fps */ .x_output = 0x510, /* 1296 */ .y_output = 0x3cc, /* 972 */ .line_length_pclk = 0x580, /* 1408 */ .frame_length_lines = 0x3e0, /* 992 */ .vt_pixel_clk = 83800000, .op_pixel_clk = 84000000, .binning_factor = 1, .max_fps = 60.00, .min_fps = 7.5, .mode = SENSOR_DEFAULT_MODE, }, { /* VGA @ 90 fps */ .x_output = 0x280, /* 640 */ .y_output = 0x1E0, /* 480 */ .line_length_pclk = 0x728, /* 1832 */ .frame_length_lines = 0x1fc, /* 508 */ .vt_pixel_clk = 83800000, .op_pixel_clk = 84000000, .binning_factor = 1, .max_fps = 90.00, .min_fps = 7.5, .mode = SENSOR_DEFAULT_MODE, }, { /* QVGA @ 120 fps */ .x_output = 0x140, /* 320 */ .y_output = 0xF0, /* 240 */ .line_length_pclk = 0xa38, /* 2616 */ .frame_length_lines = 0x10c, /* 268 */ .vt_pixel_clk = 83800000, .op_pixel_clk = 84000000, .binning_factor = 1, .max_fps = 120.00, .min_fps = 7.5, .mode = SENSOR_DEFAULT_MODE, }, #endif }; static struct sensor_lib_out_info_array out_info_array = { .out_info = sensor_out_info, .size = ARRAY_SIZE(sensor_out_info), }; static sensor_res_cfg_type_t ov5648_8909_res_cfg[] = { SENSOR_SET_STOP_STREAM, SENSOR_SET_NEW_RESOLUTION, /* set stream config */ SENSOR_SET_CSIPHY_CFG, SENSOR_SET_CSID_CFG, SENSOR_LOAD_CHROMATIX, /* set chromatix prt */ SENSOR_SEND_EVENT, /* send event */ SENSOR_SET_START_STREAM, }; static struct sensor_res_cfg_table_t ov5648_8909_res_table = { .res_cfg_type = ov5648_8909_res_cfg, .size = ARRAY_SIZE(ov5648_8909_res_cfg), }; static struct sensor_lib_chromatix_t ov5648_8909_chromatix[] = { { .common_chromatix = OV5648_8909_LOAD_CHROMATIX(common), .camera_preview_chromatix = OV5648_8909_LOAD_CHROMATIX(zsl), /* RES0 */ .camera_snapshot_chromatix = OV5648_8909_LOAD_CHROMATIX(snapshot), /* RES0 */ .camcorder_chromatix = OV5648_8909_LOAD_CHROMATIX(default_video), /* RES0 */ .liveshot_chromatix = OV5648_8909_LOAD_CHROMATIX(liveshot), /* RES0 */ }, #if 0 { .common_chromatix = OV5648_8909_LOAD_CHROMATIX(common), .camera_preview_chromatix = OV5648_8909_LOAD_CHROMATIX(preview), /* RES1 */ .camera_snapshot_chromatix = OV5648_8909_LOAD_CHROMATIX(preview), /* RES1 */ .camcorder_chromatix = OV5648_8909_LOAD_CHROMATIX(default_video), /* RES1 */ .liveshot_chromatix = OV5648_8909_LOAD_CHROMATIX(liveshot), /* RES1 */ }, { .common_chromatix = OV5648_8909_LOAD_CHROMATIX(common), .camera_preview_chromatix = OV5648_8909_LOAD_CHROMATIX(hfr_60fps), /* RES2 */ .camera_snapshot_chromatix = OV5648_8909_LOAD_CHROMATIX(hfr_60fps), /* RES2 */ .camcorder_chromatix = OV5648_8909_LOAD_CHROMATIX(hfr_60fps), /* RES2 */ .liveshot_chromatix = OV5648_8909_LOAD_CHROMATIX(liveshot), /* RES2 */ }, { .common_chromatix = OV5648_8909_LOAD_CHROMATIX(common), .camera_preview_chromatix = OV5648_8909_LOAD_CHROMATIX(hfr_90fps), /* RES3 */ .camera_snapshot_chromatix = OV5648_8909_LOAD_CHROMATIX(hfr_90fps), /* RES3 */ .camcorder_chromatix = OV5648_8909_LOAD_CHROMATIX(hfr_90fps), /* RES3 */ .liveshot_chromatix = OV5648_8909_LOAD_CHROMATIX(liveshot), /* RES3 */ }, { .common_chromatix = OV5648_8909_LOAD_CHROMATIX(common), .camera_preview_chromatix = OV5648_8909_LOAD_CHROMATIX(hfr_120fps), /* RES4 */ .camera_snapshot_chromatix = OV5648_8909_LOAD_CHROMATIX(hfr_120fps), /* RES4 */ .camcorder_chromatix = OV5648_8909_LOAD_CHROMATIX(hfr_120fps), /* RES4 */ .liveshot_chromatix = OV5648_8909_LOAD_CHROMATIX(liveshot), /* RES4 */ }, #endif }; static struct sensor_lib_chromatix_array ov5648_8909_lib_chromatix_array = { .sensor_lib_chromatix = ov5648_8909_chromatix, .size = ARRAY_SIZE(ov5648_8909_chromatix), }; /*=========================================================================== * FUNCTION - ov5648_8909_real_to_register_gain - * * DESCRIPTION: *==========================================================================*/ static uint16_t ov5648_8909_real_to_register_gain(float gain) { uint16_t reg_gain, reg_temp; if(gain > 10.0) gain = 10.0; if(gain < 1.0) gain = 1.0; reg_gain = (uint16_t)gain; reg_temp = reg_gain<<4; reg_gain = reg_temp | (((uint16_t)((gain - (float)reg_gain)*16.0))&0x0f); return reg_gain; } /*=========================================================================== * FUNCTION - ov5648_8909_register_to_real_gain - * * DESCRIPTION: *==========================================================================*/ static float ov5648_8909_register_to_real_gain(uint16_t reg_gain) { float real_gain; real_gain = (float) ((float)(reg_gain>>4)+(((float)(reg_gain&0x0f))/16.0)); return real_gain; } /*=========================================================================== * FUNCTION - ov5648_8909_calculate_exposure - * * DESCRIPTION: *==========================================================================*/ static int32_t ov5648_8909_calculate_exposure(float real_gain, uint16_t line_count, sensor_exposure_info_t *exp_info) { if (!exp_info) { return -1; } exp_info->reg_gain = ov5648_8909_real_to_register_gain(real_gain); float sensor_real_gain = ov5648_8909_register_to_real_gain(exp_info->reg_gain); exp_info->digital_gain = real_gain / sensor_real_gain; exp_info->line_count = line_count; return 0; } /*=========================================================================== * FUNCTION - ov5648_8909_fill_exposure_array - * * DESCRIPTION: *==========================================================================*/ static int32_t ov5648_8909_fill_exposure_array(uint16_t gain, uint32_t line, uint32_t fl_lines, int32_t luma_avg, uint32_t fgain, struct msm_camera_i2c_reg_setting* reg_setting) { uint16_t reg_count = 0; uint16_t i = 0; if (!reg_setting) { return -1; } for (i = 0; i < sensor_lib_ptr.groupon_settings->size; i++) { reg_setting->reg_setting[reg_count].reg_addr = sensor_lib_ptr.groupon_settings->reg_setting[i].reg_addr; reg_setting->reg_setting[reg_count].reg_data = sensor_lib_ptr.groupon_settings->reg_setting[i].reg_data; reg_count = reg_count + 1; } reg_setting->reg_setting[reg_count].reg_addr = sensor_lib_ptr.output_reg_addr->frame_length_lines; reg_setting->reg_setting[reg_count].reg_data = (fl_lines & 0xFF00) >> 8; reg_count = reg_count + 1; reg_setting->reg_setting[reg_count].reg_addr = sensor_lib_ptr.output_reg_addr->frame_length_lines + 1; reg_setting->reg_setting[reg_count].reg_data = (fl_lines & 0xFF); reg_count = reg_count + 1; reg_setting->reg_setting[reg_count].reg_addr = sensor_lib_ptr.exp_gain_info->coarse_int_time_addr; reg_setting->reg_setting[reg_count].reg_data = line >> 12; reg_count = reg_count + 1; reg_setting->reg_setting[reg_count].reg_addr = sensor_lib_ptr.exp_gain_info->coarse_int_time_addr + 1; reg_setting->reg_setting[reg_count].reg_data = line >> 4; reg_count = reg_count + 1; reg_setting->reg_setting[reg_count].reg_addr = sensor_lib_ptr.exp_gain_info->coarse_int_time_addr + 2; reg_setting->reg_setting[reg_count].reg_data = line << 4; reg_count = reg_count + 1; reg_setting->reg_setting[reg_count].reg_addr = sensor_lib_ptr.exp_gain_info->global_gain_addr; reg_setting->reg_setting[reg_count].reg_data = (gain & 0xFF00) >> 8; reg_count = reg_count + 1; reg_setting->reg_setting[reg_count].reg_addr = sensor_lib_ptr.exp_gain_info->global_gain_addr + 1; reg_setting->reg_setting[reg_count].reg_data = (gain & 0xFF); reg_count = reg_count + 1; for (i = 0; i < sensor_lib_ptr.groupoff_settings->size; i++) { reg_setting->reg_setting[reg_count].reg_addr = sensor_lib_ptr.groupoff_settings->reg_setting[i].reg_addr; reg_setting->reg_setting[reg_count].reg_data = sensor_lib_ptr.groupoff_settings->reg_setting[i].reg_data; reg_count = reg_count + 1; } reg_setting->size = reg_count; reg_setting->addr_type = MSM_CAMERA_I2C_WORD_ADDR; reg_setting->data_type = MSM_CAMERA_I2C_BYTE_DATA; reg_setting->delay = 0; return 0; } static sensor_exposure_table_t ov5648_8909_expsoure_tbl = { .sensor_calculate_exposure = ov5648_8909_calculate_exposure, .sensor_fill_exposure_array = ov5648_8909_fill_exposure_array, }; static sensor_lib_t sensor_lib_ptr = { /* sensor slave info */ .sensor_slave_info = &sensor_slave_info, /* sensor init params */ .sensor_init_params = &sensor_init_params, /* sensor actuator name */ .actuator_name = "a3907", /* module eeprom name */ .eeprom_name = "sunny_q5v22e", /* sensor output settings */ .sensor_output = &sensor_output, /* sensor output register address */ .output_reg_addr = &output_reg_addr, /* sensor exposure gain register address */ .exp_gain_info = &exp_gain_info, /* sensor aec info */ .aec_info = &aec_info, /* sensor snapshot exposure wait frames info */ .snapshot_exp_wait_frames = 2, /* number of frames to skip after start stream */ .sensor_num_frame_skip = 2, /* number of frames to skip after start HDR stream */ .sensor_num_HDR_frame_skip = 2, /* sensor exposure table size */ .exposure_table_size = 10, /* sensor lens info */ .default_lens_info = &default_lens_info, /* csi lane params */ .csi_lane_params = &csi_lane_params, /* csi cid params */ .csi_cid_params = ov5648_8909_cid_cfg, /* csi csid params array size */ .csi_cid_params_size = ARRAY_SIZE(ov5648_8909_cid_cfg), /* init settings */ .init_settings_array = &init_settings_array, /* start settings */ .start_settings = &start_settings, /* stop settings */ .stop_settings = &stop_settings, /* group on settings */ .groupon_settings = &groupon_settings, /* group off settings */ .groupoff_settings = &groupoff_settings, /* resolution cfg table */ .sensor_res_cfg_table = &ov5648_8909_res_table, /* res settings */ .res_settings_array = &res_settings_array, /* out info array */ .out_info_array = &out_info_array, /* crop params array */ .crop_params_array = &crop_params_array, /* csi params array */ .csi_params_array = &csi_params_array, /* sensor port info array */ .sensor_stream_info_array = &ov5648_8909_stream_info_array, /* exposure funtion table */ .exposure_func_table = &ov5648_8909_expsoure_tbl, /* chromatix array */ .chromatix_array = &ov5648_8909_lib_chromatix_array, }; /*=========================================================================== * FUNCTION - ov5648_8909_open_lib - * * DESCRIPTION: *==========================================================================*/ void *ov5648_8909_open_lib(void) { return &sensor_lib_ptr; }
接下来,添加摄像头效果驱动文件,文件路径如下:
android7/vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/sensors/chromatix/0301/libchromatix/chromatix_ov5648_8909
添加完成后,修改编译脚本,使得相关摄像头驱动源文件编译成动态库,路径如下:
android7/vendor/qcom/proprietary/common/config/device-vendor.mk
添加如下代码:
# add ov5648 sensor MM_CAMERA += libmmcamera_ov5648_8909 MM_CAMERA += libchromatix_ov5648_8909_common MM_CAMERA += libchromatix_ov5648_8909_cpp MM_CAMERA += libchromatix_ov5648_8909_default_video MM_CAMERA += libchromatix_ov5648_8909_preview MM_CAMERA += libchromatix_ov5648_8909_snapshot MM_CAMERA += libchromatix_ov5648_8909_video_hd MM_CAMERA += libchromatix_ov5648_8909_zsl MM_CAMERA += libchromatix_ov5648_8909_hfr_120fps MM_CAMERA += libchromatix_ov5648_8909_hfr_60fps MM_CAMERA += libchromatix_ov5648_8909_hfr_90fps MM_CAMERA += libchromatix_ov5648_8909_liveshot
添加sensor name在sensor_libs[]数组中,在系统启动中会进行sensor的probe,文件路径如下:
android7/vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/sensors/module/sensor_ini
t.c
代码修改如下:
static const char *sensor_libs[] = { "ov5648_8909", };
最后,编译源码,检查是否已经成功生成了相关的摄像头驱动动态库,并更新镜像到板子上进行效果验证。
4、调试结果分析
启动Android7系统后,抓取debug串口log进行分析,检查Linux内核启动log中是否有camera probe成功的信息,如下:
如果没有检查到该probe成功的log,需要检查上面提到的调试流程,比如上下电时序、I2C通信等。
使用逻辑分析仪抓取Sensor冷启动的波形,波形如下:
通道0到通道4依次是电源使能引脚、RESET引脚、POWD引脚、I2C时钟线、I2C数据线,通过I2C引脚正确读取到Sensor ID的波形如下:
可以看到主控通过I2C接口已经成功地从Sensor的0x300A寄存器起始地址读取到0x5648的两字节数据,这个就是Sensor的ID。
还有就是MCLK引脚的波形,在这里的驱动代码中配置了24MHz,使用示波器抓取波形如下:
打开Android7原生的骁龙相机应用检查是否已经能点亮Sensor了,如果没图像出来则需要进一步作分析。
5、小结
本文简要介绍了如何在MSM8909 Android7系统中驱动点亮Camera OV5648的基本流程。