资讯动态

Android显示驱动从0到1:DRM/KMS、MIPI DSI与HWC系统学习路线

发布时间:2026/10/23 7:16:22 来源:尧图企业网站定制
很多人问我Android显示驱动到底该怎么学。网上搜“Android 显示驱动”出来的东西要么是零散的寄存器操作要么是直接甩一份 vendor 代码让你自己看真正能把“从0到1”这条路讲清楚的很少。这篇文章我会按自己这些年做底层驱动和系统移植的实际经验把 Android 显示驱动的完整学习路线拆开讲先看懂显示链路再补内核基础然后依次搞定设备树、显示控制器、MIPI DSI 面板驱动最后打通 HWC 和 SurfaceFlinger。内容会偏向 Linux 内核 DRM/KMS 框架和 Android HAL 层适合刚接触底层显示、想系统入门的人也适合做 Framebuffer 驱动想往新架构转的同学。读完你至少能知道每一阶段该学什么、为什么学、怎么验证自己学懂了。1. 先从整体看懂显示链路Android显示驱动到底在驱动什么1.1 从一条像素到屏幕的点亮过程我见过很多新手一开始就抱着数据手册啃寄存器结果越看越糊涂。原因很简单显示驱动不是“点亮一个灯”而是一条完整的像素生产流水线。你只要把 Android 显示链路想象成一条物流线就很好理解App 界面是货物SurfaceFlinger 是分拣中心HWC 是调度员KMS/DRM 是运输干线最后面板控制器是收货站点。具体到一次屏幕刷新App 通过 HWUI 把 UI 绘制到 BufferQueue 里的 GraphicBufferSurfaceFlinger 把多个图层合成好然后交给 HWCHardware Composer决定哪些图层可以由显示硬件直接合成哪些需要 GPU 合成。合成结果最终会被提交给内核里的 DRM/KMS 驱动驱动把内存中的 Framebuffer 地址配置到显示控制器的图层寄存器经 Video Pipe 或 Command Pipe 把像素数据通过 MIPI DSI、LVDS、eDP 或 HDMI 接口发送到面板面板时序控制器TCON再把数据转换成屏幕上的亮度变化。所以所谓的“显示驱动”其实横跨了 Android 用户空间的 HWC、SurfaceFlinger以及内核空间的 DRM、VOP/Display Controller、DSI/LVDS 控制器、背光驱动和电源管理。你在学习时如果只盯着某一层很容易陷入“寄存器改对了但屏幕就是没反应”的窘境。很多工程师跳来跳去根本原因是脑中缺少这条链路的总览图。1.2 显示驱动开发者日常面对的四个层次从我的经验来看可以把 Android 显示驱动的日常开发工作分成四个层次你可以用它来对照自己的位置第一层面板驱动与背光。负责初始化屏幕模组上电时序、初始化序列、控制背光亮度、处理屏幕的睡眠唤醒。这一层和硬件关系最紧经常要对着原理图量电压、看波形。第二层显示控制器驱动。在 DRM/KMS 框架里就是 CRTC、Encoder、Connector、Plane 的注册与回调实现。这一层决定图像怎么从内存取出来、叠加、缩放最后变成某种接口协议上的数据流。第三层GPU 合成与 Framebuffer。主要涉及 Ion/DMA-BUF 内存管理以及 SurfaceFlinger 向内核提交显示缓冲区的路径。小屏设备常走 GPU 合成效率瓶颈很容易出现在这里。第四层HWC 与系统合成策略。严格来说 HWC 是 HAL 层不算内核驱动但它直接决定“硬件合成还是 GPU 合成”在低端平台上写不好 HWCCPU 占用率和功耗都会很难看。这四个层次不是要你全部精通才动手但必须知道彼此之间的接口长什么样。内核里的 DTS、DRM 驱动和用户空间的 HWC 实现、Gralloc 分配策略、SurfaceFlinger 的合成决策它们之间的信息流是相互咬合的。我给新人的建议是先以“能把一个 1080p 的屏幕从 uboot 到 Android 桌面完整点亮”为目标逐层打通再回头分析哪一层是关键瓶颈。2. 学习路线第一阶段打好内核与Linux图形基础2.1 必修的内核知识清单很多做应用开发的同学想转显示驱动第一个拦路虎就是内核基础不熟。显示驱动本质上是一个 Linux 字符设备驱动加上一堆框架回调所以下面的内核知识不是选修而是必修Linux 字符设备驱动模型file_operations、miscdevice、platform_driver 的 probe 流程。内核内存管理基础物理内存分配、虚拟地址映射、DMA 一致性映射、IOMMU/SMMU 的基本概念。中断与并发显示控制器常用中断来通知帧完成VBlank所以 tasklet、workqueue、spinlock、mutex 这些并发原语必须能看懂。DMA-BUF 与 Dma-Buf HeapAndroid 的 GraphicBuffer 最终就是通过 DMA-BUF 把 fd 传给内核不理解这个就看不懂 framebuffer 的 fd 是怎么来的。设备树DT的基础语法compatible、reg、interrupt、clock、pinctrl、power-supply、backlight 等属性的含义。我建议学习动作不只是看书而是直接拿一块常见的开发板内核源码在drivers/gpu/drm/或drivers/video/fbdev/下找一个简单的驱动从头到尾读一遍。把驱动里注册的 platform_driver、probe 里读取的资源、remove 里释放的资源对照数据手册和 DTS 文件逐一对上号这一步做完你对“驱动”的理解会立刻不一样。这里有一个常见的心理误区总觉得要先读完一整本内核编程的书才能动手。其实没必要显示驱动涉及的内核 API 相对固定你只要在遇到不懂的 API 时去查include/linux/里的头文件和内核文档理解它在当前路径下的用途效率远比按章节啃书高。当然前提是你至少得会看kmalloc/kzalloc/devm_kzalloc这类基础函数否则代码都读不连贯。2.2 从FrameBuffer到DRM/KMS的演进逻辑老一批工程师接触 Android 显示驱动时最先认识的通常是/dev/fb0也就是 FrameBuffer 设备。Android 早期版本2.x 到 4.x大量使用 fbdev 接口内核注册一个fb_info用户空间通过mmap映射显存再通过ioctl设置分辨率、位深、颜色格式。fbdev 接口简单直接调试也容易但越到后面越发现它的问题它把显示控制器抽象得太粗对“多层合成”“旋转”“缩放”“带宽管理”这些现代显示屏的常见需求支持很差。于是 DRM/KMSDirect Rendering Manager / Kernel Mode Setting逐渐成为主流。DRM 架构里有几个核心对象DRM Device、Plane、CRTC、Encoder、Connector、Framebuffer。你可以这么理解Plane相当于硬件图层图像最终是要贴到某一个 Plane 上的。CRTC显示控制器的主要输出通道负责从内存读像素、做时序生成。Encoder把 CRTC 输出的并行像素信号编码成某种接口协议。Connector物理连接的抽象比如 HDMI 口、eDP 口、DSI 口它上面挂了面板和检测引脚。Framebuffer一块具体的内存缓冲区描述包含 pitch、offset、格式、尺寸。这套抽象比 fbdev 细得多而且最重要的是它允许用户空间通过 atomic API 一次性提交多个 Plane、CRTC、Encoder 的状态保证合成结果是“一帧”的不会出现撕裂画面。现在 Android 的 HWC 和 SurfaceFlinger 已经完全基于 DRM 的 modset 接口来提交图层。所以我把“从 fbdev 到 DRM/KMS”的演进逻辑放在第一阶段的核心位置不是因为它考试常考而是因为你在看老代码和移植新内核时经常会同时遇到这两套东西。看得懂 fbdev又能说出为什么要被 DRM 取代你才能理解代码里各种 ifdef 和兼容逻辑存在的意义。我特别建议初学者去看 DRM 内核文档里关于 atomic mode setting 的说明它把 commit、state、validate 这套设计的思路讲得很清楚是理解现代显示驱动的钥匙。3. 学习路线第二阶段读懂设备树与显示控制器驱动3.1 设备树里显示节点到底配置了些什么到了实际移植一块屏幕或一个新平台时设备树DTS就是你的第一战场。很多新人打开 DTS 里 display 相关的节点看到几十个属性直接头大。其实把它们分类以后就会发现显示节点的信息无外乎四类。第一类是“连通性”即这个显示控制器接在哪个总线、使用哪几个时钟、哪几个中断、GIC 的哪一个 SPI。比如clocks属性里通常会有dclk像素时钟、aclk总线时钟、hclkAHB 时钟它们的频率直接决定屏幕刷新率能不能稳定跑到 60Hz。第二类是“内存和 IOMMU”比如iommus指向显示控制器使用的 SMMU 设备表示显存访问需要经过地址转换interconnects则描述显示控制器的内存带宽路径在某些平台上带宽不够时这里配置错了会导致高分辨率下屏幕闪烁。第三类是“输出接口”这部分一般会嵌套一个子节点描述连接的 panel 或者 HDMI 控制器。DSI 接口的节点里通常会有>

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

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

免费获取报价 →
↑