2024年5月13日发(作者:森源源)
Camera驱动流程总结
范军君
@
目录
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.c
module_init说明这段code在kernel初始化,也就是手机开机时运行。
在模块初始化函数中,注册一个i2cdevice,同时注册了一个platformdriver
本页已使用福昕阅读器进行编辑。
福昕软件(C)2005-2009,版权所有,
仅供试用。
注意drivername,匹配platformdevice需要名字一致。Platform总线为虚拟总线,注册
platformdriver主要目的是隔离上下层,增强代码的可移植性。
alps/mediatek/platform/mt6582/kernel/core/mt_dev.c
mtk平台用到的platformdevicej基本都在这个文件中注册,这样device和driver就能匹配上
了。
在platformprobe中注册i2cdriver和之前的i2cdevice匹配
配置i2cclock,注册一个字符设备。一般驱动中注册字符设备,多是利用字符设备的fops
与上层交互,特别是ioctl.
这些就是具体的fops
open只是用来计数目前打开camera的数量,所以主要的交互功能要靠ioctl来完成
这个函数很重要,沟通上下层,提供接口。这边的Command用幻字定义,可以保持cmd
的唯一性,并具备可读性。Command可带不定长参数。
下面是switchcase,通过ioctl传下来的参数选择调用分支
本页已使用福昕阅读器进行编辑。
福昕软件(C)2005-2009,版权所有,
仅供试用。
支持的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_
impSearchSensor在开机过程中被调用,用于检测id,匹配main/subimagesensor
GetSensooInitFuncList这个函数主要为了获得下表
包含了id,name和一些方法的调用,因为在driver层有相似的架构,放在后面分析具体过程。
=>impSearchSensor
Hal层开始探测sensor,应为有两颗sensor,外层循环两次
内层循环根据上面的sensor列表来,最大支持兼容16颗sensor,hal层特效,如果没有整合
特定imagesensor的hal层代码,这边会直接退出。
getCameraDefault调用很重要,hal层camera的所有特效参数,包括3A,shading都在这边设
定。getCameraDefault通过之前的Sensorlist来连接特定imagesensor的具体实现。下表是2675
的特效参数获取函数。
获取各项特项参数
=>impSearchSensor
Ioctl向驱动层下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为函数指针赋值,挂接具体SensorInit
2675的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的returnerr<0.
HAL代码
匹配上的sensor则继续下传cmd,获取一些sensor的信息和配置,流程与之前的id检测类
似,不展开。
这边预留了一个extraid的接口,主要用于区分同型号不同模组厂的sensor,通过读取指定
寄存器中厂商烧录的value来做二次id判断,以兼容不同厂商的sensor,此处为空.当然extra
id也完全可以只在driver层添加,加在初次id检测后即可。
close仅为历史遗留代码,无实际作用。
开机过程中的ID检测流程已全部给出,总结一下:
HAL层运行Searchsensor这个线程
HAL层遍历sensorlist并挂接HAL层性能如3A等一些参数获取的接口
HAL层下达setDriver的cmd,并下传正在遍历的sensor在sensorlist列表中的id
Driver层根据这个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->dvdd
Poweroff,使能reset
这边的开电顺序是有些问题的,按spec应该先D2再A再D,A2是供给AF的,属于马
达部分,先后无所谓。虽然这样子顺序上电实际也没问题,但可能存在风险。
还有一点,就是记得按datasheet的要求加上delay
开电以后就可以拉pin脚上电了,注意一下这个sensor的pin脚极性和之前表中定义的相反
在dualcamera的方案中,因为共有一组电源,所以在打开某一个sensor的时候,通常要disable
另一个的pin脚以防止漏电。
下电流程可以参考spec自行分析
Camera打开流程:
看下打开Camera后,HAL层的代码流程
=>sensor_
CreateInstance,看下pSensorDrvd的类。
=>sensor_
这边拿到了我们之前分析过的imgsensor_中的实例,因此和ImgSensorDrv的方法挂
接上了。
继续看之前的初始化代码
这个switch是检查之前的sensorsearch有没有成功,然后调用impsensor_中的init
=>impsensor_
还是通过ioctl下传cmd调底层接口。不赘述了。
最终调用2675中的接口,注意PV和CP模式下的分辨率,通常不一样,PV的帧数因此更
高,而capture则更清晰。
=>imgsensor_
获取更多的信息,最多通过2675的接口读寄存器实现,不一一列举了。ImgSensorDrv的初
始化也结束了,还没有见到open的代码
=>sensor_
先看getSensorInfo的实现。
看一下getinfo里面主要的调用
其中getInfo比较重要,直接贴底层对应的接口。
和之前直接从寄存器收集到得信息不同,这边的值是驱动可以修改的,比如进入preview和
capture需要delay的帧数,clock的极性等等。
getCurrentSensorType就是看一下sensor输出数据的类型。
再往下setTgphasesetSensorIOcurrentinitCSI2peripheralsetCSI2config,这些就是和数据
流传送对应的硬件驱动配置的hal接口了,TG是时间触发器,CSI2config指CameramipiBB
端的总线配置。
可以参考这张数据流的图示
终于到打开了,还是调用imgsensor_的接口
=>imgsensor_
熟悉的ioctl,直接贴2675的接口
再读次id,之前先软件reset。然后就是几千个寄存器的初始化,打开camera,会延迟一段
时间就是这边的缘故。
打开sensor后,将进入preview,这边的上层调用没有具体分析,通过抓底层接口的log,看
下有哪些操作。
先看下open前的,对照下刚才分析的打开流程,查看下调用时序。
featurecontrol的id如下
基本一致,再看看preview前的
上层调用,有很多底层并未去实现,preview前主要在调用yuvsetting这块
主要是一些特效设定,便于在preview时通过apk里的设置项来调整
上层可以调ioctl切换preview和capture模式,preview的入口
Imgsensor端preview的接口主要是寄存器的配置,包括clock,size,AE调整等,capture的与
之类似。
贴下preview的流程图:
包括:
3A的实时调整
Pass1,pass2是MTK架构buffer的传递过程,buffer1主要是指硬件buffer的传递,buffer2
是软件buffer的传递。
各层次preview回调
退出切换模式时退出loop
2024年5月13日发(作者:森源源)
Camera驱动流程总结
范军君
@
目录
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.c
module_init说明这段code在kernel初始化,也就是手机开机时运行。
在模块初始化函数中,注册一个i2cdevice,同时注册了一个platformdriver
本页已使用福昕阅读器进行编辑。
福昕软件(C)2005-2009,版权所有,
仅供试用。
注意drivername,匹配platformdevice需要名字一致。Platform总线为虚拟总线,注册
platformdriver主要目的是隔离上下层,增强代码的可移植性。
alps/mediatek/platform/mt6582/kernel/core/mt_dev.c
mtk平台用到的platformdevicej基本都在这个文件中注册,这样device和driver就能匹配上
了。
在platformprobe中注册i2cdriver和之前的i2cdevice匹配
配置i2cclock,注册一个字符设备。一般驱动中注册字符设备,多是利用字符设备的fops
与上层交互,特别是ioctl.
这些就是具体的fops
open只是用来计数目前打开camera的数量,所以主要的交互功能要靠ioctl来完成
这个函数很重要,沟通上下层,提供接口。这边的Command用幻字定义,可以保持cmd
的唯一性,并具备可读性。Command可带不定长参数。
下面是switchcase,通过ioctl传下来的参数选择调用分支
本页已使用福昕阅读器进行编辑。
福昕软件(C)2005-2009,版权所有,
仅供试用。
支持的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_
impSearchSensor在开机过程中被调用,用于检测id,匹配main/subimagesensor
GetSensooInitFuncList这个函数主要为了获得下表
包含了id,name和一些方法的调用,因为在driver层有相似的架构,放在后面分析具体过程。
=>impSearchSensor
Hal层开始探测sensor,应为有两颗sensor,外层循环两次
内层循环根据上面的sensor列表来,最大支持兼容16颗sensor,hal层特效,如果没有整合
特定imagesensor的hal层代码,这边会直接退出。
getCameraDefault调用很重要,hal层camera的所有特效参数,包括3A,shading都在这边设
定。getCameraDefault通过之前的Sensorlist来连接特定imagesensor的具体实现。下表是2675
的特效参数获取函数。
获取各项特项参数
=>impSearchSensor
Ioctl向驱动层下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为函数指针赋值,挂接具体SensorInit
2675的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的returnerr<0.
HAL代码
匹配上的sensor则继续下传cmd,获取一些sensor的信息和配置,流程与之前的id检测类
似,不展开。
这边预留了一个extraid的接口,主要用于区分同型号不同模组厂的sensor,通过读取指定
寄存器中厂商烧录的value来做二次id判断,以兼容不同厂商的sensor,此处为空.当然extra
id也完全可以只在driver层添加,加在初次id检测后即可。
close仅为历史遗留代码,无实际作用。
开机过程中的ID检测流程已全部给出,总结一下:
HAL层运行Searchsensor这个线程
HAL层遍历sensorlist并挂接HAL层性能如3A等一些参数获取的接口
HAL层下达setDriver的cmd,并下传正在遍历的sensor在sensorlist列表中的id
Driver层根据这个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->dvdd
Poweroff,使能reset
这边的开电顺序是有些问题的,按spec应该先D2再A再D,A2是供给AF的,属于马
达部分,先后无所谓。虽然这样子顺序上电实际也没问题,但可能存在风险。
还有一点,就是记得按datasheet的要求加上delay
开电以后就可以拉pin脚上电了,注意一下这个sensor的pin脚极性和之前表中定义的相反
在dualcamera的方案中,因为共有一组电源,所以在打开某一个sensor的时候,通常要disable
另一个的pin脚以防止漏电。
下电流程可以参考spec自行分析
Camera打开流程:
看下打开Camera后,HAL层的代码流程
=>sensor_
CreateInstance,看下pSensorDrvd的类。
=>sensor_
这边拿到了我们之前分析过的imgsensor_中的实例,因此和ImgSensorDrv的方法挂
接上了。
继续看之前的初始化代码
这个switch是检查之前的sensorsearch有没有成功,然后调用impsensor_中的init
=>impsensor_
还是通过ioctl下传cmd调底层接口。不赘述了。
最终调用2675中的接口,注意PV和CP模式下的分辨率,通常不一样,PV的帧数因此更
高,而capture则更清晰。
=>imgsensor_
获取更多的信息,最多通过2675的接口读寄存器实现,不一一列举了。ImgSensorDrv的初
始化也结束了,还没有见到open的代码
=>sensor_
先看getSensorInfo的实现。
看一下getinfo里面主要的调用
其中getInfo比较重要,直接贴底层对应的接口。
和之前直接从寄存器收集到得信息不同,这边的值是驱动可以修改的,比如进入preview和
capture需要delay的帧数,clock的极性等等。
getCurrentSensorType就是看一下sensor输出数据的类型。
再往下setTgphasesetSensorIOcurrentinitCSI2peripheralsetCSI2config,这些就是和数据
流传送对应的硬件驱动配置的hal接口了,TG是时间触发器,CSI2config指CameramipiBB
端的总线配置。
可以参考这张数据流的图示
终于到打开了,还是调用imgsensor_的接口
=>imgsensor_
熟悉的ioctl,直接贴2675的接口
再读次id,之前先软件reset。然后就是几千个寄存器的初始化,打开camera,会延迟一段
时间就是这边的缘故。
打开sensor后,将进入preview,这边的上层调用没有具体分析,通过抓底层接口的log,看
下有哪些操作。
先看下open前的,对照下刚才分析的打开流程,查看下调用时序。
featurecontrol的id如下
基本一致,再看看preview前的
上层调用,有很多底层并未去实现,preview前主要在调用yuvsetting这块
主要是一些特效设定,便于在preview时通过apk里的设置项来调整
上层可以调ioctl切换preview和capture模式,preview的入口
Imgsensor端preview的接口主要是寄存器的配置,包括clock,size,AE调整等,capture的与
之类似。
贴下preview的流程图:
包括:
3A的实时调整
Pass1,pass2是MTK架构buffer的传递过程,buffer1主要是指硬件buffer的传递,buffer2
是软件buffer的传递。
各层次preview回调
退出切换模式时退出loop