camera上电时序

今天有空去研究camera的上电时序,对了,如果你喜欢文学,历史,哲学,可以关注公众号:二校五叔

1.高通平台对于camera的代码组织,大体上还是遵循Android的框架:
即上层应用和HAL层交互,高通平台在HAL层里面实现自己的一套管理策略;
在kernel中实现sensor的底层驱动;
对于最核心的sensor端的底层设置、ISP效果相关等代码则是单独进行了抽离,放在vendor中。

2.vendor中:
\vendor\qcom\proprietary\mm-camera\mm-camera2\media-controller\modules\sensors\sensor_libs\ov5648_q5v22e

下面这个结构体便是上电时序

static struct msm_sensor_power_setting ov5648_q5v22e_power_setting[] = {
	{
		.seq_type = SENSOR_GPIO,
		.seq_val = SENSOR_GPIO_STANDBY,
		.config_val = GPIO_OUT_LOW,
		.delay = 2,
	},
	{
		.seq_type = SENSOR_GPIO,
		.seq_val = SENSOR_GPIO_RESET,
		.config_val = GPIO_OUT_LOW,
		.delay = 2,
	},
	{
		.seq_type = SENSOR_VREG,
		.seq_val = CAM_VIO,
		.config_val = 0,
		.delay = 0,
	},
	{
		.seq_type = SENSOR_GPIO,
		.seq_val = SENSOR_GPIO_VANA,
		.config_val = GPIO_OUT_HIGH,
		.delay = 1,
	},
	{
		.seq_type = SENSOR_GPIO,
		.seq_val = SENSOR_GPIO_VDIG,
		.config_val = GPIO_OUT_HIGH,
		.delay = 5,
	},

	{
		.seq_type = SENSOR_GPIO,
		.seq_val = SENSOR_GPIO_STANDBY,
		.config_val = GPIO_OUT_HIGH,
		.delay = 10,
	},

	{
		.seq_type = SENSOR_GPIO,
		.seq_val = SENSOR_GPIO_RESET,
		.config_val = GPIO_OUT_HIGH,
		.delay = 5,
	},
	{
		.seq_type = SENSOR_CLK,
		.seq_val = SENSOR_CAM_MCLK,
		.config_val = 23880000,//24000000,//19200000,//23880000,
		.delay = 5,
	},
	{
		.seq_type = SENSOR_I2C_MUX,
		.seq_val = 0,
		.config_val = 0,
		.delay = 10,
	},
};

它定义在下面的结构体中:

static struct msm_camera_sensor_slave_info sensor_slave_info = {
	...
  /* power up / down setting */
  .power_setting_array = {
    .power_setting = ov5648_q5v22e_power_setting,
    .size = ARRAY_SIZE(ov5648_q5v22e_power_setting),
    .power_down_setting = power_down_setting,
    .size_down = ARRAY_SIZE(power_down_setting),
  },
  };

这些结构体具体在哪里定义呢?在kernel中
/include/media/msm_camsensor_sdk.h中

struct msm_camera_sensor_slave_info {
...
	struct msm_sensor_power_setting_array power_setting_array;
...
};

struct msm_sensor_power_setting {
	enum msm_sensor_power_seq_type_t seq_type;
	uint16_t seq_val;
	long config_val;
	uint16_t delay;
	void *data[10];
};

struct msm_sensor_power_setting_array {
	struct msm_sensor_power_setting  power_setting_a[MAX_POWER_CONFIG];
	struct msm_sensor_power_setting *power_setting;
	uint16_t size;
	struct msm_sensor_power_setting  power_down_setting_a[MAX_POWER_CONFIG];
	struct msm_sensor_power_setting *power_down_setting;
	uint16_t size_down;
};

3.从vendor中把时序结构体的内容传递到kernel中的过程如下:
在msm_sensor_init_subdev_ioctl(…,void *arg) —>函数: msm_sensor_driver_cmd(s_init, arg);
—>语句: struct sensor_init_cfg_data *cfg = (struct sensor_init_cfg_data *)arg;
—>函数:msm_sensor_driver_probe(cfg->cfg.setting,…);
—>语句:copy_from_user((void *)&setting32, setting,sizeof(setting32))将vendor的数据传送到内核空间
—>语句: slave_info->power_setting_array.size =setting32.power_setting_array.size;
slave_info->power_setting_array.power_setting =
compat_ptr(setting32.power_setting_array.power_setting); //把vendor的时序传递完毕
—>语句:rc = msm_sensor_get_power_settings(setting, slave_info,&s_ctrl->sensordata->power_info);
//把slave_info的上电时序传递给s_ctrl结构体
—>语句:msm_camera_fill_vreg_params(…)//校正时序中SENSOR_VREG的seq_val,为设备树的CAM_VIO
—>语句:rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl); //真正执行上电时序

因为.sensor_power_up = msm_sensor_power_up,所以真正的上电函数为:msm_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl)
–>语句:power_info = &s_ctrl->sensordata->power_info; //把s_ctrl的上电信息传递给power_info
–>函数:msm_camera_power_up(power_info, s_ctrl->sensor_device_type,sensor_i2c_client);//上电
ps:vendor中的addr_type、camera_id、slave_addr等信息也是按照这样的方法从vendor中传递到kernel中的,可以加打印调试信息看这些值正确与否。

4.下面解析这个上电函数:

int msm_camera_power_up(struct msm_camera_power_ctrl_t *ctrl,
	enum msm_camera_device_type_t device_type,
	struct msm_camera_i2c_client *sensor_i2c_client)
{
	struct msm_sensor_power_setting *power_setting = NULL;
...
	rc = msm_camera_request_gpio_table( //申请gpio
		ctrl->gpio_conf->cam_gpio_req_tbl,
		ctrl->gpio_conf->cam_gpio_req_tbl_size, 1);
...
index = 0; index < ctrl->power_setting_size; index++) {
		power_setting = &ctrl->power_setting[index];        //把时序的节点一个一个取下来解析
		switch (power_setting->seq_type) {                  //判断类型
		case SENSOR_CLK:
			if (power_setting->config_val)
				ctrl->clk_info[power_setting->seq_val].clk_rate = power_setting->config_val;
			rc = msm_cam_clk_enable(...1);                  //camera频率使能
			...
		case SENSOR_GPIO:
			...
			gpio_set_value_cansleep(ctrl->gpio_conf->gpio_num_info->gpio_num[power_setting->seq_val],
				(int) power_setting->config_val);          //拉高拉低gpio口
			...
		case SENSOR_VREG:
			if (power_setting->seq_val < ctrl->num_vreg)
				msm_camera_config_single_vreg(...,1);  //函数里面打开reg_ptr的电源控制器regulator_enable,下面再解析
			...
		case SENSOR_I2C_MUX:
			if (ctrl->i2c_conf && ctrl->i2c_conf->use_i2c_mux)
				msm_camera_enable_i2c_mux(ctrl->i2c_conf);   //打开使能i2c_mux
			break;
		default:
			...
		}
		//以下是每个时序节点解析完毕后,进行的延迟,可以没有
		if (power_setting->delay > 20) {
			msleep(power_setting->delay);
		} else if (power_setting->delay) {
			usleep_range(power_setting->delay * 1000,(power_setting->delay * 1000) + 1000);
		}
	}
	...
	return 0;
}

最后两个节点比较特殊:
第一个SENSOR_VREG:
在解析设备树节点的时候:

		//从中有一路电vio为0V
		qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";//"cam_vaf";
		qcom,cam-vreg-min-voltage = <1800000 0 2850000 >;//2800000>;
		qcom,cam-vreg-max-voltage = <1800000 0 2850000 >;//2800000>;
		qcom,cam-vreg-op-mode = <200000 0 80000 100000>;
		//在kernel中对设备树节点解析的时候
		for (i = 0; i < count; i++) {
			vreg[i].min_voltage = vreg_array[i];}//存入结构体中
在msm_sensor_driver_probe函数中调用msm_camera_fill_vreg_params
在里面遍历上电时序节点:
for (i = 0; i < power_setting_size; i++)
{
		if (power_setting[i].seq_type != SENSOR_VREG)
			continue;

		switch (power_setting[i].seq_val) {
		case CAM_VDIG:
		...
		case CAM_VIO:
			for (j = 0; j < num_vreg; j++) {
				if (!strcmp(cam_vreg[j].reg_name, "cam_vio")) {
					power_setting[i].seq_val = j;   //让seq_val 对准设备树的seq_val 
					break;
				}
			}
			break;
		case CAM_VANA:
		...
		case CAM_VAF:
		...
		case CAM_V_CUSTOM1:
		...
	}
}
//再进入此节点上电的时候有
msm_camera_config_single_vreg(..&ctrl->cam_vreg[power_setting->seq_val],..);
中间的参数就是设备树上的电压。

第二个节点SENSOR_I2C_MUX:
执行函数msm_camera_enable_i2c_mux—>可能是申请锁和一帧的内存空间

解析完毕后,最后两个节点是打开时序和I2C_MUX,如果成功上电便完成了。
对照规格书:
camera上电时序_第1张图片

把DOVDD上电后,AVDD,DVDD,PWDNB的上电时序都大于图中规定时间。

回到msm_sensor_driver_probe函数中。

厉害呀,现在不妨换换思路,瞧点文学东西

如果你喜欢,聊历史,思哲学,品诗集,赏国学。

那就关注公众号:二校五叔

这个是博主的文学公众号啦_

加油

你可能感兴趣的:(camera)