Camera驱动流程总结范军君junjun.fan@sim.com目录1,Camera架构及流程简析2,初始化过程cameraid检测3,Camera上电流程4,Camera打开流程Camera架构及流程简析MTK平台camera架构:Kernel部分主要有两块:一块是imagesensor驱动,负责具体型号的sensor的id检测,上电,以及在preview,capture,初始化,3A等等功能设定时的寄存器配置。另一块是ispdriver,通过DMA将sensor数据流上传。本篇主要介绍imagesensor驱动的流程。HAL层这边主要分3块,一块是imageio,主要是数据buffer上传的pipe。一块是drv,包含imgsensor和isp的hal层控制。最后是featureio,包含各种3A等性能配置。本篇对HAL涉入不深,只在分析开机过程的id检测时会分析hal层的控制,属于第二块。流程简析:主要发生在两个时间点:开机过程中camera的动作以及打开应用时camera的动作。开机时,camera完成了sensor框架的初始化,id检测,以及上下电操作。打开应用时,camera会有上电,完成寄存器的初始配置,向上层传送基本参数及配置信息,以及preview和capture模式循环。初始化过程cameraid检测代码分析:=alps/mediatek/custom/common/kernel/imgsensor/kd_sensorlist.cmodule_init说明这段code在kernel初始化,也就是手机开机时运行。在模块初始化函数中,注册一个i2cdevice,同时注册了一个platformdriver注意drivername,匹配platformdevice需要名字一致。Platform总线为虚拟总线,注册platformdriver主要目的是隔离上下层,增强代码的可移植性。alps/mediatek/platform/mt6582/kernel/core/mt_dev.cmtk平台用到的platformdevicej基本都在这个文件中注册,这样device和driver就能匹配上了。在platformprobe中注册i2cdriver和之前的i2cdevice匹配配置i2cclock,注册一个字符设备。一般驱动中注册字符设备,多是利用字符设备的fops与上层交互,特别是ioctl.这些就是具体的fopsopen只是用来计数目前打开camera的数量,所以主要的交互功能要靠ioctl来完成这个函数很重要,沟通上下层,提供接口。这边的Command用幻字定义,可以保持cmd的唯一性,并具备可读性。Command可带不定长参数。下面是switchcase,通过ioctl传下来的参数选择调用分支支持的command汇总,包括camera开机过程中上电,id检测以及imagesensor的参数获取KDIMGSENSORIOC_X_SET_DRIVER和KDIMGSENSORIOC_T_CHECK_IS_ALIVE这两个func在开机初始化检测id会被hal层调用。=mediatek/platform/mt6582/hardware/camera/core/drv/imgsensor/imgsensor_drv.cppimpSearchSensor在开机过程中被调用,用于检测id,匹配main/subimagesensorGetSensooInitFuncList这个函数主要为了获得下表包含了id,name和一些方法的调用,因为在driver层有相似的架构,放在后面分析具体过程。=impSearchSensorHal层开始探测sensor,应为有两颗sensor,外层循环两次内层循环根据上面的sensor列表来,最大支持兼容16颗sensor,hal层特效,如果没有整合特定imagesensor的hal层代码,这边会直接退出。getCameraDefault调用很重要,hal层camera的所有特效参数,包括3A,shading都在这边设定。getCameraDefault通过之前的Sensorlist来连接特定imagesensor的具体实现。下表是2675的特效参数获取函数。获取各项特项参数=impSearchSensorIoctl向驱动层下command,注意下下传的id就是目前正在检测的imgsensor的id.终于又回到了kd_sensorlist.c这个文件下层的ioctl接口现在的情况是,针对mainsensor,hal层遍历上层的sensor列表,然后call下层的kdSetDriver.kdGetSensorList这个函数似曾相识。这个函数很有意思,用了指向指针的指针作为参数。有兴趣可以思考下不直接使用普通指针的原因,涉及堆栈和编译器对函数传参的处理。指向结构体的指针,最后一个成员很重要,是一个函数指针,其参数是函数指针的结构体指针。上层传下来的id参数解析成两部分g_invokeSocketIdx区分目前正在匹配的是main还是sub,drvIdx是sensor列表的序列号。Hal层和driver层的sensorlist应该在顺序上保持一致。函数指针的结构体指针,这些函数对应了具体imagesensor的操作,看如何挂接上具体sensor。还是由sensorlist为函数指针赋值,挂接具体SensorInit2675的SensorInit实现,主要是为pfFunc这个函数指针的结构体指针赋值,这样我们就实现了驱动代码的分离,hal层只需调用Sensorlist.c这样一个虚拟设备的驱动,就可以和具体的SensorDriver通信。这样的做法与platformdriver的设计思想一致,在lcm,传感器等驱动中也多有体现。到这一步,hal层在做Searchsensor时的第一步命令的setdriver的目的已经很清晰,就是为了挂接正在遍历的sensor的具体接口。回到Hal层,还是Searchsensor的这个函数对于正在遍历的这颗sensor,已经挂街上具体的底层驱动接口了,那么下达checkid的真正指令。底层的ioctl接口第一步是上电Poweron,这块稍后会有具体分析。这块code,就是读id了,调用之前挂接的具体sensor的Featurecontrol,并带参。2675的具体featurecontrol,诸多功能通过switch罗列在这边。读id一般在开机和打开sensor时做,读之前软件resetsensor,保证所读取的id的正确性。Ov一般都用300A和300B这两个register保存id。读到id后和预先设定的ID作比较如果一致,那么我们就找到了匹配到了要找的sensor,因为有前后摄,所以这样的动作要做两。这边还有个问题被忽略了,读id是通过i2c通信,各家sensor的i2caddr显然都不一样。imagesensor因为采取了驱动代码的分层设计,i2cdevice的注册时放在sensorlist中的,在moduleinit的时候已经注册,也指定了i2caddr为0xfe。那怎么和不同的sensor通信的呢实际的i2c通信前,对i2caddr重新赋值,kd_Sensorlist.c中的0xfe只是为了应付注册。读完id并没有结束,因为现在还在手机开机初始化阶段,并不是打开camera应用,我们check完id,一定要给sensor下电匹配上id后又回到了hal层,id不对的ioctl的returnerr0.HAL代码匹配上的sensor则继续下传cmd,获取一些sensor的信息和配置,流程与之前的id检测类似,不展开。这边预留了一个extraid的接口,主要用于区分同型号不同模组厂的sensor,通过读取指定寄存器中厂商烧录的value来做二次id判断,以兼容不同厂商的sensor,此处为空.当然extraid也完全可以只在driver层添加,加在初次id检测后即可。close仅为历史遗留代码,无实际作用。开机过程中的ID检测流程已全部给出,总结一下:HAL层运行Searchsensor这个线程HAL层遍历sensorlist并挂接HAL层性能如3A等一些参数获取的接口HAL层下达setDriver的cmd,并下传正在遍历的sensor在sensorlist列表中的idDriver层根据这个id,挂接Driver层sensorlist中对应的Sensor和具体Sensor底层操作接口HAL层对正在遍历的sensor下达checkid的指令Driver层为对应sensor上电,通过i2c读取预存在寄存器中的sensorid比较读取结果,不匹配,returnerror,继续遍历。匹配,HAL层下达其他指令收集sensor信息下电Camera上电流程Camera的供电和相关引脚电源部分:VCAMD就是DVDD数字供电,主要给ISP供电,如果是RAWDATA格式的sensor其ISP是在BB端,所以将其引脚将其NC;VCAM_IO就是VDDIO数字IO电源主要给I2C部分供电;VCAMA就是AVDD模拟供电,主要给感光区和ADC部分供电;VCAM_AF是对Camera自动对焦马达的供电引脚部分:Reset引脚和powerdown引脚代码部分:Camera在初始化和打开的时候均有上电动作,这边以初始化部分的代码分析这块流程。之前没有详细分析的上电代码。kdCISModulePowerOn这个函数的实现在kd_camera_hw.c里面=kd_camera_hw.c这个文件是MTK专门用于sensor上电时序管理的文件,各个sensor的上电均在这边实现。首先是引脚定义,包括模式和引脚的极性这边pinSetIdx可以反过来,这样就变成默认打开前摄了,现在是rearcamera优先上电的具体时序应该参考各个sensor的datasheet,下面是ov2675的上电时序图看图,首先使能pdn引脚然后开电,dovdd-avdd-dvddPoweroff,使能reset这边的开电顺序是有些问题的,按spec应该先D2再A再D,A2是供给AF的,属于马达部分,先后无所谓。虽然这样子顺序上电实际也没问题,但可能存在风险。还有一点,就是记得按datasheet的要求加上delay开电以后就可以拉pin脚上电了,注意一下这个sensor的pin脚极性和之前表中定义的相反在dualcamera的方案中,因为共有一组电源,所以在打开某一个sensor的时候,通常要disable另一个的pin脚以防止漏电。下电流程可以参考spec自行分析Camera打开流程:看下打开Camera后,HAL层的代码流程=sensor_hal.cppCreateInstance,看下pSensorDrvd的类。=sensor_drv.cpp这边拿到了我们之前分析过的imgsensor_drv.cpp中的实例,因此和ImgSensorDrv的方法挂接上了。继续看之前的初始化代码这个switch是检查之前的sensorsearch有没有成功,然后调用impsensor_drv.cpp中的init=impsensor_drv.cpp还是通过ioctl下传cmd调底层接口。不赘述了。最终调用2675中的接口,注意PV和CP模式下的分辨率,通常不一样,PV的帧数因此更高,而capture则更清晰。=imgsensor_drv.cpp获取更多的信息,最多通过2675的接口读寄存器实现,不一一列举了。ImgSensorDrv的初始化也结束了,还没有见到open的代码=sensor_hal.cpp先看getSensorInfo的实现。看一下getinfo里面主要的调用其中getInfo比较重要,直接贴底层对应的接口。和之前直接从寄存器收集到得信息不同,这边的值是驱动可以修改的,比如进入preview和capture需要delay的帧数,clock的极性等等。getCurren