drm 驱动系列 - 第二章 KMS

本文详细介绍了KernelModeSetting(KMS)的核心思想,包括CRTC、ENCODER、CONNECTOR、PLANE等组件的作用,以及drm_mode_object结构的管理机制。KMS通过drm_mode_config组件提供灵活的管理模式,允许用户通过ioctl配置硬件显示链路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章目录


作者: baron

KMS(Kernel Mode Setting) 的核心思想就是将图形子系统的各个组件进行拆分和抽象,相对于 fbdev 使得内核能够更灵活、可扩展地管理这些组件。他们被拆分为 PLANE, CRTC, CONNECTOR, ENCODER, FB. 等组件

kms = drm_mode_config + 组件(obj)


元素说明
CRTC对显示 buffer 进行扫描,并产生时序信号的硬件模块,通常指 Display Controller
ENCODER负责将 CRTC 输出的 timing 时序转换成外部设备所需要的信号的模块,如 HDMI 转换器或 DSI Controller
CONNECTOR连接物理显示设备的连接器,如 HDMI、DisplayPort、DSI 总线,通常和 Encoder 驱动绑定在一起
PLANE硬件图层,有的 Display 硬件支持多层合成显示,但所有的 Display Controller 至少要有 1 个 plane
FBFramebuffer,单个图层的显示内容,唯一一个和硬件无关的基本元素
VBLANK软件和硬件的同步机制,RGB 时序中的垂直消影区,软件通常使用硬件 VSYNC 来实现
property任何你想设置的参数,都可以做成 property,是 DRM 驱动中最灵活、最方便的 Mode setting 机制
DUMB只支持连续物理内存,基于 kernel 中通用 CMA API 实现,多用于小分辨率简单场景
PRIME连续、非连续物理内存都支持,基于 DMA-BUF 机制,可以实现 buffer 共享,多用于大内存复杂场景
fencebuffer 同步机制,基于内核 dma_fence 机制实现,用于防止显示内容出现异步问题

此处参考最简单的DRM应用程序 (double-buffer)


上述的组件都由 drm_mode_object 描述. 通过 type 来确定 obj 描述的对象类型.

struct drm_mode_object {
    uint32_t id;  // 由 dev->mode_config.crtc_idr 申请过来的 idr, 本质是查找 obj 的索引

    /*
     #define DRM_MODE_OBJECT_CRTC 0xcccccccc
     #define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0
     #define DRM_MODE_OBJECT_ENCODER 0xe0e0e0e0
     #define DRM_MODE_OBJECT_MODE 0xdededede
     #define DRM_MODE_OBJECT_PROPERTY 0xb0b0b0b0
     #define DRM_MODE_OBJECT_FB 0xfbfbfbfb
     #define DRM_MODE_OBJECT_BLOB 0xbbbbbbbb
     #define DRM_MODE_OBJECT_PLANE 0xeeeeeeee
     #define DRM_MODE_OBJECT_ANY 0
     */
    uint32_t type;  // obj 类型, 不同的 type 表示不同的对象.
    struct drm_object_properties *properties; // 最多支持 24 个 properties
    struct kref refcount;
    void (*free_cb)(struct kref *kref);  // 释放回调接口
};

从结构体描述可知这些对象主要包括三个部分的内容.

  1. 对象 id 管理, 通过对象的 id, 可以通过 dev->mode_config.crtc_idr 查找到对应的对象.
  2. 属性(paropertise)管理, 对象的属性统一被存放到 properties.
  3. 对象生命周期管理, 释放对象时提供了 free_cb 接口.

内核提供了接口 drm_mode_object_get用初始化 obj. 该函数从 dev->mode_config.crtc_idr上申请一个 dir , 并用这个 idr 作为索引初始化 obj 的 id

// dev : 所属的 drm_device
// obj : 要初始化的 obj
// obj_type : obj 是什么对象. 对象包括 CRTC, CONNECTOR, ENCODER, FB, PLANE, PROPERTY 等这些类型
int drm_mode_object_get(struct drm_device *dev,
            struct drm_mode_object *obj, uint32_t obj_type)
{
    return drm_mode_object_get_reg(dev, obj, obj_type, true, NULL);
}

int drm_mode_object_get_reg(struct drm_device *dev,
                struct drm_mode_object *obj,
                uint32_t obj_type,
                bool register_obj,
                void (*obj_free_cb)(struct kref *kref))
{
    int ret;

    mutex_lock(&dev->mode_config.idr_mutex);
    ret = idr_alloc(&dev->mode_config.crtc_idr, register_obj ? obj : NULL, 1, 0, GFP_KERNEL);
    if (ret >= 0) {
        obj->id = ret;        // 设置 id
        obj->type = obj_type; // 设置类型
        if (obj_free_cb) {    // 设置 free_cb
            obj->free_cb = obj_free_cb;
            kref_init(&obj->refcount);
        }
    }
    mutex_unlock(&dev->mode_config.idr_mutex);

    return ret < 0 ? ret : 0;
}

上述 kms 相关的组件 (obj) 通过 drm_mode_config 进行维护和管理. 所以这个数据结构非常重要, 它也是 kms 的核心. 他们的关系如下图所示.

在这里插入图片描述

这些组件 (obj) + drm_mode_config 就组成了 kms. 通过 drm_dev 导出的 ioctl 我们就可以使用任意 plane + crtc + encoder + connector + panel 来组合我们需要的硬件显示链路了.例如我们有如下硬件结构

plane0 --> crtc0 --> encoder0(mipi) --> connector0 --> panel0(lcd1)
                 --> encoder1(hdmi) --> connector1 --> panel1(lcd2)
                                                   --> panel2(lcd3)

我们有个 mipi lcd3 想用它显示, 用户可以通过 ioctl 设置我们需要的硬件链路, 十分方便.

plane0 --> crtc0 --> encoder0(mipi) --> connector0 --> panel3
<think>嗯,用户遇到了一个问题,执行了`sudo modprobe -r drm_kms_helper`,结果提示模块是内置的,无法移除。这时候我需要解释为什么会出现这个情况,以及用户可能需要采取的其他步骤。 首先,我需要确认`drm_kms_helper`模块的作用。它是DRM内核模式设置的一部分,负责处理显示输出的核心功能,比如热插拔检测和连接器轮询。这个模块通常是内置的,而不是可加载的内核模块,所以用户无法用modprobe移除它。 接下来,用户为什么会想移除这个模块呢?可能他们遇到了显示问题,比如分辨率不正确、热插拔检测有问题,或者想重新加载显卡驱动。但因为这个模块是内置的,直接移除不可行,需要其他方法。 我应该告诉用户这个模块的重要性,移除它可能导致显示问题。然后,建议替代方案:重新加载整个DRM模块或显卡驱动。比如,使用`sudo rmmod`命令卸载相关的模块,但需要注意依赖关系,可能要先卸载依赖它的模块,如`i915`(Intel显卡)、`amdgpu`(AMD显卡)或`nvidia`(NVIDIA显卡)。 然后,用户可能需要重新加载这些模块。例如,对于Intel显卡,可以执行`sudo modprobe i915`。同时,提醒用户这个过程可能导致短暂的显示中断,甚至需要重启,特别是在SSH连接中操作需谨慎。 另外,用户可能需要检查内核启动参数,如果问题与显示相关,可能需要添加参数如`nomodeset`,但这会影响图形性能。或者建议用户更新内核或显卡驱动,确保兼容性。 还需要提醒用户,强制移除内核模块有风险,可能导致系统不稳定,尤其是在生产环境中需谨慎。如果用户只是想重置显示配置,可以尝试重启或重新加载Xorg/显示管理器。 总结步骤:解释模块作用,说明无法移除的原因,提供替代方法(卸载相关驱动模块并重新加载),注意事项和风险,其他可能的解决方案。确保用户理解操作的风险,并在安全的环境下进行测试。</think>### 问题分析 您尝试执行 `sudo modprobe -r drm_kms_helper` 时出现 **"Module is builtin"** 错误,说明该模块已直接编译进内核,而非以可加载模块(.ko文件)形式存在。以下是具体解释和解决方案: --- ### 一、模块作用与错误原因 #### 1. `drm_kms_helper` 的核心功能 - **显示核心管理**:负责显示器连接状态检测(热插拔)、分辨率协商、连接器轮询等 - **依赖关系**:是 `drm` 框架的核心组件,通常与显卡驱动(如 `i915`、`amdgpu`)深度绑定 #### 2. 错误原因 ```bash modprobe: FATAL: Module drm_kms_helper is builtin. ``` - **内核编译模式**:该模块在您系统的内核配置中标记为 `=y`(内置),而非 `=m`(可加载模块) - **无法动态卸载**:内置模块会随内核启动永久驻留内存,无法通过 `modprobe -r` 移除 --- ### 二、替代解决方案 #### ▶ 场景:需要重置显示相关功能(如修复分辨率异常/热插拔失效) ```bash # 1. 强制重置整个 DRM 子系统(可能引起屏幕闪烁) sudo rmmod <显卡驱动模块> # 例如:sudo rmmod i915(Intel)/ amdgpu(AMD)/ nouveau(NVIDIA 开源) sudo modprobe <显卡驱动模块> # 重新加载驱动 # 2. 通过 sysfs 触发连接器重探测(推荐优先尝试) echo detect > /sys/class/drm/card0-HDMI-A-1/status # 将路径替换为实际接口(通过 ls /sys/class/drm 查看) ``` #### ▶ 场景:调试 EDID 或显示时序问题 ```bash # 1. 覆盖 EDID 无需卸载模块(示例) sudo cp /path/to/custom.edid /lib/firmware/edid.bin echo edid.bin > /sys/class/drm/card0-HDMI-A-1/edid_load # 通过 sysfs 注入新 EDID # 2. 通过内核参数临时禁用功能(重启生效) # 在 GRUB 配置中添加: nomodeset # 完全禁用内核模式设置(极端情况) drm_kms_helper.poll=0 # 禁用连接器轮询(解决某些 HDMI 检测问题) ``` --- ### 三、高级操作:重建内核模块(需重新编译内核) 仅建议熟悉内核编译的用户尝试: 1. **修改内核配置**: ```bash # 在 kernel source 目录执行: make menuconfig # 导航到 Graphics support → DRM → <显卡品牌> → 将 "DRM KMS helper" 设为 =m ``` 2. **编译并安装新内核** ```bash make -j$(nproc) && sudo make modules_install && sudo make install ``` 3. **重启后验证** ```bash lsmod | grep drm_kms_helper # 此时应显示为可加载模块 ``` --- ### 四、风险与注意事项 | 操作类型 | 潜在风险 | 应急恢复方法 | |-----------------------|--------------------------------------------------------------------------|----------------------------------| | 强制卸载显卡驱动模块 | 可能导致屏幕冻结、SSH 连接中断 | 物理重启设备 | | 修改内核配置 | 错误编译会导致系统无法启动 | 保留旧内核启动项作为备份 | | 写入错误 EDID | 显示器黑屏或分辨率异常 | 通过 TTY 删除错误 EDID 文件 | --- ### 五、推荐替代调试工具 1. **DRM 调试信息输出**: ```bash sudo sysctl -w drm.debug=0x0e # 启用完整 DRM 日志(dmesg 查看) ``` 2. **EDID 实时读取**: ```bash sudo cat /sys/class/drm/card0-HDMI-A-1/edid | edid-decode # 验证当前 EDID ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值