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 调用完成。
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 数据解析。
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 进行解析。
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 中注册的模组进行过滤操作。
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 过程。