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

一、需求

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

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

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

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

但是如果有3家甚至更多的模组厂呢,就无法根据sensor_id来区分了,因为sensor_id的地址只能是2个!

怎么办呢?

还记得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( int *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!

猜你喜欢

转载自blog.csdn.net/justXiaoSha/article/details/101048834