简介:面向MTK平台驱动开发者的Camera调试指南,内容围绕89平台常见sensor(imx111、imx188、imx135、ov9724、soc5140等)展开,系统讲解开机阶段sensor检测、上电流程、初始化调用链,以及排查camera点不亮问题的完整思路。文档从kd_sensorlist.c、kd_camera_hw.c等关键源码入手,说明如何修改sensor配置数组、设置前后摄识别、判断i2c通信与ID读取异常,并通过实际案例展示power down引脚极性导致sensor被前后摄同时读到的典型故障,附kernel_log.boot等trace日志辅助定位。资源为docx格式,共1个文件,约231KB,内容紧凑,适合手机驱动、BSP或Camera相关开发人员查阅。目前已有1630人学习使用,可作为MTK Camera调试与排错的实用参考。
1. MTK Camera 调试:从点不亮到前后摄打架的完整排查路径
做 MTK 驱动调试的人,多半都遇到过这种场景:新项目拿到手,前后摄单独都能点亮,一同时用就后摄黑屏;或者开机 log 里明明读到 sensor id 了,预览却一片漆黑。这类问题在 MTK Camera 调试里最容易让人翻车,因为根因往往不在 hal 层,而在开机上电这条隐性的链路里。这份调试指南以 89 平台 mt89_v10_vanzo_test 项目为例,把 imx111、ov9724 这类 sensor 从上电、读 ID、登记到前后摄归属判定的完整机制拆开讲了一遍,还附了新增一颗 sensor 需要改的文件清单。适合做手机 BSP、sensor 驱动移植、或者刚接触 MTK camera 调试点不亮问题的开发者,照着能少走很多弯路。
2. 开机检测与上电链路:从 ProjectConfig 到 GetSensorID
2.1 起点:ProjectConfig.mk 里的一行配置
MTK 平台在开机会去检查当前软件所有配置过的 camera sensor,判断依据就是 ProjectConfig.mk 里的两行:
CUSTOM_HAL_IMGSENSOR=imx111_mipi_raw ov9724_mipi_raw CUSTOM_KERNEL_IMGSENSOR=imx111_mipi_raw ov9724_mipi_raw这里有两个值得说的点。第一,HAL 和 Kernel 两处都要配,漏掉任何一个都会导致驱动加载不完整,这是我们排查新 sensor 不识别时首先检查的地方。第二,sensor 名字的命名规则是「型号_接口_类型」,imx111_mipi_raw 表示 imx111 走 MIPI 接口、RAW 输出,ov9724_mipi_raw 同理。这个命名不是随便起的,它和后面驱动文件夹的名字、kdSensorList 里的字符串必须完全一致,否则开机时按名字匹配驱动会失败。
开机后系统会逐个去 detect 这两颗 sensor,判断方式简单粗暴:给 sensor 上电,然后通过 I2C 读它的 ID 寄存器,读到就认为这颗 sensor 存在且通信正常。整个过程对用户是不可见的,但任何一个环节出问题,camera 就打不开。
2.2 kdSensorList:系统认识 sensor 的唯一户口簿
读 ID 的起点在 mediate/custom/common/kernel/imgsensor/src/kd_sensorlist.c,核心是 kdGetSensorInitFuncList 这个函数,它把系统配置的所有 sensor 初始化函数指针抓出来:
// kd_sensorlist.c static struct kd_sensor_init_func kdSensorList[] = { /* 数组里每一项都指向对应 sensor 的初始化函数 */ {imx111_mipi_raw_init}, {ov9724_mipi_raw_init}, }; int kdGetSensorInitFuncList(struct kd_sensor_init_func **ppSensorList) { /* 把 kdSensorList 的地址交给上层,上层逐个调用初始化函数 */ *ppSensorList = kdSensorList; return 0; }参数说明:ppSensorList 是二级指针,返回的是整个数组的首地址。上层拿到这个列表后,会逐个调用每个 sensor 的 init 函数,把驱动注册进系统。如果新加的 sensor 没放进 kdSensorList,那它就像没户口的人,系统完全不知道你配置过它,哪怕你在 ProjectConfig.mk 里写了也没用。
这里有个容易忽略的细节:加新 sensor 要改的不只这一处。kdSensorList 是链表头,kd_imgsensor.h 里通常还要加 sensor 的枚举定义,HAL 层也有一个对应的列表,后面第 6 章会完整列出所有修改点。
2.3 从 Ioctl 到 GetSensorID:一次完整的上电读 ID
开机检测的调用入口是 CAMERA_HW_Ioctl,这是一个标准的内核 file_operations 里的 ioctl 分发函数。整个读 ID 流程实际被拆成了两个 ioctl 命令:
// kd_camera_hw.c static long CAMERA_HW_Ioctl( struct file *a_pstFile, unsigned int a_u4Command, unsigned long a_u4Param) { switch (a_u4Command) { case KDIMGSENSORIOC_T_CHECK_IS_ALIVE: /* 第一步:给 sensor 上电 */ i4RetValue = adopt_CAMERA_HW_CheckIsAlive(); break; case KDIMGSENSORIOC_X_FEATURECONCTROL: /* 第二步:在 pbuffer 里调用具体 sensor 的 GetSensorID */ i4RetValue = adopt_CAMERA_HW_FeatureControl(pBuff); break; } }顺序很重要:先 CHECK_IS_ALIVE 上电,再 FEATURECONCTROL 读 ID。上电这一步内部会走到 kdCISModulePowerOn,按照 sensor 驱动里定义的上电时序逐一拉高或拉低各路电源和复位脚;FeatureControl 则根据传入的 command ID 分发到具体 sensor 的 GetSensorID 函数。
如果读到了 ID,这个 sensor 会被记入系统的一张表中,之后打开 camera 时直接从这个记录里找,不会再重新 detect。这条链路里最容易翻车的是第二个命令:很多人改完驱动只验证了能上电,但 FeatureControl 分发不到 GetSensorID,或者读 ID 的函数写错了寄存器地址,一样识别不了。
3. 前后摄识别黑匣子:pDrvIndex 与硬件隔离机制
3.1 读两遍 ID:不区分前后摄的检测启动
刚接触 MTK 平台的人都会有一个疑问:开机检测时系统怎么知道哪颗是前摄、哪颗是后摄?答案是:它最开始根本不区分。启动检测时,MTK 会把所有配置的 sensor 先全部当成后摄上电读一次 ID,再全部当成前摄上电读一次 ID。也就是说 imx111 会被读两次,ov9724 也会被读两次。
这里就轮到 kdSetDriver 和它的 pDrvIndex 参数出场了。pDrvIndex 是一个数组,第一个成员的位分配很有讲究:
| 位段 | 含义 | 常用取值 |
|---|---|---|
| 前 16 位 | 后摄 / 前摄的下标 | 1 表示后摄,2 表示前摄 |
| 后 16 位 | sensor 在列表中的索引 | 1、2、3、4 等 |
这段位运算逻辑是 MTK 平台的一个约定,很多人第一次看会一脸懵,但搞懂它反而是理解前后摄归属的关键。
3.2 power down 隔离:硬件决定 sensor 归属
那系统最终怎么确定谁是前摄谁是后摄?靠的是硬件上的 power down 隔离。前摄和后摄的 power down 引脚在硬件设计上是分开的,当系统把前摄 sensor 当作后摄去上电时,前摄的 power down 根本没拉起来,电源没到,I2C 自然读不到 ID;反过来也一样。所以:
- 第一轮全部按后摄上电:只有真正接在后摄位置的 sensor 能被读到 ID
- 第二轮全部按前摄上电:前摄位置上的 sensor 趁这个机会被读到了
这就是为什么判定结果不会乱套。读不到的 sensor 不会被记录,读到 ID 的才会被登记进对应的位置。
// kdCISModulePowerOn 内部根据 pDrvIndex 决定电源动作的伪代码 static int kdCISModulePowerOn(sensor_index, power_flag) { if (power_flag == SENSOR_AS_BACK) { /* 按后摄引脚定义拉电:只给后摄 power down 拉高 */ gpio_set_value(BCK_PDN_PIN, GPIO_HIGH); } else if (power_flag == SENSOR_AS_FRONT) { /* 按前摄引脚定义拉电:只给前摄 power down 拉高 */ gpio_set_value(FRT_PDN_PIN, GPIO_HIGH); } /* 这步之后才会去配置 MCLK、AVDD、DVDD 等 */ }参数说明:power_flag 对应 pDrvIndex 前 16 位的值,1 走后摄分支,2 走前摄分支。如果硬件上前后摄的 power down 没分开,或者 GPIO 拉反了,就会出现前后摄都读到同一颗 sensor ID 的情况,这也是第 4 章案例的导火索。
3.3 kernel_log.boot:不抓串口也能看的 trace
定位前后摄检测问题,最实用的工具是 mobilelog 里的 kernel_log.boot,它把开机的内核 log 保存下来了,不用接串口就能查。里面有一行关键 log,格式看起来像这样:
[0][1][1][imx111mipiraw][32] // kernel_log.boot 中的实际行这行 log 每个字段都有具体含义:
| 字段 | 含义 | 说明 |
|---|---|---|
| [0] | pDrvIndex 数组下标 | 目前只用第一个成员,第二个还没见过调用 |
| [1] | enable 标志 | 1 表示允许上电 |
| [1] | 当前按后摄检测 | 1 为后摄,2 为前摄 |
| [imx111mipiraw] | sensor 名字 | 与驱动文件夹名一致 |
| [32] | sensor 名字最大长度 | 一般固定 32 |
看 log 时有个实用技巧:搜索 sensor 型号关键字,比如 imx111,然后把所有出现的位置按时间顺序排一遍,马上就能看出这颗 sensor 在开机时被当成后摄读了几次、当成前摄读了几次、是否都读到 ID。如果一颗本该只认前摄的 sensor 在后摄检测阶段也读到了 ID,就说明上电隔离出了问题。
4. imx111 双摄共用踩坑实录:极性反转与场景耦合
4.1 一颗「双面」sensor:imx111 的特殊性
在 89 平台这批 sensor 里,imx111 的驱动可以说是最复杂的一颗,复杂在两个叠加因素:第一,它的 power down 极性和 MTK 默认代码是反的;第二,它在 MT6589 平台上既做前摄又做后摄。单独看任何一个都不算难,但两个叠加在一起,就会出现 2+2=5 的效果。
先说极性。MTK 默认的 power down 逻辑是高有效,但 imx111 和同项目的 ov9724 都是低有效——也就是 power down 引脚拉低才进入工作状态。如果不做处理,按默认逻辑 GPIO 拉高,sensor 实际被关着,I2C 怎么读都是失败。
再结合双摄共用场景,问题就变得微妙了。imx111 做后摄时,需要按后摄的引脚定义上电;做前摄时,需要按前摄的引脚定义上电。而开机检测流程不分前后摄,它会把 imx111 先当后摄读一遍,再当前摄读一遍。如果在前摄检测阶段给它正确上电了,那它就会被误判为后摄……
4.2 camera_pdn_reverse:极性矫正的时机问题
为了解决极性反向,代码里引入了一个全局变量 camera_pdn_reverse,定义在 kd_camera_hw.c 中,初始值为 0。这个变量的赋值时机非常讲究——不是在上电前,而是在 imx111 的 GetSensorID 函数中,读到 ID 之后才置为 1:
// imx111mipiraw_Sensor.c static void imx111_get_sensor_id(void) { /* 上电和读 ID 都按当前默认极性来,第一次读通常是失败的 */ if (read_sensor_id(&imx111_id) == SUCCESS) { /* 读到 ID 说明此刻电源状态是对的,把极性标记翻转 */ camera_pdn_reverse = 1; } }为什么非要等读到 ID 才翻转?因为上电阶段根本不知道这颗 sensor 的极性对不对。如果在上电一开始就把 polarity 置反,那对于默认极性的 sensor 来说反而是错的。这里的做法是在 imx111 的驱动内部手动矫正 power down 极性,把 GPIO 电平翻转一次,让 ID 能被读到,然后立刻记下「这颗 sensor 需要反极性」这个事实。
// kd_camera_hw.c 中的使用逻辑 if (camera_pdn_reverse == 1) { /* imx111 的 power down 是低有效,实际拉电时取反 */ gpio_set_value(CAMERA_PDN_PIN, !power_on_level); } else { /* 默认高有效,按原始配置走 */ gpio_set_value(CAMERA_PDN_PIN, power_on_level); }这个动作只需要做一次。正常使用场景下,系统开机 detect 完之后不会再调 GetSensorID,而是直接走 open 流程,open 时用的是已经稳定下来的 camera_pdn_reverse 值。这也是为什么很多新人看代码会觉得绕:GetSensorID 里做的不只是读 ID,还顺带完成了一次「极性自校准」。
4.3 翻车现场:mt89_v10_vanzo_test 前后摄打架
指南里记录了一个非常典型的案例:mt89_v10_vanzo_test 项目,前后摄单独使用时都能正常点亮,但前后摄同时使用时,后摄能读到 ID 却没有图像输出。这类问题最坑的地方在于,单摄测试全通过,很容易让人把怀疑对象放到上层应用或 hal 层,实际根因却藏在开机检测的逻辑里。
问题的根源在于 imx111 的 GetSensorID 函数在实际项目里没有考虑到「自己被当成后摄调用过一次」这个背景。当开机检测第一轮把它当后摄时,它作为前摄本不该读到 ID,但如果这个函数的实现没有判断当前是以什么身份在上电,而是傻傻地按后摄引脚定义给前摄也上了电——那它的 ID 就同时被前后摄都记录到了。
后果就是后摄的检测结果里混进了 imx111,但硬件上它并不存在于后摄位置。真正打开后摄时,系统按记录找到 imx111、尝试出图,可后摄的电源和引脚根本没接这颗 sensor,自然只有 ID 没有图像。这种问题让很多人排查了一两天,最后才发现是上一轮被「误上电」导致 ID 污染了登记表。
正确做法是在 GetSensorID 里加一个判断:如果是第一轮以「非真实身份」被调用,就不要给它正确的上电时序,宁可读不到 ID,也不能让它被误登记;等第二轮以真实身份被调用时再给完整上电时序。说白了,这颗 sensor 的 GetSensorID 不是幂等的,它对调用时机敏感,写驱动时必须把这个场景考虑进去。
5. 避坑与排查:点不亮到底卡在哪一环
5.1 现象:camera 完全点不亮,打开就黑屏
原因分两类:要么 I2C 不通、读不到 ID,开机时 sensor 没被登记;要么 ID 读到了,但打开后没有预览图像输出。这两个原因排查方向完全不同,所以第一步永远是确定「到底读没读到 ID」。
解决的思路是看 kernel_log.boot 里那颗 sensor 有没有出现在登记 log 里。如果连 ID 都没读到,重点查上电时序和 I2C 地址;如果 ID 读到了但没图像,重点查 MCLK、MIPI 信号和 sensor output 配置。最常见的低级错误是 I2C 地址写错——同一型号 sensor 有多个地址版本,驱动里默认地址和实际硬件对不上,读 ID 必挂。
5.2 现象:开机 log 里前后摄都读到了 imx111 的 ID
原因:imx111 和 ov9724 的 power down 极性与 MTK 默认代码相反,导致在两轮检测中被重复读到。具体来说,第一轮按后摄检测时,imx111 的电源没被正确关闭,GPIO 电平把前摄也激活了。这个问题的本质不是读 ID 失败,而是隔离失败。
解决:参考第 4 章的 camera_pdn_reverse 机制。在 GetSensorID 中读到 ID 后置位极性反转标记,同时注意上电时根据当前检测身份决定是否真正给电。另外要检查原理图上前后摄的 power down 是否真的分开,如果硬件上共用了一个 GPIO,那软件再怎么调都白搭。
5.3 现象:前后摄单独都正常,同时用就出问题
原因:这是前后摄 sensor 在开机检测时互相干扰的典型症状。单独测时只跑一轮检测,没问题;双摄同时启用时,第一轮后摄检测把前摄 sensor 误上电,污染了 ID 登记表,后摄位置记录了两颗 sensor 的 ID。
解决:打开 kernel_log.boot,搜索两颗 sensor 的名字,看它们在开机时分别出现在哪几轮检测 log 里。正常情况下,后摄 sensor 只应该出现在第一轮(按后摄检测),前摄只应该出现在第二轮。如果出现交叉,就按 4.3 节的方案在 GetSensorID 里区分调用时机。
5.4 现象:新加 sensor 后系统完全没反应,开机 log 里没有它的任何踪迹
原因:ProjectConfig.mk 里配置了,但 kdSensorList 数组没加,或者 sensor 名字字符串和驱动文件夹名不一致,导致匹配失败。
解决:先核对 ProjectConfig.mk 里的型号名,再对照 kd_sensorlist.h 里的数组项,确保逐字符一致;然后检查 Kernel 层和 HAL 层的 sensorlist 都加了对应条目。这类问题用 grep 搜一遍型号名,看它出现在哪些文件里,基本能定位。我的习惯是加完新 sensor 后先 grep 确认至少出现在 5 个以上文件里,少于这个数大概率漏改。
6. 二次开发:从零添加一颗新 Sensor 的完整改造
最后落到实际改造。以 89 平台添加 imx111 为例,新增一颗 sensor 需要动 Kernel 和 HAL 两层,共四个位置:
mediatek\custom\common\kernel\imgsensor\imx111_mipi_raw\ # Kernel 驱动文件夹 mediatek\custom\common\kernel\imgsensor\src\kd_sensorlist.h # 注册 Sensor 初始化函数 mediatek\custom\common\kernel\imgsensor\inc\kd_imgsensor.h # 增加 Sensor 枚举与宏定义 mediatek\custom\common\hal\imgsensor\src\sensorlist.cpp # HAL 层 Sensor 列表注册如果把 HAL 层的 imx111_mipi_raw 文件夹也算上,实际是五个位置。驱动文件夹里放的是 sensor 的上下电时序、GetSensorID、open、preview 设置等核心代码,一般从原厂拿到后做少量修改就能用。修改点通常集中在三处:I2C 地址、power down 极性、上电时序的 GPIO 编号。
改造完成后怎么验证?先看开机 log 确认 sensor 被识别,再分别验证前摄和后摄单独预览、前后摄切换、冷启动和热启动。我自己在这类问题上掉过最大的坑,是只验证了单摄场景就交付,结果双摄场景翻车,跟 mt89_v10_vanzo_test 的案例一模一样。从那以后我每次加完 sensor 都会强制走一遍双摄同时启用的用例,并且必须在 kernel_log.boot 里核对两轮检测的 ID 归属。希望帮到你。
本文还有配套的精品资源,点击获取