如何在软件中实现多camera模组的兼容

1. 简介

2. 详细流程

 1)kernel

 2)vendor

3. 思路总结

1)本文思路

2)另一种简单方法

4. 后续改进


1. 简介

Multi module 指的是同样的sensor,但是出自于不同的模组供应商,即sensor 的 ID 相同,module 的 ID 不同。如果使用当前的方法,在 driver 端 probe 都会成功,但是由于不同的模组具有不同的 OTP,lens 等等特性,如果错误的 module 被注册使用, 将会对效果调试产生负面效果。

当前,手机公司或 OEM,可以对 module 供应商,提出 OTP 的烧录规范,这就保证了 OTP eeprom 数据格式, 多模组保持一致,便于读写eeprom,通过区分 module ID,进行兼容性匹配。


2. 详细流程

在当前代码中,module sensor 初始化的步骤大致为 eebin,sensor,eeprom,所以在通过 eeprom 去区分 module ID,为时已晚。所幸,在当前代码中,我们可以通过 eebin 提供的接口,提前获取到 eeprom 的数据。详细如下:

1) Kernel

在 kernel 中,主要包括 dts 和 driver 部分

DTS 中,以msm8937-camera-sensor-mtp.dtsi 为例,主要是注册相关的设备,如 eeprom,camera 等,并且会在 qcom,camera 设备节点,关联相关设备:

eeprom0: qcom,eeprom@0{
              cell-index=<0>;
              reg=<0x00>;
              qcom,eeprom-name="s5k3l8";
              compatible="qcom,eeprom";
              qcom,slave-addr=<0xA0>;
              qcom,cci-master=<0>;
              qcom,num-blocks=<1>;
......
         };
qcom,camera@0{
         cell-index=<0>;
         compatible="qcom,camera";
         reg=<0x00>;
         qcom,csiphy-sd-index=<0>;
         qcom,csid-sd-index=<0>;
         qcom,eeprom-src=<&eeprom0>;
......
 };
Driver,路径为drivers/media/platform/msm/camera_v2/sensor/。对 camera node,只会进行dummy 注册并会默认 probe 成功,具体 camera sensor 的 probe 过程,会在userspace 调用完成。
但对于 eeprom,可以通过配置 userspace_probe 变量,来决定是在 userspace 进行 probe,还是在 kernel 驱动初始化时进行 probe。
当前,为了兼容性匹配,需要设置 eeprom-name 在下层进行数据的读取,即在 eeprom 驱动初始化时,读取出完整的 eeprom 数据,待用。

static int read_eeprom_memory(struct msm_eeprom_ctrl_t *e_ctrl, struct msm_eeprom_memory_block_t *block)
{
......
uint8_t *memptr = block->mapdata;
......
      for(j = 0; j < block->num_map; j++){
......
            if(emap[j].mem.valid_size){
                    e_ctrl->i2c_client.addr_type = emap[j].mem.addr_t;
                    rc=e_ctrl->i2c_client.i2c_func_tbl->i2c_read_seq(&(e_ctrl->i2c_client), emap[j].mem.addr,memptr, emap[j].mem.valid_size);
                    if(rc < 0){
                         pr_err("%s: read failed\n", __func__);
                         return rc;
                    }
                    memptr += emap[j].mem.valid_size;
           }
......
       }
       return rc;
}
在 msm_eeprom.c 中,会通过调用 msm_eeprom_platform_probe 的 read_eeprom_memory 函数,读取 eeprom 的 OTP 数据,并存放于 mapdata 中,此数据会用来在 user space 做 eeprom 数据解析。
通过代码流程,userspace 在 sensor init 调用前,会通过 eebin read 来获取此信息。
在 user space,通过 eebin read 获取到 eeprom 的数据后,即可通过 OTP 规范解析出对应 module 的 module ID,待用。
2) vendor
Probe 函数在 sensor_init.c 文件中,主要调用流程为:先去 probe eebin,再去 probe sensor,最后解析其 OTP 的具体信息。
其 probe 函数位于 module_sensor.c 的 module_sensor_init,如下
mct_module_t *module_sensor_init(const char *name)
{
……
bin_ctl.cmd = EEPROM_BIN_GET_BIN_DATA;
rc = eebin_interface_control(module_ctrl->eebin_hdl, &bin_ctl);
if (rc < 0) {
SERR("failed");
}
/* module_sensor_probe_sensors */
ret = sensor_init_probe(module_ctrl);
if (ret == FALSE) {
SERR("failed");
goto ERROR1;
}
……
/* intiialize the eeprom */
ret = mct_list_traverse(module_ctrl->sensor_bundle, module_sensor_init_eeprom,
module_ctrl->eebin_hdl);
if (ret == FALSE) {
SERR("failed");
goto ERROR1;
}
……
}
通过此函数,可以看出,首先会对 eebin 操作,获取有用的信息,其次进行 sensor_init_probe,最后,将会对 eeprom 进行解析。
完成 multi module 的兼容,需要针对的地方为 eebin_interface_control。
在 eebin_interface_control 函数中,会通过 eebin_interface_control -->> eebin_get_bin_data -->> eebin_read -->> eebin_dev_read 函数读取 cmm-data。需要修改的就是 eebin_dev_read 函数。

static boolean eebin_dev_read(eebin_hdl_t *eebin_hdl,
const char* dev_name, char**buff, uint32_t *num)
{
……
dev_fd = open(dev_name, O_RDWR);
if (dev_fd < 0) {
SHIGH("Open eeprom dev failed: %s", dev_name);
return FALSE;
}
cfg.cfgtype = CFG_EEPROM_GET_MM_INFO;
cfg.cfg.get_cmm_data.cmm_support = 0;
cfg.cfg.get_cmm_data.cmm_compression = 0;
cfg.cfg.get_cmm_data.cmm_size = 0;
if (ioctl(dev_fd, VIDIOC_MSM_EEPROM_CFG, &cfg) < 0) {
SHIGH("VIDIOC_MSM_EEPROM_CFG failed!");
goto end;
}
if (!cfg.cfg.get_cmm_data.cmm_support || !cfg.cfg.get_cmm_data.cmm_size)
goto end;
buff_l = malloc(cfg.cfg.get_cmm_data.cmm_size);
if (!buff_l){
SERR("%s failed allocating memory\n",__func__);
goto end;
}
cfg.cfgtype = CFG_EEPROM_READ_CAL_DATA;
cfg.cfg.read_data.num_bytes = cfg.cfg.get_cmm_data.cmm_size;
cfg.cfg.read_data.dbuffer = buff_l;
if (ioctl(dev_fd, VIDIOC_MSM_EEPROM_CFG, &cfg) < 0) {
SERR("CFG_EEPROM_READ_CAL_DATA failed!");
goto end_free;
}
……
}
对于这个函数中,我们可以通过修改 cmm 结构体,来获取相关信息。关于 cmm 相关设置,可以参考:kernel/Documentation/devicetree/bindings/media/video/msm-eeprom.txt。
如下:

Optional properties -EEPROM Camera Multimodule
- qcom,cmm-data-support - Camera MultiModule data capability flag.
- qcom,cmm-data-compressed - Camera MultiModule data compression flag.
- qcom,cmm-data-offset - Camera MultiModule data start offset.
- qcom,cmm-data-size - Camera MultiModule data size.
在读取 buff_l 前,可以通过比较当前设备名是否为"/dev/v4l-subdevX“来确定是否是我们需要的能获取到 module ID 的 eebin 的值,当前 8937 平台,前后摄分别为"/dev/v4l-subdev6“,"/dev/v4l-subdev7“。具体设备名,可以在 kernel 启动 probe 并注册时获取到。因此在代码中,添加类似代码,如:

cfg.cfgtype = CFG_EEPROM_READ_CAL_DATA;
cfg.cfg.read_data.num_bytes = cfg.cfg.get_cmm_data.cmm_size;
cfg.cfg.read_data.dbuffer = buff_l;
if (ioctl(dev_fd, VIDIOC_MSM_EEPROM_CFG, &cfg) < 0) {
SERR("CFG_EEPROM_READ_CAL_DATA failed!");
goto end_free;
}
if (!strcmp(dev_name,"/dev/v4l-subdev6")) {
ID-0 = Special_FUNC(buff_l)
} else if (!strcmp(dev_name,"/dev/v4l-subdev7")) {
ID-1 = Special_FUNC(buff_l)
}
其中 ID-0, ID-1 为全局变量,会在 sensor_init_probe 时,通过此 ID,对 XML 中注册的模组进行过滤操作。
通过此上步骤,即可获取 ID-0, ID-1。
而对 sensor_init_probe -->> sensor_init_xml_probe 时,在此函数中:

static boolean sensor_init_xml_probe(module_sensor_ctrl_t *module_ctrl,
int32_t sd_fd)
{
……
/* Get number of camera module configurations */
num_cam_config = sensor_xml_util_get_num_nodes(rootPtr, "CameraModuleConfig");
SLOW("num_cam_config = %d", num_cam_config);
if (!num_cam_config || num_cam_config > MAX_CAMERA_CONFIG) {
SERR(" invalid num_cam_config = %d", num_cam_config);
ret = FALSE;
goto XML_PROBE_EXIT;
}

xmlConfig.docPtr = docPtr;
xmlConfig.configPtr = &camera_cfg;

for (i = 0; i < num_cam_config; i++) {
nodePtr = sensor_xml_util_get_node(rootPtr, "CameraModuleConfig", i);
RETURN_ON_NULL(nodePtr);

xmlConfig.nodePtr = nodePtr;
ret = sensor_xml_util_get_camera_probe_config(&xmlConfig);
if (ret == FALSE) {
ret = FALSE;
goto XML_PROBE_EXIT;
}

if (slot_probed[camera_cfg.camera_id]) {
SHIGH("slot %d already probed", camera_cfg.camera_id);
continue;
}

rc = sensor_probe(module_ctrl, sd_fd, camera_cfg.sensor_name,
NULL, &xmlConfig);
if (rc == FALSE) {
SERR("failed: to probe %s", camera_cfg.sensor_name);
} else {
slot_probed[camera_cfg.camera_id] = TRUE;
}
}
……
}
在此函数中,会获取 xml 文件定义的所有支持的 sensor 的个数,保存为 num_cam_config,在对 num_cam_config 的循环时,我们就可以通过刚才获得的 ID-0, ID-1 等对 xml 文件中定义的 sensor 进行 probe 的操作。Xml 文件中,通常的模式为:


0
s5k3l8
pmic
s5k3l8
……
qtech_s5k3l8_f3l8yam_chromatix
……
由于不同的模组,会对应不同的 chromatx tuning 文件,因此,在获取不同的 module ID,即 ID-0, ID-1 后,在对不同的 ChromatixName 进行筛选,剔除 sensor ID 一致,但 module ID 不一致的 module 文件。
大致代码如下:

for (i = 0; i < num_cam_config; i++) {
……
int main_camera_eeprom_id = ID-0;
…...
if (!strcmp(camera_cfg.sensor_name, "sensor-name")) { //confirm the module name
which we need to do filter
if (main_camera_eeprom_id == moduleID-1) { //compare the Module ID
//only when the module ID and chromatix name is matched, probe
this ensor
if (strcmp(camera_cfg.actuator_name, "chromatix-name1"))
continue;
} else if (main_camera_eeprom_id == moduleID-1) { 
if (strcmp(camera_cfg.actuator_name, "chromatix-name2"))
continue;
}
}
……
}
从此,即可通过 module ID 过滤掉即使 sensor ID 一致的 module,并且阻止这些模组进行 kernel 的 probe 过程。

3. 思路总结
1) 本文思路:
综上所述,完成 multi module 的适配。
需要做的准备是:
1. 按照统一的格式完成 OTP 的烧录
2. 在 DTS 文件中,写入正确的 map 信息
3. 在 xml 文件中,正确注册不同 module 的信息,包括所需的 chromatixName
4. 做好 Module ID 和 chromatixName 的对应关系
代码的流程是:
1. 在 msm_eeprom.c 的 driver 中,完成 eeprom 的 probe 以及 memory 的读入存储
2. 在 vendor probe sensor 的过程中
a) 通过 eebin_interface_control -->> eebin_get_bin_data -->> eebin_read -->> eebin_dev_read 获取 module ID, 并存储于 vendor 进程中,以备后用
b) 通过 sensor_init_probe -->> sensor_init_xml_probe,对 module 进行筛选,通过 module ID 和 chromatixName 的匹配关系,过滤掉不匹配的 module,即不进行 kernel 层 sensor ID 的匹配

2) 另一种简单方法:
1. 在 msm_eeprom.c 的 driver 中,完成 eeprom 的 probe 以及 memory 的读入存储
2. 对于读取的 eeprom 数据,在 kernel 层驱动中,即使 OTP 规范不同,也可以根据不同的 eeprom name 进行区分,进而简单的解析,保存对应的 module ID
3. 添加新的 dummy 设备,其功能只需要使得 user space 能够通过设备节点,获取 module ID
4. 在 sensor_init_probe 之前,通过此新的设备,获取 module ID
5. 通过 sensor_init_probe -->> sensor_init_xml_probe,对 module 进行筛选,通过 module ID 和 chromatixName 的匹配关系,过滤掉不匹配的 module,即不进行 kernel 层 sensor ID 的匹配
此方法,不用修改 eebin 函数,并且流程上更为清晰。

4.后续改进
1. Xml 文件中添加 module ID 参数,用以唯一定位 module 型号,以取代 chromatixName 模式
2. 增加接口和参数,在传递 sensor ID 的同时传递 module ID,进行 kernel 层的 sensor probe 工作。
3. 添加 eeprom 的 map 参数接口,或者 module ID。

你可能感兴趣的:(qualcomm,camera)