景物通过镜头(LENS)生成的光学图像投射到图像传感器(Sensor)表面上,然后转为模拟的电信号,经过 A/D(模数转换)转换后变为数字图像信号,再送到数字信号处理芯片(DSP)中加工处理,再通过 IO 接口传输到 CPU 中处理,通过 LCD 就可以看到图像了。
2、Camera 常见的数据输出格式:
CAMERA驱动整个框架分为:三个部分hal部分逻辑调用,kernel层的通用驱动sensorlist.c 和具体IC的驱动xxxx_mipi_raw.c
这里主要介绍kernel部分和HAL层部分。
camera开机流程:poweron上电开机,然后通过i2c地址匹配i2c通讯,rest和powerdown上电(上电代码在kd_camera_hw.c中的kdCISModulePowerOn,主要有VCAM:主要给ISP供电,VCAM_IO:数字IO电源,主要给I2C供电,VCAMA:模拟供电,主要给感光区和ADC部分供电,VCAMAF:主要给对焦马达供电;具体根据datasheet添加,有时会影响cts) ,读取sensor的ID(具体ic驱动里面的open和get_imgsensor_id都有读取id的操作,sensor id只要大于0、小于0xffffffff都是合法的。),然后软复位,下载preview参数为预览做准备,下载capture为拍照做准备,然后执行下电操作。
总结:
HAL层运行Search sensor这个线程
HAL层遍历sensorlist列表并挂载HAL层性能3A等一些参数获取的接口
HAL层下达setDriver的cmd,并下传正在遍历的sensorlist列表中的ID
Driver层根据这个ID,挂载Driver层sensorlist中对应的Sensorlist中对应的Sensor和具体Sensor底层操作接口(例如Sub_GC2355_MIPI_RAW_SensorInit)
HAL层对正确遍历的sensor下达check ID的指令
Driver层为对应sensor上电,通过I2C读取预存在寄存器中的sensor id
比较读取ID结果(配置的和读到的ID),不匹配,return error,继续遍历
匹配,HAL层下达其他指令收集sensor信息
最后sensor下电
一、MT6580 平台 Camera 驱动整体框架
整个框架分为三个部分hal部分逻辑调用,kernel层的通用驱动sensorlist.c 和具体IC的驱动 xxxx_mipi_raw.c,kernel起来后不会直接去访问硬件sensor,而是会注册相关的驱动,之后Android系统起来后会启动相关的服务如:camera_service,在camera服务中会直接去访问hal,kernel驱动,进而操作camera。
二、 Camera 驱动的具体实现
从vendor/mediatek/proprietary/hardware/mtkcam/legacy/platform/mt6580/hal/sensor/imgsensor_drv.cpp中的impSearchSensor()函数说起。
———————–HAL 层部分—————————-
GetSensorInitFuncList(&m_pstSensorInitFunc);
LOG_MSG(“SENSOR search start \n”);
…
获取sensor列表后,紧接着通过:
- 针对前后摄注册platform 设备和驱动
主摄平台驱动的定义:
副摄平台驱动的定义:
主副摄cam在dts中(注册设备)定义设备信息:
当内核启动后,会解析dts编译生成的dtb文件,注册里面定义的device,如果和驱动中定义compatible字段一致,则挂载启动。上面注册了两个platform 驱动g_stCAMERA_HW_Driver,g_stCAMERA_HW_Driver2,
如果compatible匹配成功会调用各自的probe函数CAMERA_HW_probe,CAMERA_HW_probe2
- 平台probe 函数的实现
主摄probe,CAMERA_HW_probe的实现如下:
static int CAMERA_HW_probe(struct platform_device *pdev)
{
#if !defined(CONFIG_MTK_LEGACY)
mtkcam_gpio_init(pdev);
mtkcam_pin_mux_init(pdev);
#endif
return i2c_add_driver(&CAMERA_HW_i2c_driver);
}
副摄probe,CAMERA_HW_probe的实现如下:
static int CAMERA_HW_probe2(struct platform_device *pdev)
{
return i2c_add_driver(&CAMERA_HW_i2c_driver2);
}
从上可以看出在main/sub 的平台probe中分别注册了各自的i2c驱动CAMERA_HW_i2c_driver,
CAMERA_HW_i2c_driver2,main sensor 的CAMERA_HW_i2c_driver定义如下:
#ifdef CONFIG_OF
static const struct of_device_id CAMERA_HW_i2c_of_ids[] = {
{ .compatible = “mediatek,camera_main”, },
{}
};
#endif
struct i2c_driver CAMERA_HW_i2c_driver = {
.probe = CAMERA_HW_i2c_probe,
.remove = CAMERA_HW_i2c_remove,
.driver = {
.name = CAMERA_HW_DRVNAME1,
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = CAMERA_HW_i2c_of_ids,
#endif
},
.id_table = CAMERA_HW_i2c_id,
};
sub sensor 的CAMERA_HW_i2c_driver定义如下:
#ifdef CONFIG_OF
static const struct of_device_id CAMERA_HW2_i2c_driver_of_ids[] = {
{ .compatible = “mediatek,camera_sub”, },
{}
};
#endif
struct i2c_driver CAMERA_HW_i2c_driver2 = {
.probe = CAMERA_HW_i2c_probe2,
.remove = CAMERA_HW_i2c_remove2,
.driver = {
.name = CAMERA_HW_DRVNAME2,
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = CAMERA_HW2_i2c_driver_of_ids,
#endif
},
.id_table = CAMERA_HW_i2c_id2,
}; - I2c probe的实现
从上可以看出main/sub sensor在各自的平台probe中,注册了i2c_driver,当各自的i2c_driver和设备匹配
匹配规则:
(1)当在dts有创建设备节点注册设备的时候:当i2c_driver和i2c_device的.compatible字段一致,就会执行i2c_driver里面的probe入口函数
(2)当调用i2c_register_board_info注册i2c设备时,当i2c_driver驱动中ids_table中name字段匹配i2c设备中的name就可调用i2c_driver驱动中的probe函数。
成功后,会调用各自的i2c_probe函数。
main sensor 的i2c_probe函数
sub sensor 的i2c_probe函数
/* Attatch file operation. /
cdev_init(g_pCAMERA_HW_CharDrv2, &g_stCAMERA_HW_fops0);//初始化字符设备
/ Add to system */
cdev_add(g_pCAMERA_HW_CharDrv2, g_CAMERA_HWdevno2, 1));//注册到内核
//创建目录 /sys/class/sensordrv2/
sensor2_class = class_create(THIS_MODULE, “sensordrv2”);
//创建目录/sys/class/sensordrv2/kd_camera_hw_bus2
sensor_device2 = device_create(sensor2_class, NULL, g_CAMERA_HWdevno2, NULL, CAMERA_HW_DRVNAME2);
…
return 0;
}
main/sub 创建各自的字符设备过程中绑定各自的 fops(fops是上层操作底层驱动的函数接口),g_stCAMERA_HW_fops和g_stCAMERA_HW_fops0
};
};
从上可以看出各自的fops指定了相同的Ioctl函数,意味着上层操作main/sub sensor 只需要对应一个底层的ioctl即可,至于sensor的区分可以借助idx,后面会讲到
/*
- CAMERA_HW_Ioctl
/
}
这里ioctl和上层一一对应,上层要控制camera 只需要传入相应的cmd和data即可。
——————- HAL 调用Kernel 层驱动的逻辑 ————————-
前面介绍了HAL层调用ioctl 和 kernel层注册驱动,接下来继续分析,HAL层调用后驱动具体的实现流程。
- ioctl 底层的实现
4.1 先来看ioctl(m_fdSensor, KDIMGSENSORIOC_X_SET_DRIVER,&id[KDIMGSENSOR_INVOKE_DRIVER_0] );
当 KDIMGSENSORIOC_X_SET_DRIVER 被传下时,会调用 kernel 层的 kdSetDriver 接口
kdGetSensorInitFuncList(&pSensorList)) //获得sensor初始化列表
for (i = KDIMGSENSOR_INVOKE_DRIVER_0; i < KDIMGSENSOR_MAX_INVOKE_DRIVERS; i++) {
UINT32 GC5025MIPI_RAW_SensorInit(PSENSOR_FUNCTION_STRUCT pfFunc)
{
/ To Do : Check Sensor status here */
if (pfFunc!=NULL)
pfFunc=&sensor_func;
return ERROR_NONE;
} / GC5025MIPI_RAW_SensorInit */
从中可以看出,gc5025的Init函数地址传给了pfFunc,也就是后面在通用驱动中可以直接凭借pfun指针调用sensorlist中的驱动
4.2 再来看 ioctl(m_fdSensor, KDIMGSENSORIOC_T_CHECK_IS_ALIVE);
当 KDIMGSENSORIOC_T_CHECK_IS_ALIVE 被传下时,会调用kernel层的 adopt_CAMERA_HW_Feature
Control接口
…
在kdModulePowerOn中又调用_kdCISModulePowerOn
在_kdCISModulePowerOn又调用 kdCISModulePowerOn 函数
文件:kernel-3.18/drivers/misc/mediatek/imgsensor/src/mt6580/camera_hw/kd_camera_hw.c
u32 pinSetIdx = 0; /* default main sensor */
printk(“fangkuiccm %d ,currSensorName = %s pinSetIdx = %d “,LINE,currSensorName,pinSetIdx );
mdelay(50);
mdelay(10);
mdelay(10);
mdelay(10);
mdelay(50);
上电操作完成后,紧接着读取SensorID,通用驱动使用 SensorFeatureControl 来读取ID如:
g_pSensorFunc->SensorFeatureControl(g_invokeSocketIdx[i],SENSOR_FEATURE_CHECK_SENSOR_ID,(MUINT8 *) &sensorID,&retLen);
此时传入的cmd为SENSOR_FEATURE_CHECK_SENSOR_ID,就会调用 feature_control 中的get_imgsensor_id 再看 get_imgsensor_id 的实现
再看return_sensor_id的实现
return get_byte;
}
文件:kernel-3.18/drivers/misc/mediatek/imgsensor/src/mt6580/kd_sensorlist.c
/* Remove i2c ack error log during search sensor /
/ PK_ERR(“g_pstI2Cclient->ext_flag: %d”, g_IsSearchSensor); */
if (g_IsSearchSensor == 1) {
g_pstI2Cclient->ext_flag = (g_pstI2Cclient->ext_flag) | I2C_A_FILTER_MSG;
} else {
g_pstI2Cclient->ext_flag = (g_pstI2Cclient->ext_flag) & (~I2C_A_FILTER_MSG);
}
/* Remove i2c ack error log during search sensor /
/ PK_ERR(“g_pstI2Cclient2->ext_flag: %d”, g_IsSearchSensor); */
if (g_IsSearchSensor == 1) {
g_pstI2Cclient2->ext_flag = (g_pstI2Cclient2->ext_flag) | I2C_A_FILTER_MSG;
} else {
g_pstI2Cclient2->ext_flag = (g_pstI2Cclient2->ext_flag) & (~I2C_A_FILTER_MSG);
}
spin_unlock(&kdsensor_drv_lock);
i4RetValue = i2c_master_send(g_pstI2Cclient2, a_pSendData, a_sizeSendData);
if (i4RetValue != a_sizeSendData) {
PK_ERR(“[CAMERA SENSOR] I2C send failed!!, Addr = 0x%x\n”, a_pSendData[0]);
return -1;
}
这一步完成I2c的读取,也就是说如果I2c配置正确,并且上电正确,到这一步就可以正确的读取ID,整个camera也就基本就调通了。
三、总结
通过上述分析,我们可以看出,camera驱动先是注册platform平台驱动,再注册I2c驱动,然后又为前后摄注册字符设备,封装底层方法CAMERA_HW_Ioctl,,上层访问底层驱动时候先是使用setdriver将具体IC的驱动入口获取,然后使用checkisalive对sensorlist中的IC进行上电,上电完成就通过i2c读取设备ID,到此为止,上层应用与底层驱动挂接完成,紧接着就是预览和拍照,不过这都是具体IC驱动的实现了。
简要概括过程:
1.开机时,camera完成了sensor框架的初始化,id检测,以及上下电操作;
Hal层在开机初始化调用文件vendor/mediatek/proprietary/hardware/mtkcam/legacy/platform/mt6580/hal/sensor/imgsensor_drv.cpp中的
impSearchSensor(pfExIdChk pExIdChkCbf) 函数,这个函数执行4个功能:
1).用GetSensorInitFuncList(&m_pstSensorInitFunc)函数获取目前所有的camera sensor(需加log打印确认一下是获取全部sensor还是ProjectConfig.mk中配置的sensor)列表,这些 前后 camera 都在 projectconfig. mk已经设置。
2).用KDIMGSENSORIOC_X_SET_DRIVER向Kd_sensorlist.c(kernel-3.18\drivers\misc\mediatek\imgsensor\src\mt6580) 中的CAMERA_HW_Ioctl传值,通过case分支最终调用kdSetDriver函数,根据在projectconfig.mk文件里面的main sub camera的配置,找到相应的前后camera具体的驱动文件,即对于正在遍历的这颗sensor,挂接上具体的底层驱动接口Init函数。
3).用KDIMGSENSORIOC_T_CHECK_IS_ALIVE向Kd_sensorlist.c 中的CAMERA_HW_Ioctl(即前面讲到的上层操作底层的接口)传值,通过case分支最终调用adopt_CAMERA_HW_CheckIsAlive函数,在这个函数里开始给前面找到的所有camera上电,并通过向具体驱动里面的ioctrl函数传递SENSOR_FEATURE_CHECK_SENSOR_ID参数,最终通过case分支调用对应的函数通过I2C读取并核对是否为该sensor的 id,去识别具体的camera sensor id。
4).分别对前后已经配对了sensor id的camera执行m_pSubSensorInfo =m_pstSensorInitFunc.pSensorInfo;通过这个函数会调用具体驱动(这里以GC2145M 的前摄像头为例) UINT32GC2145mipiGetInfo()函数,这个函数是获取sensor IC preview capture video时的帧率 丢帧 打开时候的默认窗口,数据传输的type等基本信息。通过上面的4步,kernel内核已经识别了主板上的硬件camera IC了,并获取具体sensorIC的基本信息。
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/205369.html原文链接:https://javaforall.net
