一、需求
工作中,经常遇到这样的需求:
- 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.结语
之前问过高通的工程师,他们居然不知道怎么兼容!!!
其实也不复杂,只能说做的工作不一样吧!
兼容方案虽然简单,贵在思考!
继续当一名咸鱼(* ̄︶ ̄)!
网友评论