如何在软件中实现多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,如下
通過此函數,可以看出,首先會對 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。
總結
以上是生活随笔為你收集整理的如何在软件中实现多camera模组的兼容的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Ubuntu搭建git服务器,外网可访问
- 下一篇: 发卡机项目开发总结