☰
MTK Camera上电流程调试:从开机检测到双摄时序问题排查
2026/10/14 16:11:19 网站建设 项目流程

简介:摄像头驱动开发中,sensor点不亮或双摄无图的根因往往不在ISP配置,而在上电流程。MTK平台开机检测会依据Projectconfig.mk与kdSensorList数组,将所有配置过的sensor按前后摄各扫描一遍,通过硬件引脚隔离识别身份。读ID环节由CAMERA_HW_Ioctl下发,最终在GetSensorID中执行,而kernel_log.boot中的trace log能快速定位异常供电。当power down极性反转时,同一颗sensor可能被系统误记录为前后摄双身份,导致双摄同用无图。本文以imx111反极性案例为线索,梳理了上电链路、读ID流程、常见点不亮坑位及新增sensor的kernel与HAL层落地清单,帮助驱动工程师快速建立MTK camera bringup的完整排查路径。

1. MTK camera调试:上电流程决定八成问题方向

硬件工程师和驱动工程师之间最常出现的"翻车现场"就是:摄像头单独点亮没问题,前后摄一起用就黑屏;或者换了新模组之后,log里根本搜不到这颗sensor的ID。这类问题十有八九不在ISP配置,而在上电流程。MTK平台的开机检测逻辑会把所有配置过的sensor各扫两遍(当作后摄扫一遍、当作前摄扫一遍),靠硬件引脚隔离来区分身份。这份调试指南以mt89_v10_vanzo_test项目为例,把上电链路、读ID流程、极性反置导致的双摄互斥案例、以及新增sensor需要改动的文件全梳理了一遍。适合正在做MTK camera bringup、遇到sensor点不亮或双摄时序问题的驱动工程师,新手可以直接按章节复现排查路径。

2. 开机检测链路:kdSensorList与kdSetDriver如何决定前后摄

很多人拿到一份camera驱动,第一反应是去看sensor的init函数、去翻I2C地址,却忽略了一个前提:这颗sensor到底有没有进入系统的检测名单。MTK平台的开机检测,是从Projectconfig.mk里配置的sensor列表开始的。系统只检查配置过的传感器,而"配置过"这件事由两个地方共同决定,任何一处漏改,都会导致一颗驱动写得完全正确的sensor在log里直接消失。

2.1 Projectconfig.mk配置与kdSensorList数组的对应关系

以文档中的imx111_mipi_raw、ov9724_mipi_raw为例,Projectconfig.mk里需要同时配置HAL层和Kernel层的sensor列表:

CUSTOM_HAL_IMGSENSOR=imx111_mipi_raw ov9724_mipi_raw CUSTOM_KERNEL_IMGSENSOR=imx111_mipi_raw ov9724_mipi_raw

这两行分别对应HAL层和Kernel层的编译开关。开机后,系统调用kd_sensorlist.c里的检测入口,这个函数第一步就是调用kdGetSensorInitFuncList()拿到一个sensor列表指针,这个指针指向的kdSensorList[]数组定义在mediatek/custom/common/kernel/imgsensor/src/kd_sensorlist.h中。

/* kd_sensorlist.h */ static struct kdSensorList kdSensorList[] = { #ifdef IMX111_MIPI_RAW_SUPPORT { IMX111_MIPI_RAW_SENSOR_ID, SENSOR_DRVNAME_IMX111_MIPI_RAW, imx111_mipi_raw_init }, #endif #ifdef OV9724_MIPI_RAW_SUPPORT { OV9724_MIPI_RAW_SENSOR_ID, SENSOR_DRVNAME_OV9724_MIPI_RAW, ov9724_mipi_raw_init }, #endif };

每个数组元素包含三个关键信息:sensor ID、驱动名字字符串、初始化函数指针。imx111_mipi_raw_init就是后面真正干活的人口——它负责注册该sensor的open、getid等操作函数。需要特别注意的是,SENSOR_DRVNAME_IMX111_MIPI_RAW这个宏必须和sensor驱动文件里定义的字符串完全一致,否则即便数组里有这一项,后续按名字匹配驱动时也会落空。常见的做法是,新加sensor时先看kd_imgsensor.h里有没有对应的枚举和宏定义,没有就先补上,再回头填数组。

2.2 kdSetDriver的pDrvIndex参数:一个int里塞了两段信息

拿到sensor列表之后,检测流程会进入kdSetDriver。这个函数有一个pDrvIndex参数,它是个数组,第一个成员被反复使用。文档里明确说明了它的编码规则:前16位表示后摄和前摄的下标(取值1和2),后16位表示sensor的索引(取值1、2、3、4)。

/* kdSetDriver 的典型调用方式 */ int pDrvIndex[2] = { 0 }; /* 后摄检测:高位写1表示后摄,低位写当前sensor序号 */ pDrvIndex[0] = (1 << 16) | sensor_index; ret = kdSetDriver(pDrvIndex); /* 前摄检测:高位写2表示前摄 */ pDrvIndex[0] = (2 << 16) | sensor_index; ret = kdSetDriver(pDrvIndex);

这种一个int塞两段信息的做法在MTK底层比较常见,本质上是省参数。高位1和2是摄像头位置标识,低位是sensor在配置列表里的序号。第一个成员是目前主用的,log里出现[0]就表示用的是pDrvIndex[0],第二个成员文档里明确说"目前还没有看到有调用过"。这也是很多刚接触MTK平台的工程师容易懵的地方:明明只有一个参数,为什么又能表示位置又能表示序号——就是因为高低位拆开了。

2.3 前后摄不区分的检测顺序:硬件隔离才是最终判据

想理解这套机制,必须接受一个反直觉的结论:在开机的检测阶段,MTK平台完全不区分你配置的sensor是前摄还是后摄。它先把所有配置的sensor当成后摄,逐个上电读ID;再把所有sensor当成前摄,逐个上电读ID。那最终怎么确定谁是前摄谁是后摄?靠硬件。

/* 伪代码描述检测顺序 */ for (camera_position = MAIN; camera_position >= SUB; camera_position--) { for (i = 0; i < sensor_count; i++) { /* CHECK_IS_ALIVE: 给sensor上电 */ /* FEATURECONCTROL: 调用该sensor的GetSensorID */ } }

后摄和前摄的power down在硬件上是分开的。当系统把所有sensor当后摄去上电时,前摄那条电源通路根本没通,前摄sensor自然读不到ID。等到系统把所有sensor当前摄上电时,只有真正接在前摄位置的sensor能读到ID,于是它被记录为前摄。这个设计很巧妙,但有个前提:power down极性必须正确。一旦极性反了,前摄该拉低时反而拉高,后摄的通路可能一直保持带电状态,就会导致一颗sensor在"后摄扫描"和"前摄扫描"两轮里都被读到ID——这正是后面案例里双摄出问题的根源。理解这一节,就理解了整条开机检测链路的基本盘。

3. 读ID全流程:CAMERA_HW_Ioctl与kernel_log.boot的log定位法

上一节讲的是sensor如何被纳入检测范围,这一节落到具体执行:系统到底在哪一步给sensor上电,在哪一步调用GetSensorID,以及最关键的问题——出了问题,从哪条log开始查。很多工程师习惯于一上来就接串口抓kernel log,其实MTK平台mobilelog里的kernel_log.boot文件已经把开机检测阶段的关键痕迹都留下来了,用对了这个文件,可以省掉大半串口抓log的折腾。

3.1 KDIMGSENSORIOC_T_CHECK_IS_ALIVE:上电动作藏在这个case里

读ID的完整调用链,是从CAMERA_HW_Ioctl这个函数开始的。它是camera硬件驱动的ioctl分发入口,检测sensor是否存活时,命令字会走到KDIMGSENSORIOC_T_CHECK_IS_ALIVE这个case:

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: /* 第二步:调sensor驱动的GetSensorID读ID */ i4RetValue = adopt_CAMERA_HW_FeatureControl(pBuff); break; default: break; } return i4RetValue; }

KDIMGSENSORIOC_T_CHECK_IS_ALIVE这个case做的事情就是上电。它最终会走进kdCISModulePowerOn,也就是前面提到的上电函数。这一步如果失败,后面的读ID根本没有机会执行。重点在于:这个case不关心这是前摄还是后摄,它只负责把当前扫描的sensor对应引脚的电平拉起来。所以当power down极性配置错误时,这一步就可能把不该供电的sensor也带起来。

3.2 KDIMGSENSORIOC_X_FEATURECONCTROL:GetSensorID真正被执行的位置

上电之后,系统紧接着调用adopt_CAMERA_HW_FeatureControl()。这个函数会处理sensor的控制命令,读取ID也是通过它下发到具体sensor驱动里的。sensor驱动暴露出来的接口中,GetSensorID函数在这个阶段被调用。如果读到的ID和驱动里定义的一致,系统就认为这颗sensor存在,并将其记录在案。

/* imx111_mipi_raw_Sensor.c 中 GetSensorID 的典型结构 */ static kal_uint32 imx111_mipi_raw_GetSensorID(void) { kal_uint32 sensor_id = 0; /* 通过I2C读取sensor寄存器0x0000和0x0001 */ sensor_id = (read_i2c_byte(0x0000) << 8) | read_i2c_byte(0x0001); if (sensor_id == IMX111_MIPI_RAW_SENSOR_ID) { /* 读到预期ID,返回成功 */ return ERROR_NONE; } return ERROR_SENSOR_CONNECT_FAIL; }

这里有一个新手容易误解的点:系统记录的不是"这颗sensor叫什么名字",而是"在某个位置上存在一颗能读到ID的sensor"。后续打开camera时,CameraService去找的是这个记录,而不是重新去枚举一遍。所以一旦检测阶段把sensor的身份记错了,后面打开摄像头就会拿错误的配置去初始化,表现出来就是"读到ID了却不出图像"。这也是为什么我们反复强调检测阶段干净的重要性——开机的记录错了,后面全是白费功夫。

3.3 kernel_log.boot里面那行trace log到底怎么读

文档里反复提到一个关键字:kernel_log.boot。这个文件在mobilelog目录下,抓取方式就是正常的mobilelog抓取流程,不需要额外接串口。对定位开机检测阶段的问题非常有用。关键日志长这样:

[0][1][1][imx111mipiraw][32] [0][1][2][imx111mipiraw][32]

第一眼看上去像一串乱码,拆开看其实信息密度很高。第一个[0]表示当前正在使用的是pDrvIndex数组的第一个元素,第二个[1]表示enable使能状态,第三个字段[1]或[2]是位置标识:1代表当前作为后摄走上电流程,2代表当前作为前摄走上电流程。[imx111mipiraw]是sensor驱动的名字,最后的[32]是sensor名字缓冲区的最大长度。

对照文档中的案例:第一次出现[0][1][1][imx111mipiraw],是imx111被当作后摄上电读ID,这是正常的;问题出在第二行[0][1][2][imx111mipiraw],因为imx111实际接在后摄位置,它不应该出现在前摄扫描里。如果log里出现了这种"不该出现的人",说明前摄扫描时imx111意外被上电了。这个trace log就是整个排查流程的切入点。实际操作中,直接搜索sensor名字(比如搜"imx111")就能把相关行全部捞出来,不用关心前后文的干扰信息。

4. imx111反极性案例:前后摄单独亮、同用无图的根因与修复

这一节是整个调试指南里最有价值的部分。表面现象很典型:前摄单独用能亮,后摄单独用也能亮,但前后摄同时使用时,后摄能读到ID却不输出图像。如果没看过这份资料,按常规思路去查ISP配置、去查mipi信号,可能几天都定位不到根因。实际上问题出在开机检测阶段sensor身份被污染了。

4.1 现象复盘:imx111在两轮扫描中都读到了ID

项目配置里imx111接在后摄位置,ov9724接在前摄位置。按照正常逻辑,开机检测时imx111只应该在"作为后摄扫描"那一轮被读到ID。但实际操作中发现,使用mobilelog抓取kernel_log.boot并搜索imx111后,能看到它在后摄扫描和前摄扫描两轮中都成功读到了ID。

# 抓取后搜索 imx111 相关日志 grep -i "imx111" kernel_log.boot [0][1][1][imx111mipiraw][32] # 后摄扫描,读到ID,正常 [0][1][2][imx111mipiraw][32] # 前摄扫描,也读到ID,异常

这个现象的严重性在于:系统在开机时把imx111记录在了两个位置上——后摄位置有它,前摄位置也有它。后面用户打开前摄时,系统认为imx111就是前摄;打开后摄时,系统又认为imx111是后摄。同一颗sensor被两个身份同时引用,而它的电源和时钟其实只接通在后摄位置,前摄那一路根本没有物理连接。于是后摄能读到ID(因为硬件在位),但初始化时可能因为驱动状态被前摄的打开流程先行改动,导致真正打开后摄时出不来图像。

4.2 根因分析:power down极性与默认代码相反

为什么会两轮都被读到ID?原因出在power down极性和MTK默认代码相反。默认的上电代码假设power down引脚是"低电平有效"——拉低进入工作状态,拉高进入关闭状态。但imx111和ov9724的模组实际是反过来的:必须拉高才能工作,拉低反而被关闭。这就导致了一个连锁反应:系统在扫描前摄时,会先把前摄相关的power down引脚按默认逻辑拉低(想让ov9724工作),但后摄的power down引脚此时也处在"不该工作却工作了"的状态里,imx111趁这个机会把ID吐了出来。

/* kd_camera_hw.c 中定义的全局标志 */ bool camera_pdn_reverse = 0; /* kdCISModulePowerOn 中根据标志决定极性 */ static int kdCISModulePowerOn(...) { /* 常规上电步骤省略 */ if (camera_pdn_reverse) { /* 反转power down引脚电平 */ mt_set_gpio_out(GPIO_CAMERA_PDN, GPIO_OUT_ONE); } else { mt_set_gpio_out(GPIO_CAMERA_PDN, GPIO_OUT_ZERO); } }

修复方式是引入camera_pdn_reverse这个全局变量,初始值为0。在imx111驱动的GetSensorID函数中,成功读到ID之后再把它置为1。这样做的含义是:第一次(也就是检测阶段)给imx111上电时,极性其实是错的,需要在GetSensorID里手动矫正一次,让它能读到ID;读到之后置1,后面真正打开camera时,kdCISModulePowerOn看到这个标志,就会用正确极性上电。

4.3 修复的代价:为什么GetSensorID要承担额外职责

这种方案的巧妙之处在于它不改变整体上电流程,而是通过一个全局标志在检测阶段完成极性校正。但代价也明显:GetSensorID函数不再只是"读寄存器返回ID"的纯净函数,它还得负责判断是否要翻转极性,并且要保证这个翻转动作在正确的时机发生且只发生一次。

/* imx111_mipi_raw_Sensor.c */ static kal_uint32 imx111_mipi_raw_GetSensorID(void) { kal_uint32 sensor_id = 0; /* 手动矫正power down极性,保证这次能读到ID */ mt_set_gpio_out(GPIO_CAMERA_PDN, GPIO_OUT_ONE); sensor_id = (read_i2c_byte(0x0000) << 8) | read_i2c_byte(0x0001); if (sensor_id == IMX111_MIPI_RAW_SENSOR_ID) { /* 读到ID后修正全局极性标志,后续open时走正确流程 */ camera_pdn_reverse = 1; return ERROR_NONE; } return ERROR_SENSOR_CONNECT_FAIL; }

这里要刻意关注两个边界条件。第一,这个手动矫正动作在正常使用阶段不会重复执行——因为系统一旦在检测阶段记录完sensor,后面打开摄像头走的是open流程而不是再调GetSensorID,所以矫正动作恰好只执行一次。第二,文档强调imx111是"目前所有的sensor中最复杂的一个",因为它同时在MT6589项目上既做前摄也做后摄。作为后摄时要按后摄的引脚定义上电,作为前摄时要按前摄的引脚定义上电。而它在第一次被调用时系统以为是后摄,如果这时候给它按前摄的引脚定义正确上电,它反而会被误判为后摄——这中间的取舍非常微妙。调试这类问题时,建议先确认这颗sensor在当前项目里是纯前摄、纯后摄还是前后摄同型号,再决定GetSensorID里要不要做身份相关的分支处理。

5. 避坑排查:camera点不亮的常见问题与排查顺序

到了这一章,把调试过程中反复踩过的坑集中整理出来。每一条都是实际发生过的问题,按"现象→原因→解决"的顺序拆解,可以直接当作排查checklist来用。

坑1:抓到的kernel_log.boot里搜不到sensor名 现象:mobilelog里确实有kernel_log.boot文件,但搜索sensor名字(如imx111)一条结果都没有。 原因:多半是抓log时开机阶段已经过去,boot日志被轮转覆盖;或者是当前软件版本根本没有把sensor加进Projectconfig.mk编译开关。 解决:先确认CUSTOM_KERNEL_IMGSENSOR配置里有这颗sensor;然后重新抓一次包含完整开机过程的mobilelog,最好在复现问题时保持设备刚从关机状态启动,别在系统运行很久之后才去抓log。搜索时注意大小写,kernel log里sensor名字通常是小写拼接。

坑2:读到ID但不出预览图像 现象:kernel_log.boot里能看到某颗sensor读到了ID,但打开camera就是黑屏或卡住。 原因:ID能读到说明I2C和上电基本正常,问题往往出在身份记录被污染(比如前后摄都被同一颗sensor写入)、mipi时钟配置不对、或者sensor的pclk频率和平台端不匹配。 解决:先回kernel_log.boot确认这颗sensor是否只出现在正确的那一轮扫描里。如果出现在了两轮里,优先检查power down极性和前后摄GPIO隔离,不需要急着调ISP。如果身份记录正常,再去看mipi频率和lane数配置是否与模组规格书一致。下表整理了分流判断方法:

现象优先排查方向排查手段
搜不到sensor名编译配置/驱动注册检查Projectconfig.mk和kd_sensorlist.h
两轮扫描都读到ID极性/GPIO隔离检查power down引脚电平与极性配置
读到ID但不出图mipi/身份记录抓camera打开时的kernel log看报错
完全无上电反应硬件电源万用表量avdd/dovdd/afvdd供电

坑3:新加sensor改了一堆文件还是detect不到 现象:驱动代码看起来都写好了,kd_sensorlist.h也加了数组项,开机还是搜不到。 原因:漏改了kd_imgsensor.h里的枚举定义或SENSOR_DRVNAME宏,导致数组编译进去但名字匹配不上。另一个高频漏改点是HAL层的SensorList.cpp,这个文件是HAL层感知sensor存在的依据,只改kernel层文件,HAL层就会静默失败。 解决:把第6章的文件清单过一遍,逐一确认kernel层三个位置和HAL层两个位置都改到了。改完不要只编kernel,HAL层相关库也要重新编译。

坑4:probe阶段偶发读ID失败,复位后恢复正常 现象:冷启动时读ID失败,但热重启或再次开关camera就正常了。 原因:sensor的复位时序太紧,上电后立刻读ID,sensor内部还没稳定。这类问题和模组本身关系不大,更多是上电时序的延时配置不够。 解决:在kdCISModulePowerOn里给power down变为高电平之后、发起I2C读操作之前,加一个10ms~20ms的延时。注意别加太长,否则开机检测全部sensor的时间会被明显拉长。

坑5:判断i2c问题与上电问题的分界线 现象:前后摄都打不开,但不确定是总线不通还是没供电。 原因:很多情况下I2C地址没配错,驱动也加载了,但电源树没配对——sensor的avdd或dovdd没被拉起来,I2C通信自然失败。 解决:优先在kernel_log.boot里确认有没有读到ID的痕迹。如果一颗sensor都读不到,先量硬件供电;如果部分sensor读到,部分读不到,对比读不到的那颗和读得到的之间的上电配置差异,重点查GPIO定义和power down极性。

6. 新增sensor驱动落地清单:从kernel层到HAL层一次改全

以imx111_mipi_raw驱动为模板,把需要改动的文件重新梳理一遍。kernel层和HAL层各有一组文件,少改任何一个,新sensor都可能在检测阶段"隐身"。

6.1 kernel层三处改动

第一处是新增驱动文件夹。在mediatek/custom/common/kernel/imgsensor/下创建imx111_mipi_raw/目录,内部放imx111mipiraw_Sensor.c、imx111mipiraw_Sensor.h等驱动文件。第二处是修改kd_sensorlist.h,把新sensor加进kdSensorList[]数组。第三处是修改kd_imgsensor.h,添加sensor ID枚举、SENSOR_DRVNAME宏以及编译开关的宏定义。

/* kd_imgsensor.h 中需要添加的定义(示意) */ #define IMX111_MIPI_RAW_SENSOR_ID 0x111 #define SENSOR_DRVNAME_IMX111_MIPI_RAW "imx111_mipi_raw"

6.2 HAL层两处改动

HAL层需要在mediatek/custom/mt6589/hal/imgsensor/下添加同名的imx111_mipi_raw文件夹,里面放HAL层使用的sensor配置文件。然后修改mediatek/custom/common/hal/imgsensor/src/sensorlist.cpp,在这个文件里注册新sensor,这样HAL层才能在上层调用时找到对应的驱动实例。这一步做完,新sensor才算真正接入系统。

6.3 验证流程:加完驱动后确认真的生效

改完文件之后,建议固定走一遍验证流程。编译烧录后,先抓一次包含完整开机过程的mobilelog,搜索新sensor名字,确认它出现在检测日志里;然后分别打开前摄和后摄,确认都能出图像;最后做一次前后摄连续切换的压力测试,观察是否出现"读ID正常但无图像"的异常。文档里那个imx111案例就是输在这一步上——单独用都正常,双摄同用才暴露问题。

从那以后我每次加完新sensor,都会强制走一遍"开机log确认检测、单摄点亮、双摄切换"三件事,不通过就不算完成。这套流程看起来笨,但真的能拦住九成以上的隐性时序问题。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询