Linux内核显示驱动KMS驱动与设备树配置实战 - 第1节
1.1 Linux 图形显示栈架构
Linux 图形显示栈是一个多层协作的软件体系,从用户态的图形应用程序到底层的显示硬件,每一层都承担着特定的职责。理解整个栈的层次结构,是掌握 KMS/DRM 子系统的基础。
1.2 KMS 与 DRM 的关系
DRM (Direct Rendering Manager) 和 KMS (Kernel Mode Setting) 是 Linux 图形子系统中两个紧密关联但职责不同的组件。很多初学者容易混淆它们,理解其关系是入门的关键。
| 维度 | DRM (Direct Rendering Manager) | KMS (Kernel Mode Setting) |
|---|---|---|
| 核心职责 | 图形缓冲区管理、渲染同步、GPU 命令调度 | 显示模式设置、分辨率/刷新率控制、显示管线配置 |
| 主要功能 | GEM 对象管理、DMA-BUF 共享、Fence 同步、GPU 上下文 | CRTC/Encoder/Connector/Plane 配置、VBLANK 控制、DPMS |
| 用户态接口 | drmIoctl() — DRM_IOCTL_GEM_* 系列 |
drmModeSetCrtc() — DRM_IOCTL_MODE_* 系列 |
| 硬件依赖 | 需要 GPU 硬件支持(至少 2D 加速) | 需要 Display Controller 硬件支持 |
| 历史渊源 | 最初为 DRI (Direct Rendering Infrastructure) 而生 | 从 DRM 中分离,替代 UMS (User Mode Setting) |
drivers/gpu/drm/ 目录下,与 DRM 核心代码交织在一起。驱动开发时,我们通常说“实现一个 DRM 驱动”,但实际工作量的 70% 以上是 KMS 相关的显示管线配置。
1.3 KMS 核心概念详解
KMS 的核心抽象模型由四个基本对象组成:CRTC、Encoder、Connector 和 Plane。它们共同构成了从帧缓冲区到显示器的完整数据通路。理解这四个对象及其关系,是编写 KMS 驱动的第一步。
1.3.1 四大核心对象一览
| 对象 | 英文全称 | 硬件对应 | 核心职责 |
|---|---|---|---|
| CRTC | Cathode Ray Tube Controller | 显示控制器 (Display Controller) | 扫描输出、时序生成、帧缓冲读取、VBLANK 中断 |
| Encoder | Encoder | 显示信号编码器 (LVDS/HDMI/DP 编码器) | 像素数据编码为特定接口信号、时钟调整 |
| Connector | Connector | 物理连接器 (HDMI/DP/VGA 接口) | 物理连接检测、EDID 读取、热插拔事件 |
| Plane | Plane | 硬件图层 (Overlay/Sprite) | 独立帧缓冲、缩放、色彩转换、图层叠加 |
1.3.2 CRTC — 显示控制器的核心
CRTC 是 KMS 中最重要的对象,它模拟了传统 CRT 显示器的扫描控制逻辑。在现代 LCD 显示器中,CRTC 对应 SoC 内部的 Display Controller 或 Video Timing Controller。
- 时序生成: 产生 H-sync、V-sync、DE (Data Enable)、像素时钟等时序信号
- 帧缓冲读取: 从内存中指定的 Framebuffer 地址读取像素数据
- 扫描输出: 按行/帧顺序将像素数据推送到显示管线
- VBLANK 控制: 在帧消隐期产生中断,用于同步渲染和避免撕裂
- 模式设置: 配置分辨率、刷新率、像素格式等显示参数
在设备树中,CRTC 通常对应一个显示控制器节点。例如,在 i.MX8M Plus 平台上,LCDIF 控制器就是一个 CRTC 实例:
/* 设备树中 CRTC 对应的显示控制器节点示例 */
&lcdif {
compatible = "fsl,imx8mp-lcdif";
reg = <0x32e00000 0x10000>;
interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk IMX8MP_CLK_MEDIA_DISP1_PIX>,
<&clk IMX8MP_CLK_MEDIA_DISP1_APB>;
clock-names = "pix", "axi";
assigned-clocks = <&clk IMX8MP_CLK_MEDIA_DISP1_PIX>;
assigned-clock-parents = <&clk IMX8MP_CLK_VIDEO_PLL1_OUT>;
status = "disabled";
};
1.3.3 Encoder — 信号编码与转换
Encoder 负责将 CRTC 输出的并行像素数据转换为特定显示接口所需的串行信号格式。不同的显示接口(HDMI、DP、LVDS、MIPI DSI)需要不同的 Encoder 硬件。
| Encoder 类型 | 输出接口 | 典型硬件 | 驱动中的实现 |
|---|---|---|---|
| LVDS Encoder | LVDS 差分对 | i.MX8 LVDS 桥接器 | drm_encoder_init() + DRM_MODE_ENCODER_LVDS |
| HDMI Encoder | TMDS 信号 | Synopsys DesignWare HDMI | drm_encoder_init() + DRM_MODE_ENCODER_TMDS |
| DP Encoder | DisplayPort 差分对 | Cadence DP TX | drm_encoder_init() + DRM_MODE_ENCODER_DP |
| MIPI DSI Encoder | MIPI D-PHY 差分对 | Rockchip DSI 控制器 | drm_encoder_init() + DRM_MODE_ENCODER_DSI |
1.3.4 Connector — 物理连接与检测
Connector 代表物理显示接口,负责检测显示器是否连接、读取显示器的 EDID 信息(包含支持的分辨率、刷新率等)、处理热插拔事件。
drm_edid_read() 和 drm_add_edid_modes() 标准接口来简化开发。
1.3.5 Plane — 硬件图层与合成
Plane 是 KMS 中相对较新的概念,它代表一个独立的硬件图层。每个 Plane 可以有自己的 Framebuffer、位置、大小、缩放比例和色彩格式。CRTC 会将所有绑定的 Plane 按照 Z-order 顺序合成最终的显示画面。
Plane 分为两种类型:
- Primary Plane: 每个 CRTC 必须有一个 Primary Plane,它是显示画面的基础层
- Overlay Plane: 可选的叠加层,用于实现视频播放、UI 悬浮层、光标等效果
/* 驱动中创建 Plane 的典型代码片段 */
static int driver_plane_init(struct drm_device *drm)
{
struct drm_plane *plane;
int ret;
/* 分配并初始化一个 Plane */
plane = kzalloc(sizeof(*plane), GFP_KERNEL);
if (!plane)
return -ENOMEM;
/* 使用通用 Plane 辅助函数初始化 */
ret = drm_universal_plane_init(drm, plane, 0,
&driver_plane_funcs,
driver_formats,
ARRAY_SIZE(driver_formats),
NULL, DRM_PLANE_TYPE_PRIMARY, NULL);
if (ret) {
kfree(plane);
return ret;
}
/* 注册 Plane 的 atomic 更新回调 */
drm_plane_helper_add(plane, &driver_plane_helper_funcs);
return 0;
}
1.4 KMS 对象之间的连接关系
在 KMS 驱动中,四大对象不是孤立的,它们通过特定的拓扑结构连接在一起,形成完整的显示管线。理解这个拓扑是配置设备树和编写驱动的基础。