1. 课程导入

在全志(Allwinner)平台上移植 DRM/KMS 显示驱动,真正让工程师“卡住”的往往不是寄存器配置,而是显示缓存(framebuffer / GEM buffer)如何分配、如何共享、如何被显示控制器读取。全志 SoC 的显示通路通常由 DE(Display Engine)、TCON、MIPI DSI/HDMI 等模块组成,它们通过 DMA 直接读取内存中的像素数据;而 GPU、VPU、摄像头又常常需要与显示共享同一块缓冲区。于是 DMA-BUF 成为贯穿整条链路的关键机制。本课程的目标,就是把这套“缓存—DMA-BUF—调试”的工程能力系统化地建立起来。

一、学习目标:从“能点亮”到“能定位”

本课程不满足于让屏幕亮起来,而是希望你能独立回答以下问题:

  • 全志 DE 显示时,framebuffer 的物理地址、stride、格式是如何被驱动解析并写入寄存器的?
  • GEM 对象、DMA-BUF 文件描述符、scatterlist 三者之间是什么关系?
  • 当出现花屏、撕裂、绿屏、DMA 读写越界时,应该从哪一层开始排查?
  • 如何用 ftrace、debugfs、dma-buf 导出信息和 DRM 调试节点定位缓存问题?

对应到能力层面,学完本课程你应当能够:读懂全志 DRM 驱动中的 plane/crtc 提交路径;为跨设备共享缓冲区选择合适的 DMA-BUF 导入/导出方式;使用内核提供的调试接口验证缓存一致性与地址映射是否正确。

二、核心概念:三张“地图”必须打通

理解全志显示缓存,需要同时掌握三张地图:内存地图、显示地图、共享地图。

  • 内存地图:CMA、ION(旧方案)、DMA heap、系统内存,决定了缓冲区从哪里来、是否连续、能否被 DMA 访问。
  • 显示地图:DRM framebuffer → plane → crtc → encoder → connector,决定了像素数据如何被扫描输出。
  • 共享地图:GEM handle → dma_buf → attachment → sg_table,决定了不同设备如何“看到”同一块内存。
关键认知:DMA-BUF 不是一块内存,而是一份“内存的契约”。它把 exporter 的物理页描述(sg_table)以文件描述符的形式交给 importer,importer 再据此建立自己的映射。调试 DMA-BUF,本质是调试这份契约是否被正确履行。

三、整体框架:从应用到像素的完整链路

下图给出本课程贯穿始终的整体框架。左侧是用户态与 DRM 接口,中间是 GEM/DMA-BUF 缓存管理层,右侧是全志 DE 显示硬件。可以看到,缓存管理位于“承上启下”的位置:向上承接应用与合成器的 buffer 请求,向下为 DE 提供可 DMA 的物理描述。

全志 DRM 显示缓存与 DMA-BUF 整体框架 用户态 / 应用 Wayland / X11 / 直接 DRM 申请 buffer、提交 page flip dma-buf fd 跨进程传递 DRM / KMS 核心 framebuffer / plane / crtc atomic commit 提交路径 format / modifier / stride GEM / DMA-BUF 缓存管理层 GEM object → dma_buf → attachment sg_table / dma_addr / 一致性维护 CMA / DMA heap / ION 后端 全志显示硬件:DE (Display Engine) + TCON + 输出接口 DE 图层 / 通道 TCON 时序控制 MIPI DSI / HDMI 显示面板 DMA 地址写入寄存器 调试与观测手段 dmesg / DRM debug debugfs: dma_buf, gem ftrace / tracepoint devmem / 寄存器 dump

四、实战路线图:四步走

为了让学习路径清晰,本课程按“先通链路、再管缓存、后调共享、终排故障”的顺序推进:

  1. 打通显示链路:理解全志 DE 驱动 probe、crtc/plane 初始化、modeset 流程,能点亮一个基本 framebuffer。
  2. 掌握缓存分配:区分 CMA、DMA heap、ION 的适用场景,读懂 GEM object 的创建与 mmap 路径。
  3. 吃透 DMA-BUF:掌握 export/import、attach、map_attachment、begin/end_cpu_access 的完整时序。
  4. 建立调试体系:用 debugfs、ftrace、寄存器 dump 定位花屏、撕裂、越界与一致性问题。

五、典型场景与常见误区

一个高频场景是:GPU 渲染完成后,把 dma-buf fd 交给 DRM plane 显示。此时若 exporter 与 importer 对缓存一致性(coherency)理解不一致,就会出现“GPU 写完但 DE 读到旧数据”的花屏。另一个场景是摄像头采集的 buffer 直接送显示,若 stride 或格式对齐不匹配,会出现斜纹或错位。

常见现象 可能层级 首选排查手段
屏幕全绿 / 全黑 DE 未取到有效地址或格式错误 dump DE 寄存器、检查 fb format
画面撕裂 page flip 与 vsync 未同步 检查 atomic commit 与 vblank 事件
斜纹 / 错位 stride 或像素格式不匹配 对比 pitch 与 width×bpp
GPU 写完显示旧帧 缓存一致性未维护 检查 begin/end_cpu_access 与 fence
DMA 越界报错 sg_table 长度或偏移错误 debugfs 查看 dma_buf 附件信息
误区提醒:很多初学者把 DMA-BUF 当成“零拷贝的银弹”,忽略了 exporter 必须正确实现 sg_table 与一致性回调。若 exporter 只返回一段虚拟地址而没有物理页描述,importer 侧很可能拿到错误地址,表现为随机花屏或内核 oops。

六、动手前的准备

建议准备一块全志开发板(如 H3/H5/A64/R528 等)、可用的内核源码与交叉编译工具链,并确认内核已开启以下配置:

CONFIG_DRM=y
CONFIG_DRM_SUN4I=y
CONFIG_DMABUF_HEAPS=y
CONFIG_DMA_SHARED_BUFFER=y
CONFIG_DEBUG_FS=y
CONFIG_FTRACE=y

同时熟悉几个基础命令,后续章节会反复使用:

cat /sys/kernel/debug/dma_buf/bufinfo
ls /sys/kernel/debug/dri/0/
dmesg | grep -i -E "sun4i|drm|dma"
cat /proc/interrupts | grep -i tcon

当你能够读懂 bufinfo 中每个 attachment 的设备和方向,能够从 dmesg 中分辨出是 DE 取址失败还是 TCON 时序异常,就已经具备了本课程要求的核心调试直觉。接下来,我们将从全志 DRM 驱动的显示通路开始,逐层拆解缓存与 DMA-BUF 的工程细节。