高通平台mm-camera上電時序


高通平台mm-camera上電時序

背景

作為高通平台Camera知識的一種補充。

參考文檔:https://blog.csdn.net/m0_37166404/article/details/64920910

介紹

高通平台對於camera的代碼組織,大體上還是遵循Android的框架,即:

  • 上層應用和HAL層交互,高通平台在HAL層里面實現自己的一套管理策略;
  • 在kernel中實現sensor的底層驅動;
  • 對於最核心的sensor端的底層設置、ISP效果相關等代碼則是單獨進行了抽離,放在vendor中。

上電時序

時序屬性

路徑:vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/sensors/includes/sensor_lib.h

  • 最終位於:kernel/include/media/msm_camsensor_sdk.h
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];
};

有關的時序設置

以:ov5648_q5v22e 為例。

對照規格書:

把DOVDD上電后,AVDD,DVDD,PWDNB的上電時序都大於圖中規定時間。

路徑:vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/sensors/sensor_libs/xxx

例如:vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/sensors/sensor_libs/ov5648_q5v22e當中的ov5648_q5v22e_lib.c

下面這個結構體便是上電時序

tatic struct msm_sensor_power_setting ov5648_q5v22e_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,
  },
  {
    .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_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),
    },
};

這些Camera的屬性具體在msm_camsensor_sdk定義。

  • 最終位於: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;
};

驅動流程解析

在此之前是ioctl,注冊到v4l2子系統中。從vendor中把時序結構體的內容傳遞到kernel中的過程如下:

注意:vendor中的addr_type、camera_id、slave_addr等信息也是按照這樣的方法從vendor中傳遞到kernel中的,可以加打印調試信息看這些值正確與否。

++ kernel/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.c
msm_sensor_init_subdev_ioctl()
    msm_sensor_driver_cmd() : case VIDIOC_MSM_SENSOR_INIT_CFG
++ kernel/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c
	    msm_sensor_driver_probe()
    		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); 
			// 把slave_info的上電時序傳遞給s_ctrl結構體
			msm_sensor_get_power_settings
                msm_sensor_get_power_up_settings(setting, slave_info, power_info);
    		
		    /* 校正時序中SENSOR_VREG的seq_val,為設備樹的CAM_VIO */
		    msm_camera_fill_vreg_params();
			/* 執行真正的上電程序*/
			s_ctrl->func_tbl->sensor_power_up(s_ctrl);
				(msm_sensor_power_up)
					由於之前有  .sensor_power_up = msm_sensor_power_up,
					所以最終為: msm_sensor_power_up
++ kernel/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
					 //把s_ctrl的上電信息傳遞給power_info
				    power_info = &s_ctrl->sensordata->power_info;
				    sensor_i2c_client = s_ctrl->sensor_i2c_client;
				    slave_info = s_ctrl->sensordata->slave_info;
				    sensor_name = s_ctrl->sensordata->sensor_name;

				    // 通過之前設置好的時序上電
				    rc = msm_camera_power_up(power_info, 
                                             s_ctrl->sensor_device_type,
                                             sensor_i2c_client);

msm_camera_power_up流程分析

解析這個上電函數:

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;
                //camera頻率使能
                rc = msm_cam_clk_enable(...1);                  
                //...
            case SENSOR_GPIO:
                //...
                //拉高拉低gpio口
                gpio_set_value_cansleep(ctrl->gpio_conf->gpio_num_info->gpio_num[power_setting->seq_val],
                                        (int) power_setting->config_val);          
                //...
            case SENSOR_VREG:
                //函數里面打開reg_ptr的電源控制器regulator_enable,稍后會解析
                if (power_setting->seq_val < ctrl->num_vreg)
                    msm_camera_config_single_vreg(...,1);  
                //...
            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;
        //...
    }
}

// 再進入此節點上電的時候有下列的語句,中間的參數就是設備樹上的電壓。
msm_camera_config_single_vreg(..&ctrl->cam_vreg[power_setting->seq_val],..);

SENSOR_I2C_MUX

執行函數msm_camera_enable_i2c_mux—>可能是申請鎖和一幀的內存空間

解析完畢后,最后兩個節點是打開時序和I2C_MUX,如果成功上電便完成了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM