资讯动态

Android显示驱动开发入门:从DRM/KMS到点屏排错

发布时间:2026/10/13 5:51:05 来源:尧图企业网站定制
写Android驱动的人大概率都经历过这样的至暗时刻改完设备树、把厂商给的镜像烧进去串口日志里内核已经欢快地跑起来了结果屏幕一片漆黑或者更惨一点屏幕亮了但花屏花得像万花筒只有上半截能勉强辨认出boot logo的形状。这真不是个例。显示链路太长了——从应用往buffer里画画、到SurfaceFlinger合成帧、到内核里一堆CRTC/Encoder/Connector对象来回倒腾、最后通过MIPI DSI或DP接口把像素一个个刷到屏幕上任何一环出问题终端呈现的就是黑屏、花屏、闪屏三件套。所以我一直觉得Android显示驱动这个方向难的不是某个知识点而是你脑子里得先有一张完整的链路线路图否则很容易掉进修好一个bug又冒出来三个bug的泥潭。这篇是驱动学习系列的第八篇前面几篇我们把GPIO、中断、I2C、内核模块这些地基打了一遍今天终于可以聊正题了Android显示驱动从0到1到底该怎么学。我会用自己实际摸过的路径把整条链路、内核态的主战场、点屏实操、调试方法、以及Android系统层和内核的衔接关系全部串一遍。无论是刚接触内核驱动的在校生还是半路转驱动开发的工程师这篇文章都适用——我不打算给你堆术语而是把每一步为什么要这么做讲透。1. 先看懂整条显示链路你写的代码只是其中一个环节1.1 从App到像素刷上屏幕数据到底经过了几层很多人学显示驱动第一反应就是冲到内核源码里翻DRM框架这其实顺序反了。你得先知道一块屏幕上的画面是怎么从无到有出现的否则你在内核里看到的每一个函数都像孤岛找不到它存在的意义。完整链路大概是这样的App通过SurfaceControl申请一块图形缓冲区然后用Skia或者OpenGL ES往这块buffer里画内容画完之后这块buffer会被交给BufferQueue由SurfaceFlinger统一管理。SurfaceFlinger是Android图形系统的总管它手里攒着各个App的layer然后根据visibility、透明度、遮挡关系等条件决定哪些layer要合成、用什么方式合成。合成这块活一部分由GPU干一部分会交给硬件合成器HWC去干。HWC拿到layer之后通过libdrm的用户态接口——本质上是ioctl——把我要把这些buffer显示出来的命令交给内核。内核里的DRM/KMS子系统的活就是从这一刻开始的。它会看你提交过来的fbframebuffer、显示模式、图层数量然后把它们跟真实的显示控制器硬件对应起来配置好CRTC、Encoder、Connector、Plane这些对象最终通过MIPI DSI、DP或eDP这类总线接口把像素时钟、lane数据、同步信号送给显示屏模组。显示屏模组内部还有自己的TCON时序控制器它收到数据流以后按照面板的扫描规则把每个像素写到对应的位置上屏幕就亮了。所以你看显示驱动工程师在内核里干的活只是整个链条的最末梢。但如果这一末梢不工作上面所有层的努力全部白费。这也是为什么显示驱动排错特别容易让人绝望——问题可能出在应用层、合成层、内核层、甚至屏端模组的任何一处而你手里通常只握着内核日志和示波器。1.2 搞清楚自己项目的落点内核态与用户态的边界学习路线要想不跑偏第二个要弄清楚的事情是你的代码最终会在哪一层常住。如果做的是传统的内核显示驱动开发你的主战场在内核态。你需要处理DRM框架、面板驱动的初始化序列、DSI控制器的时序、背光控制、以及电源域的上电顺序。这是整个显示系统里最接近硬件、最难调试的部分也是这篇内容的重点。如果做的是系统级集成比如帮一个方案商把某款屏适配到Android系统上那你不仅要碰内核还要碰HWC HAL层和SurfaceFlinger的配置。你得知道哪个layer走硬件plane、哪个layer要GPU合成还要处理dumpsys SurfaceFlinger里面那一大坨状态输出。还有些场景更偏用户态比如调试modetest这样的工具、开发虚拟显示、处理投屏时的Display ID映射——这时候内核DRM框架只能算你手里的资源真正要花时间的是理解BufferQueue和libdrm的封装逻辑。我建议的学习顺序还是从内核态切入先把点屏搞定再往用户态走。原因很实在点亮屏幕是显示驱动的刚需也是最容易获得成就感的标志性节点而且内核态这块的调试手段相对固定串口日志、内核trace、DRM debug开关三板斧学会了以后通用的。1.3 显示驱动在整个系统里的位置不是孤立的设备驱动再啰嗦一句定位问题。很多人以为显示驱动就是LCD驱动申请一个platform_driver、注册一个fb_info就完事了。这在老式framebuffer时代确实如此但现在的Android平台早就全面转向DRM/KMS了。framebuffer只是DRM对外提供的一个兼容接口真正的逻辑在DRM里面。这意味着你要用面向对象的眼光看待这套系统drm_device是整个显示设备的抽象drm_crtc是扫描控制器drm_encoder是信号编码器drm_connector是物理接口drm_plane是图层平面。这四个对象之间的关系需要刻在脑子里。从点到面先把它们的继承、组合、回调关系画明白你再去看任何一块vendor的显示驱动都会觉得眼熟——不管是高通的还是某个通用ARM平台的BSP本质都是在这套框架上做增删改查。2. 内核态的主战场DRM/KMS框架的对象模型和提交机制2.1 四个核心对象用投影仪的比喻一次讲清DRM/KMS框架里有四个高频对象把它们的职责搞清楚你再看驱动的probe函数和atomic回调就不会懵了drm_connector物理输出接口相当于投影仪屁股后面的HDMI/VGA口。它负责探测有没有屏插在上面上报EDID或者panel的参数告诉系统这块屏支持哪些分辨率。drm_encoder信号编码器。它把CRTC输出的图像信号转换成交接口能传输的格式。在LCD场景下encoder往往比较透明但它依然存在——比如RGB转DSI、DSI转eDP这种转换就是encoder的逻辑。drm_crtc扫描控制器。它才是真正指挥画面扫描的导演什么时候开始扫一帧行同步、帧同步信号怎么打扫描的区域多大都由它决定。你平时调的那些timing参数最终都是配置到CRTC上。drm_plane图层平面。它决定一块buffer怎么被显示出来放在屏幕哪个位置、要不要缩放、用不用透明度混合。现在的智能手机基本都有一个主plane、若干overlay plane对应SurfaceFlinger那边的一个个layer。如果拿投影仪讲课来类比CRTC是投影仪主控芯片Encoder是接口转换器Connector是那个插HDMI线的口Plane就是你在电脑上选仅第二屏幕显示时指定的那一路画面源。四个对象各有各的属性但最终要协作输出一帧画面。这四个对象在设备树和驱动代码里通常是一起出现的。一个典型的LCD驱动probe流程是从设备树里拿到panel节点注册一个panel驱动然后在一个平台驱动的bind回调里把CRTC、Encoder、Connector、Plane统统初始化好再通过drm_simple_display_pipe_init或者更细粒度的API把它们捆绑成一条显示管道。2.2 Atomic Commit现代显示驱动的心脏DRM框架从传统legacy模式进化到Atomic模式是显示驱动开发者最该关注的一次架构升级。Atomic的核心思想是把一帧显示状态的所有变更——包括每个plane的buffer地址、显示区域、CRTC的模式、connector的连接状态——打包成一个原子事务整体提交、整体校验、整体生效。为什么需要这个机制你设想一个没有Atomic的糟糕场景用户切分辨率如果先改了CRTC的timing、再改connector的mode这两步之间屏幕输出了一个中间态——可能就是闪一下花屏或者直接黑一下。而Atomic commit会先把所有要改的状态收集到drm_atomic_state里经过所有对象的atomic_check回调校验通过之后再统一交给atomic_commit去执行。一旦中间任何一个check失败整个事务回滚屏幕纹丝不动体验上就是要么没变要么一次变到位。在Android显示场景里这个机制尤其重要。SurfaceFlinger每次合成完毕通过HWC提交给内核的其实就是一个多layer的原子提交——每个硬件plane对应一个layer它们要同时切换新内容。如果做不到原子性屏幕上就会出现撕裂画面上一半是新帧下一半还是旧帧。实操上你主要打交道的是一组回调族atomic_check负责各种约束校验atomic_update负责把状态写到硬件寄存器atomic_enable/atomic_disable负责显示管道的启停commit的末尾还要处理vblank事件的触发。刚开始接触时不要试图把一个vendor驱动所有的回调都读懂先抓住一条最小路径modetest设置一个mode然后看它走了哪几个函数、改了哪些寄存器。2.3 面板驱动与桥接芯片你在bsp里看得最多的两种角色真正点亮一块LCD屏通常还需要一个panel驱动它属于DRM框架里相对独立的角色。标准面板驱动的生命周期大概是这样在probe里解析设备树参数获取GPIO、供电、初始化序列在get_modes里向connector上报这块屏支持的模式在prepare里拉GPIO、开电源、初始化面板寄存器在enable里让面板真正开始接收数据disable和unprepare是相反的顺序关掉面板。这里的坑点在于面板的enable/prepare不是你想的那么简单。MIPI DSI面板分为command mode和video mode两种。Command mode面板自带GRAM主机把整帧数据写进面板内存然后面板自己负责刷新适合做低功耗的OLED和部分LCDVideo mode面板则要求主机持续不断地推流像流水一样把像素时钟、行场信号喂给面板TCON。这两种模式对驱动代码和初始化序列的要求完全不同选错模式你就等着看屏幕亮但持续撕裂或者屏幕整体延迟半秒的诡异现象。还有一类是bridge芯片比如RGB转LVDS、DSI转eDP这种。它们在DRM里表现为drm_bridge挂在encoder和connector之间。调试这类设备时除了关注面板本身的时序还得盯住bridge芯片的配置——PLL频率、lane数、通道映射经常会是花屏的根源。2.4 Vblank与Fence为什么Android的每一帧都在等中断再补一个重要概念vblank。CRTC每扫完一帧、进入消隐区时会触发一个vblank中断。内核的DRM框架用这个中断来通知用户态这一帧已经稳定显示完了可以准备下一帧了。Android的SurfaceFlinger把整个合成节奏都押在vsync上而vsync的源头就是显示驱动的vblank。如果你的驱动没有正确调用drm_crtc_vblank_off/on、没有上报vblank事件Android系统会出现极其诡异的表现界面操作卡顿、帧率掉一半但dumpsys看着又一切正常。与vblank紧密相关的是fence机制。内核通过dma_fence告诉用户态这块buffer我已经扫完了你放开用吧反过来GPU渲染完的buffer要交给display扫描也需要走fence同步。显示驱动里处理fence的代码量不大但逻辑一旦错就是画面卡死但系统没死的经典症状。学习时可以专门找一个刷帧的例子跟着dma_fence的生命周期走一遍你会对这套同步机制有透彻的理解。3. 从点屏到点亮一次完整的上电时序和初始化序列排错过程3.1 先怀疑时序再怀疑代码最后才怀疑硬件点亮屏幕这件事听着很酷实际做起来极其枯燥而且90%的时间不是花在写代码上而是花在时序对齐上。我最开始点屏的时候收到一套未知来源的LCD模组和一堆散乱文档第一反应就是猛翻驱动代码怀疑某个寄存器配错了。折腾两天后来拿示波器一量发现是panel的reset引脚释放得太早上电时序根本不对代码再怎么写都白搭。这条经验值得刻在工位上点屏排错永远遵循先硬件时序、再软件配置、再验证硬件本身的顺序。硬件时序指的是panel模组规格书里那张上电时序图。它往往规定了VDD电源稳定后要等多少毫秒、RESX低电平要维持多久、释放RESX后要隔多久才能发起初始化命令、初始化序列发完后又要等多久才能开启背光。这些时间参数非常死板差一点就可能导致面板初始化失败或者画面异常。修驱动前先把量产的电源时序测试波形和规格书逐项对一遍这是点屏的第一关。3.2 初始化序列那些一长串的MIPI命令怎么逐条验证面板初始化序列通常是厂家给的一堆MIPI写命令格式大概长这样写0xF0、0x5A、0x5A然后写0xB0、0x01再写0xC1、0x02……这些命令用来自动调节面板内部TCON的一堆寄存器。很多人拿到序列直接在驱动代码里逐条拷贝进去但经常踩两个坑。第一个坑是命令的长度和DSI包的格式。MIPI DSI命令在command mode下发时要区分短包和长包。短包只有几个字节的参数长包需要按块传输并且带校验。如果你的驱动把它们一律当短包发长包内容会被截断面板收到的初始化序列就是残缺的表现出来的症状可能是低亮度下有细纹、颜色偏色、或者局部显示乱码。第二个坑是初始化时机。序列必须在合适的DSI控制状态下发——通常是HS模式下、或者面板刚从sleep out唤醒的特定窗口期。如果你在错误的控制器状态下发命令命令会被面板忽略而且没有任何日志反馈。这时候你只能靠逻辑分析仪去抓MIPI总线数据对比看发给面板的命令是否和规格书一致。实践中的稳妥做法是先用厂商提供的工具在量产板上验证序列能点亮再把这个序列翻译到内核驱动里。如果你手头没有参考板唯一能信的只有规格书。遇到花屏优先对照输出格式参数——不是命令内容错而是像素格式排错了比如面板是18bit RGB你却配置成了24bit或者lane数配少了数据吞吐不够画面自然支离破碎。3.3 屏亮一半和闪一下黑掉的典型排查链路屏亮一半是我见过最多的疑难杂症也是最容易误导人的。症状是屏幕上半部分正常下面一半要么黑、要么花、要么颜色断层。这通常不是panel的问题而是输出timing和面板实际物理分辨率对不上导致的。你从EDID或者设备树里拿到的分辨率和TCON内部配置的行场偏移不一致扫描到中途数据就对不齐了。排查这种问题链路可以这样走用dmesg确认DRM驱动的mode设置是否成功看打印出来的hdisplay/vdisplay和驱动里实际配置的timing。用modetest查当前connector上显示的分辨率、刷新率和你在代码里设置的值是不是一回事。拿着示波器去量DSI clock lane的bit rate换算成像素频率对比理论值是否偏差过大。查一下porch参数——front porch、back porch、sync width——这些参数决定了行场扫描的消隐区。消隐区配置错误就会表现出画面偏移、半截花屏。闪一下黑掉则多半是另一类问题屏能点亮一瞬间接下来面板进入了异常保护。这种情形先要区分有没有背光——如果背光还在但不显示内容通常是DSI数据链路断了面板触发了underflow保护如果连背光一起灭那多半是供电或者背光驱动那边主动off掉了。对着日志看panel驱动的disable/unprepare有没有被异常调用如果没有再去查电源管理那一侧有没有过流保护这类问题的排查链路基本固定怕的是东试一下西试一下把现场搞乱。3.4 点屏必备工具清单点屏排错工具比代码更重要。我的常备清单如下串口板看内核日志、示波器至少四通道量时序和clk、逻辑分析仪复杂场景抓MIPI总线数据用、万用表量供电和GPIO电平、以及一台装了厂商量产工具的Windows或虚拟机用来交叉验证初始化序列。如果条件受限最低限度要有一根能看内核日志的串口线否则全凭猜效率极低。4. 环境搭建与交叉编译这台机器不动起来后面全是空中楼阁4.1 选对内核源头别一上来就困死在厂商BSP里学显示驱动最忌讳的事情是拿到一块厂商的开发板一头扎进全套BSP源码里。那个代码量动辄几十G而且大部分是板级适配和驱动杂烩你根本分不清哪些是通用框架、哪些是vendor私有逻辑。我更推荐的做法是分两步走先在通用ARM板或者模拟器上把Linux内核主线里的DRM/KMS机制跑熟然后再切换到真实Android BSP里看厂商怎么在这个框架上做定制。前面若能在通用环境里跑通modetest、对着内核文档把DRM四个对象的生命周期弄明白你回来看BSP的时候视野会完全不一样。如果你有一条开发板能跑主线内核那就再理想不过。主线内核的drivers/gpu/drm下面有不少简单的显示驱动示例哪怕只是虚拟显示驱动vc4或者简单的panel-simple读源码的学习效率都比直接啃vendor的几千行代码高得多。4.2 交叉编译链、设备树和最小可启动环境环境搭建本身并不算难但细节决定体验。交叉编译工具链看你的ARM平台而定主线内核一般用通用的aarch64工具链就能编过。前提是配置文件选对需不需要开DRM、开不开DRM_DEBUG相关的选项、framebuffer兼容层要不要——这些在make menuconfig里都找得到建议head第一遍时把调试相关的选项全开。设备树是显示驱动初始化的源头。你需要在设备树里描述一块panel的参数compatible、reg、reset-gpios、power-supply、dsi-lanes、refresh-rate、以及各porch时序。这一步最烦因为一点语法错误或者GPIO号写错驱动就直接probe失败日志又不友好。建议养成一个习惯每次改完设备树先解包编译好的dtb再用fdtdump验证一遍确保内容真的写进去了。最小可启动环境的意思是你不需要为了学显示驱动去编一整套AOSP。用内核适当裁剪的initramfs能把你写的DRM驱动加载起来然后用modetest去点亮屏幕就够了。等到你想跑Android图形系统、想看SurfaceFlinger怎么和你内核里的plane联动再上完整系统也不迟。4.3 调试三板斧日志、Trace、DRM Debug开关显示驱动的调试三板斧分别是dmesg日志、内核trace、以及DRM框架自带的debug开关。dmesg最基础但要看懂门道。驱动probe的过程、atomic_check失败的原因、panel驱动的错误打印都在这。开发初期建议在驱动的关键路径里主动加dev_dbg/dev_err不要省打印因为显示驱动的时序问题很多时候日志就是唯一的线索。trace用来查时序问题尤其好用。比如你想知道一次atomic commit从ioctl进入到硬件寄存器生效花了多久一个vblank到下一次vblank的间隔稳不稳定用ftrace的事件跟踪比手动打点精确得多。DRM debug内核里drivers/gpu/drm有很成熟的动态调试框架。开启动态调试后可以单独打开某个模块的DRM_DEBUG_ATOMIC、DRM_DEBUG_KMS等输出观察atomic状态迁移、connector模式设置、plane属性变更的详细过程。这是除了串口以外你最重要的眼睛。拿一次经典的花屏问题举例我开着DRM debug看到atomic commit成功但硬件plane的src区域配置和显示尺寸对不上瞬间定位到用户态计算缩放参数时出了错省了一大半排查时间。5. 站在Android系统层回看内核HWC、SurfaceFlinger如何决定你该写什么驱动5.1 你看到的活跃plane全是SurfaceFlinger的合成决策真正把一套显示驱动放进Android系统里跑你会发现内核里plane的workload突然变得很不规律一会儿只有一个主plane在工作一会儿多个overlay plane同时开启。这个现象的背后就是SurfaceFlinger的合成策略。SurfaceFlinger会综合每个layer的可见性、透明度、旋转角度等因素决定哪些layer可以交给我们内核的硬件plane直接显示哪些必须由GPU先合成成一张大图。这个决策最终通过HWC HAL传递给内核。HWC 2.x引入的是一个更灵活的模型硬件合成器实际上接管了一部分合成工作能直接使用硬件的图层混合能力就不用GPU再画一遍。作为显示驱动开发者你要关注的是为什么一些layer没有被发到硬件plane上是驱动申报的能力不对还是硬件本身在那里卡住了运行dumpsys SurfaceFlinger时你会看到每一层的composition type如果全部都是ClientGPU合成而你硬件明明有多个可靠的overlay plane那就要检查是不是plane的格式支持列表申报得太保守了。这个在BSP适配里非常常见——厂商原厂的plane能力表往往只覆盖常用格式多一个hdr格式或者10bit格式就能让所有layer被迫走GPU合成性能直线下降。5.2 内核里的fence就是Android图形栈的节拍器前面提到的dma_fence在Android图形栈里的重要性比你想象得高。我在内核侧见过不少诡异bug画面迟早会显示但迟到一帧两帧或者突然卡住几秒然后恢复。最后查下来全是fence信号时机不对。举个例子SurfaceFlinger提交一个layer buffer给HWC内核通过dma_buf映射这个buffer作为drm_framebuffer。但display扫描完这一块buffer之后必须释放一个fence告诉SurfaceFlinger这个buffer你用完了可以还给App继续画了。如果这个fence发早了App会在buffer还在屏幕上显示的时候往里面写数据导致画面撕裂如果发晚了App会误以为buffer一直占用帧率被硬生生拉低。所以调试Android显示驱动时一定要会看fence。具体到内核代码就是drm_gem_fence、dma_resv这些机制到用户态就是HWC接口里的releaseFence和acquireFence。学习顺序上我建议先在内核模块里写一个简单的dma_fence实现体会创建fence、signal fence、等待fence的三段式流程再去看Android那边怎么消费它。5.3 BufferQueue与DMA-BUF平面切换背后的buffer流转再往深里走一层Android的图形缓冲机制是一套复杂的buffer流转系统。App的画面画在一个由BufferQueue管理的缓冲区上这缓冲区对应的底层存储可能是ion或者dmabuf堆。当SurfaceFlinger决定要在屏幕上显示它时这个buffer要被导入到DRM驱动里变成一个DRM framebuffer才能被CRTC扫描。这个导入过程对应的linux机制是DRM Prime用户态通过dma-buf fd调用drm_prime_fd_to_handle驱动侧分配一个GEM对象把dma-buf的sg_table映射到显示设备能访问的地址上。跨设备共享时坑很多如果GPU和display IP之间没有做好cache一致性处理你会看到画面出现老数据残影——因为display扫描的还是GPU写之前缓存的旧内容。所以学习路线里建议花时间专门搞明白一次正常刷帧过程中buffer经历了几处cache操作、何时flush何时invalidate。5.4 帧同步Android为什么死死咬住vsync不放Android系统从架构上就围绕着vsync来编排整个图形流水线App的绘制、SurfaceFlinger的合成、HWC的提交全部对齐到vsync信号上。这个vsync的源头可以是硬件display的vblank中断也可以由用户态的disp sync模块模拟。对我们写内核驱动的人来说意味着CRTC中断处理程序必须准确上报vblank。如果vblank来早了合成器会提前开始下一帧的合成导致真正显示出来的画面和用户可见的刷新差半拍体感上是界面操作有一点莫名的延迟如果来晚了帧率会掉速。排查这类问题时一个有效的工具是检查dumpsys SurfaceFlinger里的SF stats延迟数据以及对比内核vblank中断的实际时间戳。这里顺便提一句做显示驱动千万不要只盯着把屏点亮就满足。帧率稳定性、vsync对齐、fence时序、功耗优化这些才是真正区分初级和高级驱动开发者的分水岭。5.5 高级话题预告虚拟显示、多屏DP、Display ID现代Android设备早就不止一块物理屏幕了。投屏、副屏、截图模拟显示这些场景都依赖虚拟显示和更多的输出接口。内核DRM框架对多屏的支持其实很成熟——多个connector、多个crtcatomic状态里可以同时管理多路输出。学习路线走到一定阶段可以试着在设备树里挂两个panel、注册两条显示管道然后在用户态切换主副屏这会逼你把CRTC和connector的关系再梳理一遍。Display ID则是另一个容易被忽略的领域。多屏场景下Android需要知道哪个屏是什么、分辨率是多少、序号怎么排。这里头涉及DisplayManagerService、HWC的display id分配和内核connector id的对应关系。debug时如果发现投屏画面投到了错误的屏幕十有八九就是display id的映射错位了。6. 从0到1的动手路线图三个阶段、三个验证物6.1 阶段一在通用环境里跑通DRM框架第一阶段的目标是在一个通用开发板或者模拟器上让DRM驱动跑起来modetest能看到完整的connector/mode列表。具体动作包括配置内核编译选项、编译并安装一个含DRM的内核、在用户态装好libdrm、运行modetest。如果手头有真正的显示接口和屏幕那么能看到modetest里扫出的具体mode如果只有无头环境那就用虚拟显示驱动照样能枚举出一堆格式。这一阶段建议死磕这几个问题drm_device是怎么注册进系统里的一个connector的型号和mode从哪来CRTC和encoder怎么和connector绑定modetest切一个mode的过程会调用哪些函数这个阶段的验收物是你能够对着官方文档从零写一个最小化的显示驱动骨架并且在虚拟接口上成功跑出一个模式。6.2 阶段二在真实屏幕上做点屏第二阶段的目标明确就是点亮一块真实的屏幕。拿到panel规格书把GPIO、供电、背光、DSI配置全部弄清楚然后一步步完成初始化序列、mode设置、背光控制。别追求一次成功带着前面讲过的排查方法把屏亮一半闪一下黑掉这些经典问题都亲身体验一遍。这一阶段的验收物是你手上这块屏完全点亮并且用modetest、自写测试程序切分辨率都稳定。此时你才算真正把驱动开发四个字落到了地面上。6.3 阶段三跑Android系统做系统级显示调试第三阶段把完整Android系统刷上设备验证SurfaceFlinger、HWC、内核DRM这一整套链条是否协作正常。你会遇到典型的系统级问题某个游戏场景卡顿、HDR内容偏色、帧率不稳。这时候要会用的工具是dumpsys SurfaceFlinger、dumpsys display、以及内核侧的DRM debug日志。结合前面章节说的fence、vsync、plane能力表去定位并解决一两个实际的性能问题。这个阶段的验收物是你能够用一段文字清楚解释一个App的画面经过哪些环节最终出现在屏幕上其中每一环节涉及哪一个线程、哪一块buffer、哪一次同步。6.4 学习时间规划与资料取舍如果全职投入我的建议是4到6周可以走完三个阶段第一周读DRM内核文档加跑通modetest第二到三周做点屏第四到五周上Android系统排查问题最后一周沉淀总结成自己的文档。如果是业余时间把这个周期拉长到两三个月即可关键是保持连续——显示驱动知识碎断几天就捡不回来了。资料选择上内核自带的Documentation/gpu目录是最该先读的尤其是介绍DRM/KMS架构和atomic mode setting的章节。源码层面推荐从简单的显示驱动开始读——越小越容易理解整个骨架。进阶再啃厂商BSP的时候带着我在通用框架里见过这个的心态就不会被海量代码淹没。6.5 学习路上的几个常见误区最后聊聊坑。第一个误区是只学框架不碰硬件跑通modetest就以为学会了真到点屏时无从下手第二个误区是过度依赖厂商文档不会自己看规格书和时序图厂商文档更新慢、错漏也多第三个误区是忽略用户态只纠结内核函数遇到Android层的合成策略问题就抓瞎。另一个容易忽略的学习误区是不重视写作和记录。显示驱动的调试往往需要交叉验证大量参数时序、寄存器、日志混杂在一起不形成文档过两天自己都看不懂。我自己的习惯是每个bug修完写一段issue笔记把现象、排查链路、根因、验证过程四条线记录清楚这既是给未来的自己留线索也是面试时最有说服力的素材。如果你只能带一句话离开这篇文章我想说显示驱动学习的核心不是背一串DRM函数名而是建立帧的生命周期思维——明白一帧画面临时要在什么时机经过哪条总线、被哪个对象管理、靠什么同步机制保证顺序你就拥有了一张不会迷路的显示系统地图。踩过点屏的坑、查过fence的错、对齐过vsync之后你会发现自己看整个Android图形系统的眼光都不一样了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑