显示驱动调试:Qt显示适配与显示性能调优 - 第2节
2.1 Linux DRM/KMS 框架概述
在 Linux 显示子系统中,DRM (Direct Rendering Manager) 和 KMS (Kernel Mode Setting) 构成了现代图形栈的基石。DRM 最初是为 GPU 的 3D 加速而设计的内核模块,而 KMS 则接管了显示控制器的模式设置、分辨率切换、平面合成等任务。两者合并后,DRM/KMS 成为 Linux 上唯一的标准显示驱动框架。
2.1.1 DRM 架构分层
DRM 框架采用三层结构,从内核到用户空间逐级抽象:
| 层级 | 组件 | 职责 |
|---|---|---|
| 用户空间 | libdrm / Mesa / Qt | 封装 ioctl 调用,提供 GBM、EGL、KMS API |
| 内核空间 | DRM Core + KMS Helper | 设备管理、对象模型、原子提交、帧缓冲管理 |
| 硬件层 | GPU 厂商驱动 (i915/amdgpu/msm) | 寄存器编程、中断处理、显示控制器初始化 |
2.2 Framebuffer 原理与实现
Framebuffer 是显示内存中的一块连续区域,存储着屏幕每个像素的颜色数据。在 DRM/KMS 框架中,Framebuffer 是一个抽象对象,它描述了一块内存的格式、宽度、高度、步幅等信息。用户空间的应用程序将渲染结果写入 Framebuffer,KMS 负责将其扫描输出到显示器。
2.2.1 物理 Framebuffer vs 逻辑 Framebuffer
| 类型 | 说明 | 典型用途 |
|---|---|---|
| 物理 Framebuffer | 位于显存或系统内存中的实际像素缓冲区 | 直接由显示控制器读取并输出 |
| 逻辑 Framebuffer | DRM 对象,包含元数据(格式、尺寸、修饰符) | 用户空间通过 FD 引用,用于提交显示 |
| Dumb Buffer | 简单连续内存,适用于无 GPU 的嵌入式设备 | 小型 SoC 的显示输出 |
2.2.2 Framebuffer 创建流程
- 打开 DRM 设备:
open("/dev/dri/card0", O_RDWR) - 获取资源:调用
drmModeGetResources()获取 CRTC、Encoder、Connector 列表 - 创建 Dumb Buffer:
drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create) - 映射到用户空间:
drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map)+mmap() - 添加 Framebuffer 对象:
drmModeAddFB(fd, width, height, depth, bpp, pitch, handle, &fb_id) - 设置 CRTC:
drmModeSetCrtc(fd, crtc_id, fb_id, x, y, connectors, count, mode)
// 创建 Dumb Buffer 的核心代码片段
struct drm_mode_create_dumb create = {0};
create.width = 1920;
create.height = 1080;
create.bpp = 32;
drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create);
struct drm_mode_map_dumb map = {0};
map.handle = create.handle;
drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map);
uint32_t *fb_map = mmap(NULL, create.size, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, map.offset);
// 填充像素数据...
drmModeAddFB(fd, 1920, 1080, 24, 32, create.pitch, create.handle, &fb_id);
drmModeSetCrtc(fd, crtc_id, fb_id, 0, 0, &conn_id, 1, &mode);
modetest 工具可以快速验证 Framebuffer 创建和显示输出。命令示例:modetest -M imx-drm -s 32:1920x1080
2.3 Mode Setting 原理
Mode Setting 是指配置显示控制器的输出时序、分辨率、刷新率、色彩深度等参数的过程。KMS 将 Mode Setting 从用户空间 X Server 移入内核,使得切换显示模式时不会出现闪烁或撕裂。
2.3.1 显示模式参数
一个完整的显示模式由 drmModeModeInfo 结构体描述,包含以下关键字段:
| 字段 | 含义 | 典型值 (1080p60) |
|---|---|---|
| hdisplay | 水平有效像素 | 1920 |
| vdisplay | 垂直有效像素 | 1080 |
| htotal | 水平总像素(含消隐区) | 2200 |
| vtotal | 垂直总像素(含消隐区) | 1125 |
| clock | 像素时钟 (kHz) | 148500 |
| vrefresh | 刷新率 (Hz) | 60 |
2.3.2 原子 Mode Setting (Atomic API)
传统 KMS 接口需要多次 ioctl 调用分别设置 CRTC、Plane、Connector,容易导致中间状态不一致。DRM 原子接口允许在一次原子提交中同时更新多个对象的状态,保证显示切换的完整性。
2.3.3 CRTC、Plane、Encoder、Connector 关系
DRM/KMS 使用四个核心对象描述显示管线:
- CRTC (Cathode Ray Tube Controller):控制扫描输出时序,驱动一个显示通道
- Plane:硬件图层,每个 Plane 可绑定一个 Framebuffer,支持缩放和合成
- Encoder:将 CRTC 输出的像素流编码为特定接口信号(HDMI、DP、LVDS)
- Connector:物理连接器,连接显示设备,包含 EDID 信息
drm_info 或 cat /sys/class/drm/*/edid | edid-decode 可以验证 EDID 是否正常。
2.4 本章小结
Linux DRM/KMS 框架为显示驱动提供了统一的内核接口,Framebuffer 作为像素数据的载体,Mode Setting 负责配置显示时序。理解这三者的关系是进行 Qt 显示适配和性能调优的基础。在实际调试中,掌握 modetest、drm_info、libdrm 等工具链,能够快速定位显示初始化、模式切换、图层合成等环节的问题。