2. DRM框架基础:DRM架构与核心数据结构
DRM(Direct Rendering Manager)是Linux内核中负责图形显示和渲染管理的核心框架。它向上提供统一的用户空间接口(通过/dev/dri/card0等设备节点),向下抽象不同GPU和显示控制器的硬件差异。理解DRM的架构和核心数据结构,是掌握KMS组件初始化流程的前提。
核心观点: DRM框架通过
drm_device和drm_driver两个核心结构体,将硬件驱动、显示管理、内存管理、渲染调度等子系统有机整合在一起。
2.1 DRM架构分层
DRM框架在Linux内核中位于字符设备层与硬件驱动层之间,其架构可分为三个层次:
- 用户空间接口层: 提供
ioctl系统调用,用户态程序(如Xorg、Wayland compositor、Mesa)通过/dev/dri/card0访问DRM功能。 - DRM核心层: 实现设备注册、文件操作、缓冲区管理、帧缓冲(FB)、模式设置(KMS)、同步对象(fence)等通用逻辑。
- 硬件驱动层: 由具体GPU厂商(Intel、AMD、NVIDIA、Qualcomm等)实现,填充
drm_driver结构体中的回调函数,操作具体硬件寄存器。
2.2 核心数据结构:drm_device
drm_device是DRM框架中最重要的核心结构体,代表一个DRM设备实例。每个物理GPU或显示控制器对应一个drm_device对象。该结构体聚合了所有子系统的状态信息。
| 字段名 | 类型 | 说明 |
|---|---|---|
dev |
struct device * |
指向内核通用设备结构体,用于电源管理、DMA等 |
driver |
struct drm_driver * |
指向驱动回调函数集合 |
primary |
struct drm_minor * |
主设备节点(card0)对应的minor结构 |
render |
struct drm_minor * |
渲染设备节点(renderD128)对应的minor结构 |
mode_config |
struct drm_mode_config |
KMS模式配置管理结构,包含所有encoder、crtc、connector、plane |
filelist |
struct list_head |
已打开该设备的文件描述符链表 |
event_lock |
spinlock_t |
保护事件队列的自旋锁 |
vblank |
struct drm_vblank_crtc * |
垂直消隐(vblank)控制结构数组 |
开发提示: 驱动初始化时,通常通过
drm_dev_alloc()分配drm_device,然后调用drm_dev_register()注册到系统。在KMS驱动中,mode_config字段是初始化显示管线的核心入口。
2.3 核心数据结构:drm_driver
drm_driver是驱动开发者需要重点实现的结构体。它定义了一组回调函数指针,DRM核心层在特定时机调用这些回调,驱动通过填充这些回调来操作硬件。
| 回调函数 | 调用时机 | 典型实现 |
|---|---|---|
load() |
设备探测时(已废弃,推荐使用drm_dev_init) |
初始化硬件、分配资源 |
open() |
用户空间打开设备节点时 | 分配文件私有数据 |
postclose() |
用户空间关闭设备节点时 | 清理文件私有数据 |
lastclose() |
最后一个文件描述符关闭时 | 恢复显示模式、释放资源 |
irq_handler() |
硬件中断触发时 | 处理vblank、fence信号 |
enable_vblank() |
开启垂直消隐中断 | 写寄存器使能vblank中断 |
disable_vblank() |
关闭垂直消隐中断 | 写寄存器屏蔽vblank中断 |
gem_create_object() |
创建GEM对象时 | 分配显存或系统内存 |
2.4 设备初始化流程
一个典型的DRM设备初始化流程包含以下步骤:
- 平台驱动探测: 内核调用驱动的
probe()函数,传入struct platform_device或struct pci_dev。 - 分配drm_device: 调用
drm_dev_alloc()分配并初始化drm_device,同时绑定drm_driver。 - 硬件初始化: 驱动进行寄存器映射、时钟使能、中断注册等硬件操作。
- KMS组件初始化: 创建crtc、encoder、connector、plane等KMS对象,并添加到
mode_config。 - 注册设备: 调用
drm_dev_register()创建设备节点,暴露给用户空间。
注意事项: 在旧版本内核中,驱动通常使用
drm_platform_init()或drm_get_pci_dev()等辅助函数。现代内核推荐使用drm_dev_alloc() + drm_dev_register()的组合,并配合devm_drm_dev_init()实现资源管理。
2.5 代码示例:最小DRM驱动骨架
以下代码展示了一个最小DRM驱动的初始化骨架,重点展示drm_driver和drm_device的使用:
#include <linux/module.h>
#include <drm/drm_drv.h>
#include <drm/drm_device.h>
static int my_driver_open(struct drm_device *dev, struct drm_file *file)
{
pr_info("DRM device opened\n");
return 0;
}
static void my_driver_postclose(struct drm_device *dev, struct drm_file *file)
{
pr_info("DRM device closed\n");
}
static struct drm_driver my_drm_driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM,
.open = my_driver_open,
.postclose = my_driver_postclose,
.name = "my_drm_driver",
.desc = "My Minimal DRM Driver",
.date = "20250101",
.major = 1,
.minor = 0,
};
static int my_probe(struct platform_device *pdev)
{
struct drm_device *drm;
int ret;
drm = drm_dev_alloc(&my_drm_driver, &pdev->dev);
if (IS_ERR(drm))
return PTR_ERR(drm);
/* 硬件初始化代码在此 */
ret = drm_dev_register(drm, 0);
if (ret) {
drm_dev_put(drm);
return ret;
}
platform_set_drvdata(pdev, drm);
return 0;
}
static int my_remove(struct platform_device *pdev)
{
struct drm_device *drm = platform_get_drvdata(pdev);
drm_dev_unregister(drm);
drm_dev_put(drm);
return 0;
}
static struct platform_driver my_pdrv = {
.probe = my_probe,
.remove = my_remove,
.driver = {
.name = "my_drm_driver",
},
};
module_platform_driver(my_pdrv);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("KMS Expert");
MODULE_DESCRIPTION("Minimal DRM Driver Example");
2.6 关键概念对比
| 概念 | 作用域 | 生命周期 | 由谁实现 |
|---|---|---|---|
drm_device |
每个GPU/显示控制器一个实例 | 从probe到remove | DRM核心分配,驱动填充 |
drm_driver |
全局静态定义,所有设备共享 | 模块加载到卸载 | 驱动开发者定义 |
drm_file |
每个打开的设备节点一个实例 | open到close | DRM核心分配 |
drm_mode_config |
每个drm_device一个实例 | 与drm_device相同 | 驱动初始化时填充 |
总结: DRM框架通过
drm_device聚合所有显示子系统状态,通过drm_driver定义硬件操作接口。理解这两个结构体及其关系,是后续学习KMS组件初始化(crtc、encoder、connector、plane)的基础。下一章将深入分析这些KMS组件的初始化细节。