如何在軟件中實現多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 = <span>eebin_interface_control</span>(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;
}
<span>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)
}</span>

其中 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 文件中,通常的模式爲:

 

<CameraModuleConfig>
<CameraId>0</CameraId>
<SensorName>s5k3l8</SensorName>
<FlashName>pmic</FlashName>
<EepromName>s5k3l8</EepromName>
……
<ChromatixName>qtech_s5k3l8_f3l8yam_chromatix</ChromatixName>
……
</CameraModuleConfig>

由於不同的模組,會對應不同的 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;
…...
<span>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;
}
}</span>
……
}

從此,即可通過 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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章