做显示驱动调试这些年我越来越觉得处理速度往往不取决于你会不会写代码而取决于你手上有没有合适的工具以及知不知道在哪个环节用哪个工具。这是系列的第5篇我想把显示驱动开发中最常用、关键时候能救命的一批调试工具梳理出来。这篇文章不会讲某个芯片厂商的私有SDK而是把下层Linux DRM/KMS环境下从点亮一块屏到排除疑难杂症的通用工具串一遍。如果你刚开始做显示驱动可以用它当一份工具地图如果你已经有几年经验我更希望它帮你建立一个判断遇到现象先想该用哪个工具而不是先猜代码错在哪儿。1. 显示驱动调试其实在调一条“像素链路”我入行时带我的老师傅说过一句话显示驱动调试不是“调代码”而是“调一条从GPU到像素的链路”。这句话到现在我都觉得是理解这一行的最好入口。一帧图像从应用层生成命令到GPU渲染到显存里的framebuffer再到显示控制器把这块buffer按时序读出来送进Panel中间还要经过plane合成、color correction、pixel format转换最后才能变成你眼睛看到的光。这整条链路上任何一个环节的时序、格式、属性没有对齐都会表现为黑屏、花屏、偏色、亮度不对甚至只是偶尔闪一下。问题往往不是简单的“某行驱动代码写错了”而是链路中某个环节是不是真的匹配。所以决定用什么调试工具之前先想清楚你正在调的是“链路的哪一段”。1.1 把“软件状态、硬件信号、面板需求”分开看我一般把显示驱动的调试对象分成三层三层用的工具完全不一样状态层当前使用哪个connector、哪个modeCRTC是否使能plane绑定的是哪个framebuffer像素格式是什么。这一层只需要软件工具比如modetest、dmesg、debugfs就能看得很清楚。属性层连接器支持哪些分辨率EDID内容是什么面板要求的color depth、bus format、同步极性是哪些。这一层同样在软件里能查但许多人容易忽略。信号层实际输出的PCLK、HSYNC、VSYNC、DE 波形是否正确MIPI DSI的lane时序、背光PWM频率是否和设置一致。这一层软件只能看个大概必须用示波器、逻辑分析仪这些仪器来判案。很多新入行的同事一上来就把示波器探头夹在排线上或者只盯着dmesg看报错都是把工具用错了位置。我建议你先问自己一个问题“我现在这个现象最可能出在链路的哪一段”然后再决定开哪个工具。1.2 按现象选工具而不是按心情改代码举几个我常遇到的例子如果现象是“系统起来后屏幕偶尔闪花一下”dmesg里没有任何error你更需要的是打开DRM的atomic日志用ftrace抓vblank和fence事件看看是不是哪个commit在等待时超时。这时候反复改时序参数往往无效因为问题根本不在时序。如果现象是“画面有了但是颜色明显不对红色发暗、蓝色发亮”你八成要检查framebuffer的format和panel节点的bus-format是否一致而不是去调gamma。如果现象是“完全黑屏但背光亮着”说明电源、背光都正常问题大概率在eDP或MIPI的数据通道、时序、或者显示控制器根本没有正常输出这时候示波器比改代码有用得多。如果是“连接器状态unknown明明屏接了就是不亮”一般先查热插拔检测HPD和DDC通道用逻辑分析仪/i2c工具读EDID而不是去debug frame buffer。工具选择只要对了很多问题半小时内就能定位工具没对上可能在同一个坑里连改几周代码。1.3 显示驱动调试工具的速查表下面这张表是我自己习惯的工具分类按调试对象整理出来后续每节都会展开调试对象典型现象优先工具辅助工具状态与链路黑屏、无输出、模式设置失败modetest、dmesg、/sys/kernel/debug/dridrm_info、weston属性与面板偏色、分辨率设不上去edid-decode、modetest -p、panel dts节点i2c-tools、逻辑分析仪内核执行路径卡顿、休眠唤醒异常、诡异时序dynamic_debug、ftrace/trace-cmdDRM debug参数、kgdb信号与时序花屏、滚动条、闪屏示波器逻辑分析仪渲染与合成撕裂、帧率不稳、格式不对kmscube、weston、igt-gpu-toolsglmark2、api trace工具背光电源黑屏、亮度不可调、上电时序错/sys/class/backlight、示波器万用表这张表不是标准答案但至少能让你有一个判断的起点。下面我按软件、硬件、帧缓冲与颜色三大块把最核心的工具和用法展开讲。2. 软件工具链modetest、kmscube和内核日志的正确用法软件层工具是显示驱动调试中使用频率最高的。它们的价值在于不用接任何硬件就能看到驱动内部的状态也最容易定位到“状态设置”和“代码路径”上的问题。2.1 modetest先把当前状态一条条列出来modetest是libdrm自带的命令行工具几乎每个Linux显示驱动开发者的第一个正式工具就是它。它做两件事枚举DRM设备的资源列出当前所有connector、encoder、CRTC、plane的信息然后可以手动让某个connector输出指定模式做测试。最常用的几条命令modetest -M card0 -c modetest -M card0 -p modetest -M card0 -c -p-c是connector-p是plane。两个参数一起用基本就是一张“当前显示链路全貌图”。输出里会看到类似这样的内容Connectors: id encoder status type size (mm) modes 6 5 connected HDMI-A-1 520x290 1920x108060 ... Planes: id CRTC FB CRTC x,y x,y gamma size 31 6 0 0,0 0,0 256在点屏bring-up阶段这个输出的意义很大connector status到底是connected、disconnected还是unknown。如果屏幕明明接着status却是unknown说明HPD或者I2C/DDC多半有问题这时候根本轮不到设置mode。如果状态正常可以用modetest强制输出一个模式。比如modetest -M card0 -s 6:1920x108060 -a意思是把connector 6设置成1920x108060-a走atomic接口。这样不需要重启图形栈就能手动触发一次完整的显示状态切换。如果在常规启动过程中黑屏但手动设置模式屏幕能亮那说明应用层或者display manager初始化路径有问题而不是面板本身不工作。modetest的局限性也很明显它只做状态切换不验证正在渲染的内容是否正确。所以还需要配合其他工具做内容级验证。2.2 kmscube与weston用真实渲染验证原子提交kmscube是一个直接在DRM/KMS上运行的OpenGL示例程序用GStreamer渲染一个旋转的彩色cube输出到屏幕。它不依赖X11、不依赖Wayland直接用DRM master上屏环境非常干净。kmscube --mode1920x108060只要kmscube能正常显示旋转cube说明从GPU渲染、framebuffer、plane合成到扫描输出的主链路是通的。如果cube出现大块花屏我会先检查framebuffer的format和stride再检查显示控制器读显存时用的地址是否和实际内容一致如果cube出现轻微抖动或者像隔行扫描我需要检查实际的PCLK和porch参数。weston则是另一个参考实现。它是Wayland compositor的参考代码里面带了一堆dmabuf和KMS属性的测试用例比如用weston-simple-dmabuf-egl测试dma-buf通路。很多高级属性比如rotation、scaling、多个plane同时使能modetest不太好模拟但kmscube和weston场景能覆盖到。实际调试中如果kmscube正常而weston一跑就花屏问题往往集中在多个plane的合成路径或者属性提交顺序上。2.3 dmesg、dynamic_debug和ftrace让内核“开口说话”显示驱动的很多疑难bug并不直接crash而是进入某条异常路径。比如颜色不对或者睡眠唤醒后显示不恢复。这时候驱动内部日志是唯一线索。打开DRM的基础日志用echo 0x1f /sys/module/drm/parameters/debug dmesg -w0x1f对应DRM_UT_CORE、DRIVER、KMS、PRIME、ATOMIC这五类日志能看到打开/关闭framebuffer、设置mode、atomic commit等操作。如果排查时发现某个atomic commit卡住日志里能看到它停在哪里是等待fence还是等待某个mutex。如果系统日志还不够细用dynamic_debug把指定文件的函数调用打印出来echo file drivers/gpu/drm/drm_atomic.c p /sys/kernel/debug/dynamic_debug/control不过更高效的办法是用ftrace抓函数调用栈。比如我怀疑atomic提交后fence等不到信号会这样抓trace-cmd record -e drm:drm_atomic_commit_fence_wait -e drm:drm_vblank_event trace-cmd report把trace时间点和屏幕现象对齐通常能看出个大概是vblank事件根本没来还是某个fence回调没有被触发。这种问题如果不用ftrace光靠读源码会非常痛苦。软件工具能覆盖状态层和属性层的大多数问题但是到了“像素时钟不对”“数据线某一条短路”这类硬件问题它们只能缩范围最后还是要靠仪器。3. 示波器、逻辑分析仪与EDID工具硬件问题的裁判在显示驱动调试里硬件工具往往承担“最后裁判”的角色。因为很多显示问题伪装得特别好看起来像驱动代码的问题实际上就是某个信号没达到面板的电气要求。3.1 示波器像素时钟、同步信号和极性判断示波器在显示驱动调试中的地位有点像手术台上的心电监护仪。你以为你在软件里设了一个1920x108060的timing实际上屏幕收到的可能不是那个时钟。这种情况我在不同平台上遇到过好几次。比如某款MIPI DSI屏上软件配置的porch参数完全按照datasheet来屏幕却总有一条从下往上滚动的色带。用示波器一量发现PCLK比配置值大了0.3%。滚动周期刚好和帧周期的几十倍对上了问题出在PLL配置的粒度不够怎么改面板参数都没用。需要重点测的信号包括PCLK或者bit clock的频率和抖动HSYNC和VSYNC的周期、脉宽和极性DE有效窗口的位置和宽度MIPI DSI的时钟lane和数据lane的差信号eDP/HDMI的辅助通道AUX/DDC信号。测量时建议先把探头挂在靠近显示控制器的输出端确认源端信号是否正确再去测面板端的插座。很多工程师上来就在面板端的软排线上夹一堆探头结果测到的全是接触不良的噪声反而浪费大量时间。3.2 逻辑分析仪与i2c-toolsDDC和EDID出了问题如果HDMI、DP这类接口出现“屏没接上/识别不到”第一步通常是检查DDC通道能否读到EDID。Linux下最简单的办法是用i2c工具。i2cdetect -l i2cdetect -y bus在对应bus上看看0x50地址是否出现应答设备。EDID的I2C地址是0x50如果没有任何应答说明DDC通道物理上就有问题要么是连接线虚焊要么是电平不对。如果读到了EDID再配合edid-decode看内容cat /sys/class/drm/card0-HDMI-A-1/edid | edid-decodeedid-decode会告诉你这块屏支持哪些分辨率、色深、色彩空间以及是否启用了HDR。它还可以帮你发现EDID校验错误比如checksum错误、timing descriptor异常这些都会导致连接器状态异常。我处理过一台设备的hdmi输出时好时坏最后发现是EDID里有一行extended timings写错了系统设置分辨率时反复失败。逻辑分析仪更多用在抓DDC时序和I2C寄存器读写。尤其当你怀疑驱动读写EDID的顺序有问题时逻辑分析仪可以完整抓到主机到底往0x50写了几次起始信号有没有真的读完128字节。低成本的分析仪只要支持逻辑分析协议就够用一般不依赖高端型号。3.3 PWM背光与电源时序黑屏不一定都是驱动的事有一类“显示驱动”问题经常被误判屏幕不亮但拿万用表量芯片供电又是好的。这时你需要检查背光PWM和panel的上电时序。Linux下背光的简易调试cat /sys/class/backlight/*/brightness echo 64 /sys/class/backlight/*/brightness如果亮度值写不进去检查backlight驱动注册是否成功如果写进去了但亮度没有变化用示波器测背光PWM输出脚确认PWM频率和占空比是否变化。常见一个板子的问题显示数据路径完全正常但是panel reset脚和backlight enable脚的时序间隔只有几毫秒正好卡在面板要求的边界值上。屏有点亮但偶尔点不亮或者在温度变化后点不亮。这种问题软件日志永远不会报错只有拿示波器同时量几个GPIO看时序窗口才能确定是硬件时序不满足。硬件仪器虽然“土”但往往能解决最麻烦的玄学问题。4. 颜色、格式和撕裂用测试图案把问题逼到墙角当一个屏终于亮了真正的显示驱动调试才刚刚开始。颜色不对、格式不对、撕裂、闪烁这些都需要一套针对性方法。4.1 纯色测试图案一眼看出RGB顺序和色深在判断色彩问题时我最喜欢用一张纯红色图片。纯红如果显示成蓝色大概率是RGB byte order反了如果显示成接近黑色的暗红大概率是bit depth或者颜色格式处理有问题如果显示成绿色或品红可能是planes之间的合成顺序或者alpha通道没处理好。实际操作中可以用fbi或者fbv这类小工具把一张纯色PPM直接刷到framebuffer上。fbi -d /dev/fb0 red.ppm如果没有fbi用dd把裸的RGB数据写到/dev/fb0也能测但要注意分辨率、位深和行对齐。用纯色图案测试的关键在于依次测试红、绿、蓝、白、黑五种颜色并且每次只换一张图不要同时改代码、改参数。通过“哪一色谱变了”可以快速缩小范围。比如红色变暗、蓝色变亮这种变化往往是format转换矩阵没配或者CSC系数不对而不是接线位序问题。4.2 从modetest -p到panel节点找到格式不一致的位置很多工程师拿到一个新屏第一件事是改设备树但忘记确认屏的接口格式。比如RK、Allwinner这类平台的MIPI DSI屏需要在panel节点里配置bus-format是RGB888、RGB666还是BGR888。如果和实际屏的接线不一致画面必然偏色。一个很典型的排查路径是用modetest -p查看当前plane支持的DRM format列表比如XRGB8888、XBGR8888、NV12等再用cat /sys/kernel/debug/dri/0/state看当前framebuffer实际使用的format和size最后检查设备树panel节点的bus-format字段确认和当前显示的format是否匹配。有一次一块RGB接口的屏幕显示纯红色时成了蓝色但花屏并不明显。我折腾了半天的gamma和CTM最后才发现是DTS里写错了bus-format把MEDIA_BUS_FMT_SRGB8888写成了MEDIA_BUS_FMT_SBGR8888导致输出端的BGR顺序反了。这个bug用等量替换代码怎么都看不出来换一张纯蓝色图片反而一下子暴露了。4.3 撕裂与同步沿着vblank和fence排查撕裂是显示驱动调试的经典问题现象是画面像被横向撕开上一帧和下一帧各占一半。产生原因是显示控制器正在扫描而应用改写了framebuffer内容或者多个plane同时在更新。排查思路一般是确认vblank事件是否稳定产生用dmesg看是否有vblank wait timed out之类的信息检查用户空间应用是否真正等待了vblank/fence还是在忙循环里直接提交省了同步检查atomic commit是否用了DRM_MODE_ATOMIC_NONBLOCK以及有没有依赖fence完成后再提交。调试工具上可以用igt-gpu-tools里的kms系列测试比较方便地模拟撕裂场景。比如kms_vsync、kms_plane这些测试项会不断切换framebuffer并且统计vblank时间。如果你在测试中能稳定复现撕裂再打开ftrace追踪fence回调往往能定位到是某个callback唤醒晚了还是display controller的vertical blank期没有正确对齐。颜色、格式、撕裂这三大类问题都有一个共同规律必须把现象“标准化”。用纯色图案或者固定测试用例让它稳定重现可比调一千行驱动代码高效得多。5. 两个典型场景从现象到定位的完整思路工具聊得再多最终还是得落到具体排查过程。这里分享两个我实际处理过的场景重点看思路和工具顺序。5.1 花屏看起来没规律其实有规律场景一块MIPI DSI接口的彩屏系统起来后桌面字迹能看到但画面整体像隔了一层“斑点噪声”而且颜色越深的地方越明显。大家第一反应是屏坏了或者排线接触不良。我的排查顺序是先看软件状态。dmesg | grep drm没有任何errormodetest显示connector connected说明驱动已经完成状态注册。这一步排除了“根本没点亮”的问题。用kmscube跑一个旋转cube发现同样的斑点说明问题不在应用层而在底层显示链路。看plane的format。modetest -p里显示当前plane输出是XRGB8888framebuffer内容也确实是ARGB8888这一步没看出异常。最后查panel节点的bus-format发现配置的是RGB888但实际屏的接口是BGR888。修改bus-format后斑点立刻消失。这个案例想说明的是花屏不是“没有规律”而是你没有把“格式”这个维度纳入检查。软件状态看起来正常实际上是输出端和面板端对同样的数据字节解释不同。用纯红、纯蓝的测试图来验证会比看桌面桌面更加清晰。5.2 无EDID导致黑屏按I2C、HPD、状态依次排除另一个高频场景一台HDMI接显示器系统启动时屏幕没有画面。打开dmesg大概率会看到类似connector status: disconnected或者no EDID的信息。我的排查顺序是先查物理链路。用示波器或万用表确认HDMI插头处的5V、HPD引脚电平正常。HPD脚如果是低电平系统不可能认为有屏接入。用i2cdetect扫DDC bus看0x50是否有应答。没有应答继续查DDC的I2C上拉、走线、连接器座子有应答继续查EDID内容。读EDID文件用edid-decode分析。如果文件大小不为0但decode报checksum错误可能需要用逻辑分析仪抓DDC时序确认有没有漏字节。用modetest -c重新看connector状态。如果HPD和EDID都好了状态会从unknown恢复成connected再设置mode一般就能点亮。在这个场景里工具的次序特别重要。如果你先改驱动代码可能绕了一大圈最后发现是连接器座子虚焊。我会坚持一个原则先物理后逻辑先状态后代码能读到EDID之前不碰源码。6. 调试环境和两个被我反复用到的习惯工具不是一堆命令的堆砌真正有价值的是“怎么用好”和“怎么配合起来”。最后聊一下调试环境的搭建以及我觉得值得所有人坚持的两个习惯。6.1 没有板子时也能验证KMS逻辑的虚拟环境做显示驱动不仅需要硬件调试也需要在没有硬件的情况下快速验证KMS逻辑。QEMU配合virtio-gpu是一个很好的选择。qemu-system-x86_64 -m 4G -enable-kvm \ -kernel vmlinuz -initrd initrd.img \ -append root/dev/ram0 drm.debug0x1f \ -device virtio-gpu-pciguest起来以后可以查看/sys/kernel/debug/dri/0/state确认atomic state是否正确提交。我自己经常先在QEMU里跑一遍kmscube验证新写的属性流程不会导致回退再拿到真实板子上测试。虽然虚拟环境无法验证像素时钟、显示信号但格式设置、属性提交、buffer管理这些KMS核心逻辑是可以覆盖的这能省掉大量在板子上反复改内核的时间。6.2 内核调试开关把日志密度控制在自己手里调试显示驱动时如果不做任何设置内核日志可能只会显示一堆“too big”“Unsupported”这样的半截信息。大多数DRM驱动都支持在启动参数里打开debug开关drm.debug0x1f如果已经启动了也可以通过sysfs动态打开echo 0x1f /sys/module/drm/parameters/debug0x1f是五个DRM debug类别的总和。抓atomic问题可以单独开0x12KMSATOMIC。不过要注意全量日志的量非常大建议配合dmesg -w以及重定向文件不然关键信息会被刷下去。另外dynamic_debug是一个更精细的开关。我常用的是echo file drivers/gpu/drm/* p /sys/kernel/debug/dynamic_debug/control这样只开启DRM目录下的调试打印不影响系统其他模块。日志密度不要太低也不要太高否则和没开差不多。6.3 记录、拍照、回放比仪器更值钱的好习惯最后一个是习惯层面的经验。我做显示驱动调试时会坚持一个很简单的动作每次修改任何参数都写下改了什么为什么改屏幕现象拍一张照片并把当时的dmesg输出存成文件。文件名按照下面的格式20231115_panel_a_1920x1080_bus-format_before.jpg 20231115_panel_a_1920x1080_bus-format_after.jpg听起来有点笨但在接新项目、或者半年后回看同一个问题时这些记录的价值完全超过想象。显示驱动问题往往和硬件批次、温度、时序都相关如果没有记录很容易把同一个坑反复踩上三四回。而在和硬件同事协作时一张带时间戳的波形照片比一百句“刚才好像闪了一下”更有说服力。我自己的体会是调试工具最终能发挥多大作用不取决于工具贵不贵而取决于你有没有把它放到正确的排查链路里并留下足够多的现场证据。把软件状态、硬件信号和帧缓冲格式这三条线齐头并进再复杂的显示驱动问题也有解开的路径。