2. DRM驱动初始化:platform driver注册、drm_device分配与初始化、mode_config设置

DRM驱动的初始化是整个显示子系统工作的起点。对于嵌入式平台(如i.MX、Rockchip、Allwinner等),驱动通常以platform driver的形式注册到内核,随后完成drm_device的分配、初始化以及mode_config的配置。本章将拆解这三个关键步骤,并给出典型代码实现与调试要点。

2.1 platform driver注册

大多数嵌入式GPU/Display Controller都挂载在platform总线上。驱动入口通过module_platform_driver()宏完成注册,核心结构体为struct platform_driver。

成员 说明 典型值/回调
.probe 设备匹配成功后的初始化入口 xxx_drm_probe
.remove 设备移除时的清理入口 xxx_drm_remove
.driver.name 驱动名称,用于与DT compatible匹配 "xxx-display"
.driver.of_match_table 设备树匹配表 xxx_of_match
调试要点: 如果probe未被调用,请检查设备树中compatible属性是否与of_match_table一致,以及内核是否编译了对应的驱动模块。
// 典型 platform driver 注册示例
static const struct of_device_id xxx_of_match[] = {
    { .compatible = "vendor,display-controller", },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, xxx_of_match);

static struct platform_driver xxx_drm_platform_driver = {
    .probe  = xxx_drm_probe,
    .remove = xxx_drm_remove,
    .driver = {
        .name           = "xxx-display",
        .of_match_table = xxx_of_match,
    },
};

module_platform_driver(xxx_drm_platform_driver);

2.2 drm_device分配与初始化

在probe回调中,驱动需要完成drm_device的分配与核心初始化。标准流程分为三步:

  1. 分配:调用drm_dev_alloc()或devm_drm_dev_alloc()分配struct drm_device。
  2. 驱动私有数据设置:通过container_of或drm_device->dev_private关联硬件上下文。
  3. 注册:调用drm_dev_register()将设备注册到DRM子系统。
推荐做法: 使用devm_drm_dev_alloc()可以自动管理生命周期,避免手动释放遗漏。
// drm_device 分配与初始化典型代码
static int xxx_drm_probe(struct platform_device *pdev)
{
    struct drm_device *drm;
    struct xxx_drm_private *priv;
    int ret;

    // 1. 分配 drm_device + 私有数据(推荐 devm 版本)
    drm = devm_drm_dev_alloc(&pdev->dev, &xxx_drm_driver,
                             struct xxx_drm_private, drm);
    if (IS_ERR(drm))
        return PTR_ERR(drm);

    priv = to_xxx_private(drm);
    priv->dev = &pdev->dev;
    platform_set_drvdata(pdev, drm);

    // 2. 初始化硬件相关资源(IRQ、MMIO、时钟等)
    ret = xxx_hw_init(priv);
    if (ret)
        return ret;

    // 3. 设置 mode_config 回调与参数
    xxx_mode_config_init(drm);

    // 4. 注册 DRM 设备
    ret = drm_dev_register(drm, 0);
    if (ret)
        return ret;

    drm_fbdev_generic_setup(drm, 32);

    return 0;
}

2.3 mode_config设置

mode_config是DRM驱动的显示能力描述结构,必须在注册前完成初始化。关键设置项包括:

字段 含义 典型值
min_width / max_width 帧缓冲最小/最大宽度 16 / 4096
min_height / max_height 帧缓冲最小/最大高度 16 / 4096
funcs mode_config 回调函数集 &xxx_mode_config_funcs
fb_base 帧缓冲物理基地址(可选) 0 / 实际地址
preferred_depth 默认颜色深度 32
async_page_flip 是否支持异步 page flip true / false
// mode_config 初始化示例
static void xxx_mode_config_init(struct drm_device *drm)
{
    drm->mode_config.min_width  = 16;
    drm->mode_config.max_width  = 4096;
    drm->mode_config.min_height = 16;
    drm->mode_config.max_height = 4096;
    drm->mode_config.funcs      = &xxx_mode_config_funcs;
    drm->mode_config.preferred_depth = 32;
    drm->mode_config.async_page_flip = true;
}
常见错误: 忘记设置funcs会导致drm_mode_config_init()内部校验失败,驱动加载时出现WARNING或BUG。务必在drm_dev_register()之前完成所有mode_config字段的赋值。

2.4 初始化流程总览

下图展示了从platform driver匹配到mode_config设置完成的完整调用链:

DRM驱动初始化流程 platform_driver_register() xxx_drm_probe() devm_drm_dev_alloc() xxx_hw_init() xxx_mode_config_init() drm_dev_register() 说明:mode_config 必须在 drm_dev_register() 之前完成设置,否则注册时会校验失败

2.5 关键数据结构关系

以下SVG展示了platform_driver、drm_device、mode_config三者之间的引用关系:

核心数据结构关系图 platform_driver .probe / .remove drm_device dev_private / mode_config drm_mode_config min/max / funcs probe创建 包含 xxx_drm_private 硬件上下文 container_of 调试提示:通过 /sys/kernel/debug/dri/0/name 可查看已注册的 drm_device 通过 /sys/kernel/debug/dri/0/mode_config 可查看 mode_config 参数

2.6 调试检查清单

  • probe是否被调用:在xxx_drm_probe入口添加dev_info()打印,确认设备树匹配成功。
  • drm_device分配是否成功:检查devm_drm_dev_alloc()返回值,避免空指针解引用。
  • mode_config完整性:确认funcs、min/max已赋值,否则内核会抛出WARN_ON。
  • drm_dev_register返回值:注册失败通常是因为mode_config校验不通过或IRQ申请失败。
  • 查看注册状态:ls /sys/class/drm/ 应出现card0或card1等设备节点。
实战经验: 在调试初期,建议在xxx_drm_probe中逐段添加dev_dbg(),配合dmesg -w实时观察初始化进度。一旦drm_dev_register成功,用户空间即可通过libdrm的drmOpen()访问设备。