• 高通平台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,如果成功上电便完成了。

    如果说我的文章对你有用,只不过是我站在巨人的肩膀上再继续努力罢了。
    若在页首无特别声明,本篇文章由 Schips 经过整理后发布。
    博客地址:https://www.cnblogs.com/schips/
  • 相关阅读:
    iframe的边框如何去掉
    vue-computed计算属性用法
    Vue-input框checkbox强制刷新
    TED 积极心理学感悟(二)
    路由器使用子网掩码进行分组转发的过程
    DHCP 服务器和 DHCP 客户端的交互过程
    IPv4 协议中的 NAT 协议和 CIDR 协议
    IPv6 是解决 IPv4 地址耗尽问题的根本途径
    TED 积极心理学感悟
    初级错误之 for 中的局部变量
  • 原文地址:https://www.cnblogs.com/schips/p/how_camera_power_up_in_qualcomm_drivers.html
Copyright © 2020-2023  润新知