美文网首页
【Camera专题】高通-Camera多模组兼容方案

【Camera专题】高通-Camera多模组兼容方案

作者: c枫_撸码的日子 | 来源:发表于2019-09-18 19:47 被阅读0次

    一、需求

    工作中,经常遇到这样的需求:

    • sensor相同
    • 模组厂不同(意味着走线、fpc、镜头、组装等不相同)

    这时候,效果的同事需要做针对不同模组厂同一颗sensor进行效果调试。效果参数是2份,即做兼容!

    如果你只兼容2个模组厂生产的同一颗sensor,那么直接让2家模组厂区分sensor_id就可以了!
    然后驱动代码搞2份,sensor_id和效果文件区别一下就行了!

    但是如果有3家甚至更多的模组厂呢,就无法根据sensor_id来区分了,因为sensor_id的地址只能是2个!
    (ps:为啥sensor_id只有2个地址:
    你看一下sensor规格书就知道了。sensor_id的地址是由硬件上某根线接高或者接地决定的。
    以s5k4h7为例子,如果接地了,sensor_id为0x20,接电源,sensor_id为0x5A。)

    怎么办呢?

    还记得OTP的知识吗?

    OTP烧录的数据类型
    一般包括:

    • AF:自动对焦校准数据
    • AWB:白平衡校准数据
    • LSC:镜头阴影校准 (Lens Shading Calibration)
    • Moudle Info:模组信息,包含模组的生产年日月,模组ID等
      模组ID等信息是模组厂烧录进去的,我们可以让模组厂烧录不同的模组ID进行区分。

    兼容方案小结

    • 确定只兼容2家模组厂
      可以根据sensor_id进行区分
    • 模组厂有3家及其以上
      根据module_id进行区分,这种方案最实用!

    二、兼容方案的实现

    我们让模组厂把sensor_id弄成相同,这样方便otp数据读取!
    module_id不同,方便进行驱动的兼容!

    1.原理分析

    kernel/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c

    int msm_sensor_match_id(struct msm_sensor_ctrl_t *s_ctrl)
    {
        int rc = 0;
        uint16_t chipid = 0;
        struct msm_camera_i2c_client *sensor_i2c_client;
        struct msm_camera_slave_info *slave_info;
        const char *sensor_name;
    
        if (!s_ctrl) {
            pr_err("%s:%d failed: %pK\n",
                __func__, __LINE__, s_ctrl);
            return -EINVAL;
        }    
        sensor_i2c_client = s_ctrl->sensor_i2c_client;
        slave_info = s_ctrl->sensordata->slave_info;
        sensor_name = s_ctrl->sensordata->sensor_name;
    
        if (!sensor_i2c_client || !slave_info || !sensor_name) {
            pr_err("%s:%d failed: %pK %pK %pK\n",
                __func__, __LINE__, sensor_i2c_client, slave_info,
                sensor_name);
            return -EINVAL;
        }    
    
        rc = sensor_i2c_client->i2c_func_tbl->i2c_read(
            sensor_i2c_client, slave_info->sensor_id_reg_addr,
            &chipid, MSM_CAMERA_I2C_WORD_DATA);
        if (rc < 0) { 
            pr_err("%s: %s: read id failed\n", __func__, sensor_name);
            return rc;
        }
    
        pr_err("%s: read id: 0x%x expected id 0x%x:\n",
                __func__, chipid, slave_info->sensor_id);
        if (msm_sensor_id_by_mask(s_ctrl, chipid) != slave_info->sensor_id) {
            pr_err("%s chip id %x does not match %x\n",
                    __func__, chipid, slave_info->sensor_id);
            return -ENODEV;
        }
        return rc;
    }
    

    我们来看msm_sensor_match_id这个函数,逻辑很简单。

    • 1.获取的驱动代码里面的slave_info->sensor_id
    • 2.通过i2c_read把sensor_id读取到变量chipid 中
    • 3.比较这两个sensor_id,相同则匹配驱动成功,否则失败!

    思考:
    既然这个函数,通过比较sensor_id是否相同,来匹配驱动!
    那么我们一样可以在这里比较module_id是否相同,来匹配驱动!

    需要注意的地方:
    sensor_id的读取,可以直接通过一个i2c_read函数就轻松读到!
    module_id的读取,稍微复杂些,要遵守OTP数据的读写规范!

    2.具体实现

    sensor:s5k4h7
    模组厂:丘钛、信利
    模组id:丘钛烧录值为0x1、信利烧录值为0x2

    2.1 OTP读写规则


    2.2 module_id的地址

    2.3 函数的封装与实现
    static int read_module_id( uint_16 *module_id ,const char *sensor_name ,struct msm_camera_i2c_client *sensor_i2c_client;)
    { 
        int rc = 0;
        //如果sensor是s5k4h7,模组厂是丘钛或者信利
        if(!strcmp(sensor_name, "s5k4h7_qiutai") || !strcmp(sensor_name, "s5k4h7_truly")){
           /*set the page21 of otp*/
            sensor_i2c_client->i2c_func_tbl->i2c_write(sensor_i2c_client, 0x0A02,
            0x15, MSM_CAMERA_I2C_BYTE_DATA);
            if (rc < 0) {
                pr_err("%s: %s: write 0x0A02  failed\n", __func__, sensor_name);
                return rc;
            }
            /*OTP enable and read start*/
            sensor_i2c_client->i2c_func_tbl->i2c_write(sensor_i2c_client, 0x0A00,
            0x0100, MSM_CAMERA_I2C_WORD_DATA);
            if (rc < 0) {
                pr_err("%s: %s: write 0x0A00failed\n", __func__, sensor_name);
                return rc;
            }
            /*读取0x0A05寄存器的值并保持到变量module_id 中*/
            sensor_i2c_client->i2c_func_tbl->i2c_read(sensor_i2c_client, 0x0A05,
                    module_id , MSM_CAMERA_I2C_BYTE_DATA);
            if (rc < 0) {
                pr_err("%s: %s: read 0x0A05failed\n", __func__, sensor_name);
                return rc;
            }
        }
    
        //如果你想兼容其他sensor,可以继续添加
    
        return rc;
    }
    

    2.4 调用read_module_id

    kernel/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c

    int msm_sensor_match_id(struct msm_sensor_ctrl_t *s_ctrl)
    {
    ···
    ++  int module_id = -1;
    ···
        sensor_i2c_client = s_ctrl->sensor_i2c_client;
        slave_info = s_ctrl->sensordata->slave_info;
        sensor_name = s_ctrl->sensordata->sensor_name;
    
        if (!sensor_i2c_client || !slave_info || !sensor_name) {
            pr_err("%s:%d failed: %pK %pK %pK\n",
                __func__, __LINE__, sensor_i2c_client, slave_info,
                sensor_name);
            return -EINVAL;
        }    
    
        rc = sensor_i2c_client->i2c_func_tbl->i2c_read(
            sensor_i2c_client, slave_info->sensor_id_reg_addr,
            &chipid, MSM_CAMERA_I2C_WORD_DATA);
        if (rc < 0) { 
            pr_err("%s: %s: read id failed\n", __func__, sensor_name);
            return rc;
        }
    
        pr_err("%s: read id: 0x%x expected id 0x%x:\n",
                __func__, chipid, slave_info->sensor_id);
        if (msm_sensor_id_by_mask(s_ctrl, chipid) != slave_info->sensor_id) {
            pr_err("%s chip id %x does not match %x\n",
                    __func__, chipid, slave_info->sensor_id);
            return -ENODEV;
        }
    
    //read id成之后,继续比较module_id
    ++++++++++++++++++++
        rc = read_module_id(&module_id ,sensor_name ,sensor_i2c_client );
        if (rc < 0) { 
            pr_err("%s: %s: read module_id failed\n", __func__, sensor_name);
            return rc;
        }
    
        //如果加载到的是 丘钛 的驱动代码
        if(!strcmp(sensor_name, "s5k4h7_qiutai")) {
            if(module_id == 0x1) {//判断module_id 是否为0x1
                //丘钛 sensor 匹配上了丘钛 的驱动代码
                pr_err("%s: %s: module_id = 0x1,it is qiutai sensor\n", __func__, sensor_name);
                return rc;//直接返回成功
            }else {
                 pr_err("%s: %s: module_id != 0x1,it not't qiutai sensor\n", __func__, sensor_name);
                return -ENODEV;//模组id不匹配,直接返回失败
          }
        }
        //如果加载到的是 信利 的驱动代码
        if(!strcmp(sensor_name, "s5k4h7_truly")) {
            if(module_id == 0x2) {//判断module_id 是否为0x2
                //信利sensor 匹配上了信利的驱动代码
                pr_err("%s: %s: module_id = 0x1,it is truly sensor\n", __func__, sensor_name);
                return rc;//直接返回成功
            }else {
                 pr_err("%s: %s: module_id != 0x1,it not't truly sensor\n", __func__, sensor_name);
                return -ENODEV;//模组id不匹配,直接返回失败
          }
        }
    ++++++++++++++++++++
        return rc;
    }
    

    到这里,我们的兼容就ok了,剩下的就是驱动代码和效果代码提供2份了!

    2.5 驱动代码的修改

    • 丘钛的驱动
      vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/sensors/sensor_libs/s5k4h7_qiutai

    s5k4h7_qiutai.c

    
    #define SENSOR_MODEL_NO_S5K4H7 "s5k4h7_qiutai"
    //区分效果文件
    #define S5K4H7_LOAD_CHROMATIX(n) \
      "libchromatix_"SENSOR_MODEL_NO_S5K4H7"_"#n".so"
    
    //这里只有sensor_name 不同,slave_addr 都是0x5A
    static struct msm_camera_sensor_slave_info sensor_slave_info = {
      .i2c_freq_mode = I2C_FAST_MODE,
      .sensor_name = "s5k4h7_qiutai",
      /* Camera slot where this camera is mounted */
      .camera_id = CAMERA_0,
      /* sensor slave address */
      .slave_addr = 0x5A, //0x20
    ···
    }
    
    • 信利的驱动
      vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/sensors/sensor_libs/s5k4h7_truly

    s5k4h7_truly.c

    
    #define SENSOR_MODEL_NO_S5K4H7 "s5k4h7_truly"
    //区分效果文件
    #define S5K4H7_LOAD_CHROMATIX(n) \
      "libchromatix_"SENSOR_MODEL_NO_S5K4H7"_"#n".so"
    
    //这里只有sensor_name 不同,slave_addr 都是0x5A
    static struct msm_camera_sensor_slave_info sensor_slave_info = {
      .i2c_freq_mode = I2C_FAST_MODE,
      .sensor_name = "s5k4h7_truly",
      /* Camera slot where this camera is mounted */
      .camera_id = CAMERA_0,
      /* sensor slave address */
      .slave_addr = 0x5A, //0x20
    ···
    }
    

    3.结语

    之前问过高通的工程师,他们居然不知道怎么兼容!!!
    其实也不复杂,只能说做的工作不一样吧!
    兼容方案虽然简单,贵在思考!

    继续当一名咸鱼(* ̄︶ ̄)!

    Stay Hungry,Stay Foolish!

    相关文章

      网友评论

          本文标题:【Camera专题】高通-Camera多模组兼容方案

          本文链接:https://www.haomeiwen.com/subject/dkihuctx.html