drm 驱动系列- 第一章 drm_device

本文是DRM学习记录,介绍了DRM子系统核心数据结构,其通过drm_device统一描述,提供用户空间与内核中KMS交互。详细阐述了drm_dev注册过程,包括初始化和注册步骤,还对源码中drm_dev_init和drm_dev_register等函数进行解读。

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

作者: baron

    drm 学习记录, 基础知识部分请参考何小龙的drm专题, 该系列博客非常详细的介绍了drm 的整个框架. 博主也是通过该系列文章入门, 只是有很多地方不是很理解, 于是参考内核源码进行一些补充. 文章内容引用会该专题部分内容.

    drm 子系统的核心数据结构, linux 中的每个 drm 子系统都由一个 drm_device 统一进行描述. drm_device 的核心功能就是提供用户空间和内核中的 kms 进行交互. 他的本质是一个主设备号为 DRM_MAJOR 226的字符设备, 每一个 drm_minor 都是一个次设备. 总共有三种次设备 primarycontrolrender.

struct drm_device {
    struct list_head legacy_dev_list;
    int if_version;         

    struct kref ref;              /* 设备引用计数 */
    struct device *dev;           /* 表示这是一个设备 */
    struct drm_driver *driver;    /* 对应的 drm_driver */
    void *dev_private;            /* 私有数据 */
    struct drm_minor *control;    /* drm_dev 下的 control 设备,对应的 driver type DRM_MINOR_CONTROL */
    struct drm_minor *primary;    /* drm_dev 下的 primary 设备,对应的 driver type DRM_MINOR_PRIMARY */
    struct drm_minor *render;     /* drm_dev 下的 render  设备,对应的 driver type DRM_MINOR_RENDER */
    bool registered;              // 设备是否已经注册
    ......
};

    通过 drm_minor 用来描述一个 drm_dev 下的三种不同可以实例化的设备, 每个 drm 子系统至少有一个 DRM_MINOR_PRIMARY 设备

  • DRM_MINOR_PRIMARY 对应主设备
  • DRM_MINOR_CONTROL 对应控制设备
  • DRM_MINOR_RENDER 对应管理设备
struct drm_minor {
    int index;                 // 次设备号, 用来标识该设备. 它由 drm_minors_idr 维护. 
    int type;                  // 表示该次设备的设备类型, DRM_MINOR_PRIMARY, DRM_MINOR_CONTROL, DRM_MINOR_RENDER
    struct device *kdev;       // 设备模型中的设备结构
    struct drm_device *dev;    // 指向所属的 drm 设备

    struct dentry *debugfs_root;   // 指向调试文件系统

    struct list_head debugfs_list; // 用于将次设备的 DebugFS 目录添加到全局列表中的链表头。
    struct mutex debugfs_lock;     // 互斥锁
};

    内核提供了两个接口 drm_dev_initdrm_dev_register来创建和注册 drm_dev.

static struct idr drm_minors_idr; 

    idr 全称 ID Radix IDR 主要用于建立 id 与指针(指向对应的数据结构) 之间的对应关系. 在 drm 框架中用来维护 drm 设备对应的次设备号. 次设备号在 drm_minors_idr 中申请. (0 - 64) 对应 DRM_MINOR_PRIMARY 设备, (64 - 128) 对应 DRM_MINOR_CONTROL 设备, (128 - 192) 对应 DRIVER_RENDER 设备.

关于 idr 的用法参考: IDR: ID Radix

1. 注册 drm_dev

    drm_device 的注册为两个部分, 首先是调用 drm_dev_init 创建并初始化一个 drm_device 然后调用 drm_dev_register 进行注册.

请添加图片描述

    注册完成之后会分别创建三个属性节点 dev/dri/cardxdev/dri/controlxdev/dri/enderDx, 用来和 kms 进行交互, 关系如下所示

在这里插入图片描述

    当我们使用 openioctlmmap等函数接口最终会回调到 drm_driver 的 drm_open, drm_ioctl, drm_gem_cma_mmap. 其中 drm_ioctl它提供了很多默认的实现。

static const struct drm_ioctl_desc drm_ioctls[] = {
    DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version,DRM_UNLOCKED|DRM_RENDER_ALLOW),
    DRM_IOCTL_DEF(DRM_IOCTL_GET_UNIQUE, drm_getunique, DRM_UNLOCKED),
    DRM_IOCTL_DEF(DRM_IOCTL_GET_MAGIC, drm_getmagic, DRM_UNLOCKED),
    DRM_IOCTL_DEF(DRM_IOCTL_IRQ_BUSID, drm_irq_by_busid, DRM_MASTER|DRM_ROOT_ONLY),
    ......
};

    libdrm 也是基于这些 ioctl 来实现的.这些 ioctl 有啥用呢?, 用户空间通过这些 ioctl 就可以与 CRTC, CONNECTOR, ENCODER, FB, PLANE, PROPERTY 等等这些 kms 中的 obj 进行交互.当然也支持客制化自己的 ioctl. 例如 rk 平台自己增加的 ioctl

static const struct drm_ioctl_desc rockchip_ioctls[] = {
    DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_CREATE, rockchip_gem_create_ioctl, DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW),
    DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_MAP_OFFSET, rockchip_gem_map_offset_ioctl, DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW),
    DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_GET_PHYS, rockchip_gem_get_phys_ioctl, DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW),
    DRM_IOCTL_DEF_DRV(ROCKCHIP_GET_VCNT_EVENT, rockchip_drm_get_vcnt_event_ioctl, DRM_UNLOCKED),
}; 

static struct drm_driver rockchip_drm_driver = {
    ......
    .ioctls         = rockchip_ioctls,
    .num_ioctls     = ARRAY_SIZE(rockchip_ioctls),
    ......
};

1.1 connector 属性文件

drm_device 注册完成之后会创建一系列 connector 相关的属性文件用于 debug connector 的状态.

  • status 节点: 有三个属性值 “connected”“disconnected”“unknown” 用来描述显示器的连接状态. 实例:
rk3566_rgo:/ # cat /sys/class/drm/card0-DSI-1/status
connected
rk3566_rgo:/ # cat /sys/class/drm/card0-Writeback-1/status
connected

小知识: Writeback 用于获取的图片不需要显示, 可以先通过 Writeback 回写系统内存(屏幕截图, 视频捕获等), 需要显示再写入显存.

  • enabled 节点: 有两个属性值 “enabled”“disabled”. 用来判断当前 connector 是否连接上了 encoder
rk3566_rgo:/ # cat /sys/class/drm/card0-DSI-1/enabled // 唤醒时
enabled
rk3566_rgo:/ # cat /sys/class/drm/card0-DSI-1/enabled // 休眠时
disabled
  • dpms 节点: 有四个属性值, 用来描述显示器的电源状态. “On”, 表示显示器正常工作. “Standby”", 表示显示器出于待机状态, 即仅仅背光没亮. “Suspend”, 显示器进入低功耗状态. “Off”, 表示显示器关闭电源.
rk3566_rgo:/ # cat /sys/class/drm/card0-DSI-1/dpms // 唤醒时
Off
rk3566_rgo:/ # cat /sys/class/drm/card0-DSI-1/dpms // 休眠时
On
  • modes 节点: 用来返回显示分辨率
rk3566_rgo:/ # cat /sys/class/drm/card0-DSI-1/modes
720x1280

2. 源码阅读

2.1 drm_dev_init

该函数用来创建一个 drm_dev. 每一个 drm 系统都需要创建一个 drm_dev 来和 kms 中的 obj 进行交互…

  1. 对应成员变量的简单初始化
  2. 检测 driver 的 driver_features 标志位是否设置 DRIVER_RENDER , 有则创建对应的设备 dev/dri/enderD(128 - 192)
  3. 创建一个 DRM_MINOR_PRIMARY 子设备, 每个 drm_dev 必须有一个默认的 DRM_MINOR_PRIMARY 设备 dev/dri/card(0 - 64)
  4. 检测 driver 的 driver_features 标志位是否设置 DRIVER_GEM , 如果设置了则会为我们分配并创建一个默认的起始偏移地址为 DRM_FILE_PAGE_OFFSET_START内存大小为 DRM_FILE_PAGE_OFFSET_SIZE的 vma_offset_manager
  5. 将父设备名称用作 DRM 设备的唯一标识符 unique(drm dev的成员变量),没有父设备则使用驱动程序名称作为 unique 唯一标识符.
  • 小贴士: 这个接口仅仅创建并初始设备化并不会注册
int drm_dev_init(struct drm_device *dev,
         struct drm_driver *driver,
         struct device *parent)
{
    int ret;

    if (!drm_core_init_complete) {
        DRM_ERROR("DRM core is not initialized\n");
        return -ENODEV;
    }

    kref_init(&dev->ref);    // 设置 kref 引用计数
    dev->dev = parent;       // 设置父设备
    dev->driver = driver;    // 设置 driver

    INIT_LIST_HEAD(&dev->filelist);  /* 一系列链表的初始化 */ 
    INIT_LIST_HEAD(&dev->ctxlist);
    INIT_LIST_HEAD(&dev->vmalist);
    INIT_LIST_HEAD(&dev->maplist);
    INIT_LIST_HEAD(&dev->vblank_event_list);

    spin_lock_init(&dev->buf_lock);    /* 一系列锁的初始化 */
    spin_lock_init(&dev->event_lock);
    mutex_init(&dev->struct_mutex);
    mutex_init(&dev->filelist_mutex);
    mutex_init(&dev->ctxlist_mutex);
    mutex_init(&dev->master_mutex);

    dev->anon_inode = drm_fs_inode_new();
    if (IS_ERR(dev->anon_inode)) {
        ret = PTR_ERR(dev->anon_inode);
        DRM_ERROR("Cannot allocate anonymous inode: %d\n", ret);
        goto err_free;
    }

    // 检测 driver 的 driver_features 标志位是否设置 DRIVER_RENDER
    // 如果设置了调用 drm_minor_alloc 创建一个 DRIVER_RENDER 设备
    // dev/dri/enderD128 - dev/dri/enderD192
    if (drm_core_check_feature(dev, DRIVER_RENDER)) {
        ret = drm_minor_alloc(dev, DRM_MINOR_RENDER);
        if (ret)
            goto err_minors;
    }

    // 默认创建一个 DRM_MINOR_PRIMARY 子设备
    ret = drm_minor_alloc(dev, DRM_MINOR_PRIMARY);
    if (ret)
        goto err_minors;

    // 暂时不知道干啥的
    ret = drm_ht_create(&dev->map_hash, 12);
    if (ret)
        goto err_minors;

    drm_legacy_ctxbitmap_init(dev);

    // 如果驱动支持 DRIVER_GEM ,做 gem 相关初始化
    if (drm_core_check_feature(dev, DRIVER_GEM)) {
        ret = drm_gem_init(dev);
        if (ret) {
            DRM_ERROR("Cannot initialize graphics execution manager (GEM)\n");
            goto err_ctxbitmap;
        }
    }

    // 将父设备名称用作 DRM 设备的唯一标识符,但对于虚拟设备(例如 vgem),则使用驱动程序名称作为后备标识符
    ret = drm_dev_set_unique(dev, parent ? dev_name(parent) : driver->name);
    if (ret)
        goto err_setunique;

    return 0;

err_setunique:
    if (drm_core_check_feature(dev, DRIVER_GEM))
        drm_gem_destroy(dev);
err_ctxbitmap:
    drm_legacy_ctxbitmap_cleanup(dev);
    drm_ht_remove(&dev->map_hash);
err_minors:
    drm_minor_free(dev, DRM_MINOR_PRIMARY);
    drm_minor_free(dev, DRM_MINOR_RENDER);
    drm_minor_free(dev, DRM_MINOR_CONTROL);
    drm_fs_inode_free(dev->anon_inode);
err_free:
    mutex_destroy(&dev->master_mutex);
    mutex_destroy(&dev->ctxlist_mutex);
    mutex_destroy(&dev->filelist_mutex);
    mutex_destroy(&dev->struct_mutex);
    return ret;
}
EXPORT_SYMBOL(drm_dev_init);
1) drm_minor_alloc
  1. 根据传入的 type 在 drm_minors_idr 链表中申请一个可用的 id, 并且使用这个 id 作为次设备号, 创建对应的 minor 设备.
  • DRM_MINOR_PRIMARY id 范围 0 - 64 ==> dev/dri/card0 - dev/dri/card64
  • DRM_MINOR_CONTROL id 范围 64 - 128 ==> dev/dri/controlD64 - dev/dri/controlD128
  • DRM_MINOR_RENDER id 范围 128 - 192 ==> dev/dri/enderD128 - dev/dri/enderD192
static int drm_minor_alloc(struct drm_device *dev, unsigned int type)
{
    struct drm_minor *minor;
    unsigned long flags;
    int r;

    minor = kzalloc(sizeof(*minor), GFP_KERNEL);
    if (!minor)
        return -ENOMEM;

    /*
     * 总共有 3 种 type
     *   enum drm_minor_type {
     *       DRM_MINOR_PRIMARY, id 范围 0  - 64,  , 对应的设备节点 dev/dri/card0 - dev/dri/card64
     *       DRM_MINOR_CONTROL, id 范围 64 - 128  , 对应的设备节点 dev/dri/controlD64 - dev/dri/controlD128
     *       DRM_MINOR_RENDER,  id 范围 128 - 192 , 对应的设备节点 dev/dri/enderD128 - dev/dri/enderD192
     *   };
     */
    minor->type = type; // 设置 type 
    minor->dev = dev;   // 设置所属的 drm_dev

    idr_preload(GFP_KERNEL);
    spin_lock_irqsave(&drm_minor_lock, flags);
    
    // 根据设备类型,申请一个空 id 用来做此设备的次设备号,从这里可以得到 
    // DRM_MINOR_PRIMARY id 范围 0 - 64
    // DRM_MINOR_CONTROL id 范围 64 - 128
    // DRM_MINOR_RENDER  id 范围 128 - 192
    r = idr_alloc(&drm_minors_idr,
              NULL,
              64 * type,
              64 * (type + 1),
              GFP_NOWAIT);
    spin_unlock_irqrestore(&drm_minor_lock, flags);
    idr_preload_end();

    if (r < 0)
        goto err_free;

    // 设置申请到的 id 也是次设备号
    minor->index = r;
    
    // 根据前面申请到的次设备号, 为 minor 创建一个对应类型的 dev ,并初始化.
    minor->kdev = drm_sysfs_minor_alloc(minor);
    if (IS_ERR(minor->kdev)) {
        r = PTR_ERR(minor->kdev);
        goto err_index;
    }

    // 设置根据类型 type 将 minor 挂接到对应的位置.
    *drm_minor_get_slot(dev, type) = minor;
    return 0;

err_index:
    spin_lock_irqsave(&drm_minor_lock, flags);
    idr_remove(&drm_minors_idr, minor->index);
    spin_unlock_irqrestore(&drm_minor_lock, flags);
err_free:
    kfree(minor);
    return r;
}
2) drm_sysfs_minor_alloc
  1. 根据 drm_minor_alloc 申请的设备号创建对应名称的设备,可选的设备如下
  • DRM_MINOR_PRIMARY ==> card0 --> card64
  • DRM_MINOR_CONTROL ==> controlD64 --> controlD128
  • DRM_MINOR_RENDER ==> enderD128 --> enderD192
  1. 设置父设备为所属 drm_dev
  2. 设置私有数据为对应的 minor
  3. 设置设备所属设备类 drm_class ==> “/sys/class/drm”
struct device *drm_sysfs_minor_alloc(struct drm_minor *minor)
{
    const char *minor_str;
    struct device *kdev;
    int r;

    // 根据类型设置设备名称
    if (minor->type == DRM_MINOR_CONTROL)
        minor_str = "controlD%d";
    else if (minor->type == DRM_MINOR_RENDER)
        minor_str = "renderD%d";
    else
        minor_str = "card%d";

    // 创建设备结构
    kdev = kzalloc(sizeof(*kdev), GFP_KERNEL);
    if (!kdev)
        return ERR_PTR(-ENOMEM);

    // 设备初始化
    device_initialize(kdev);
    kdev->devt = MKDEV(DRM_MAJOR, minor->index); // 设置设备号
    kdev->class = drm_class;                     // 设置设备所属设备类
    kdev->type = &drm_sysfs_device_minor;        // 设置设备类型 drm_minor
    kdev->parent = minor->dev->dev;              // 设置父设备为所属 drm_dev
    kdev->release = drm_sysfs_release;           // 设置 release 回调函数
    dev_set_drvdata(kdev, minor);                // 设置私有数据为 minor

    // 设置设备名称
    r = dev_set_name(kdev, minor_str, minor->index);
    if (r < 0)
        goto err_free;

    return kdev;

err_free:
    put_device(kdev);
    return ERR_PTR(r);
}
3) drm_minor_get_slot

返回 type 对应的 dev 下的设备

static struct drm_minor **drm_minor_get_slot(struct drm_device *dev,
                         unsigned int type)
{
    switch (type) {
    case DRM_MINOR_PRIMARY:
        return &dev->primary;
    case DRM_MINOR_RENDER:
        return &dev->render;
    case DRM_MINOR_CONTROL:
        return &dev->control;
    default:
        return NULL;
    }
}
4) drm_gem_init
  1. 分配一个 vma_offset_manager 用来管理内存
  2. 初始化起始偏移地址为 DRM_FILE_PAGE_OFFSET_START, 内存大小为 DRM_FILE_PAGE_OFFSET_SIZE 的 vma_offset_manager
int drm_gem_init(struct drm_device *dev)
{
    struct drm_vma_offset_manager *vma_offset_manager;

    mutex_init(&dev->object_name_lock);
    idr_init_base(&dev->object_name_idr, 1);

    // 分配一个 vma_offset_manager 用来管理内存
    vma_offset_manager = kzalloc(sizeof(*vma_offset_manager), GFP_KERNEL);
    if (!vma_offset_manager) {
        DRM_ERROR("out of memory\n");
        return -ENOMEM;
    }

    // 初始化起始偏移地址为 DRM_FILE_PAGE_OFFSET_START, 内存大小为 DRM_FILE_PAGE_OFFSET_SIZE 的 vma_offset_manager
    dev->vma_offset_manager = vma_offset_manager;
    drm_vma_offset_manager_init(vma_offset_manager,
                    DRM_FILE_PAGE_OFFSET_START,
                    DRM_FILE_PAGE_OFFSET_SIZE);

    return 0;
}

2.2 drm_dev_register

  1. 注册 dev 下对应的 tpye 的设备, 创建对应设备的设备节点 dev/dri/cardx, dev/dri/controlDx, dev/dri/enderx 创建 /sys/kernel/debug/dri下的调试文件.

  2. 注册 dev 下的 planecrtcconnector 即回调对应的 xxxx->funcs->late_registe回调函数, 如果没设置也不会报错

  3. 注册 connector 设备到 drm_class即创建 /sys/class/drm/card%d(index)-%s(connector->name), 创建默认的属性文件 statusenabledpmsmodes, 回调 connector->funcs->late_registe最后调用 drm_mode_object_register以及设置 connector->registered标志位

int drm_dev_register(struct drm_device *dev, unsigned long flags)
{
    int ret;

    mutex_lock(&drm_global_mutex);

    // 注册 DRM_MINOR_CONTROL 设备
    ret = drm_minor_register(dev, DRM_MINOR_CONTROL);
    if (ret)
        goto err_minors;

    // 注册 DRM_MINOR_RENDER 设备
    ret = drm_minor_register(dev, DRM_MINOR_RENDER);
    if (ret)
        goto err_minors;

    // DRM_MINOR_PRIMARY
    ret = drm_minor_register(dev, DRM_MINOR_PRIMARY);
    if (ret)
        goto err_minors;

    // 设置 registered 标志位
    dev->registered = true;

    // 回调 dev->driver->load 接口
    if (dev->driver->load) {
        ret = dev->driver->load(dev, flags);
        if (ret)
            goto err_minors;
    }

    // 如果支持 DRIVER_MODESET
    if (drm_core_check_feature(dev, DRIVER_MODESET))
        drm_modeset_register_all(dev);

    ret = 0;
    goto out_unlock;

err_minors:
    drm_minor_unregister(dev, DRM_MINOR_PRIMARY);
    drm_minor_unregister(dev, DRM_MINOR_RENDER);
    drm_minor_unregister(dev, DRM_MINOR_CONTROL);
out_unlock:
    mutex_unlock(&drm_global_mutex);
    return ret;
}
1) drm_minor_register

真正的设备注册函数. 注册 dev 下对应的 tpye 的设备, 创建 /sys/kernel/debug/dri 下的调试文件. 没有对应的设备直接返回.

static int drm_minor_register(struct drm_device *dev, unsigned int type)
{
    struct drm_minor *minor;
    unsigned long flags;
    int ret;

    DRM_DEBUG("\n");

    // 获取对应的 type 的 drm_minor
    minor = *drm_minor_get_slot(dev, type);
    if (!minor)
        return 0;

    // 创建 /sys/kernel/debug/dri 下的调试文件
    ret = drm_debugfs_init(minor, minor->index, drm_debugfs_root);
    if (ret) {
        DRM_ERROR("DRM: Failed to initialize /sys/kernel/debug/dri.\n");
        goto err_debugfs;
    }

    // 注册设备, 即设备真正注册的地方
    ret = device_add(minor->kdev);
    if (ret)
        goto err_debugfs;

    /* replace NULL with @minor so lookups will succeed from now on */
    spin_lock_irqsave(&drm_minor_lock, flags);
    idr_replace(&drm_minors_idr, minor, minor->index);
    spin_unlock_irqrestore(&drm_minor_lock, flags);

    DRM_DEBUG("new minor registered %d\n", minor->index);
    return 0;

err_debugfs:
    drm_debugfs_cleanup(minor);
    return ret;
}
2) modeset_register_all
  1. 注册 dev 下的 planecrtc
    encoder 设备并调用对应的 xxxx->funcs->late_registe回调函数
  2. 注册 dev->mode_config.connector_list上所有的 encoder 创建默认属性节点 statusenabledpmsmodes并调用 connector->funcs->late_registe最后调用 drm_mode_object_register以及设置 connector->registered标志位
int drm_modeset_register_all(struct drm_device *dev)
{
    int ret;

    // 遍历 dev下所有的 plane 如果设置了plane->funcs->late_register 则调用
    ret = drm_plane_register_all(dev);
    if (ret)
        goto err_plane;

    // 遍历 dev 下所有的 crtc , 并回调 crtc->funcs->late_register();
    ret = drm_crtc_register_all(dev);
    if  (ret)
        goto err_crtc;

    // 遍历 dev 下所有的 encoder , 并回调 encoder->funcs->late_register();
    ret = drm_encoder_register_all(dev);
    if (ret)
        goto err_encoder;

    // 遍历 dev->mode_config.connector_list 上所有的 connector, 如果有未注册的, 则进程注册.
    // 1.  注册 connector 设备到 drm_class 即创建 /sys/class/drm/card%d(index)-%s(connector->name) 
    // 2.  创建默认的属性文件 status、enable、dpms、modes
    // 3.  如果设置了 connector->funcs->late_registe 则调用该函数. 
    ret = drm_connector_register_all(dev);
    if (ret)
        goto err_connector;

    return 0;

err_connector:
    drm_encoder_unregister_all(dev);
err_encoder:
    drm_crtc_unregister_all(dev);
err_crtc:
    drm_plane_unregister_all(dev);
err_plane:
    return ret;
}
drm_plane_register_all

遍历 dev 下所有的 plane , 并回调 plane->funcs->late_register();

int drm_plane_register_all(struct drm_device *dev)
{
    struct drm_plane *plane;
    int ret = 0;

    drm_for_each_plane(plane, dev) {
        if (plane->funcs->late_register)
            ret = plane->funcs->late_register(plane);
        if (ret)
            return ret;
    }

    return 0;
}
drm_crtc_register_all

遍历 dev 下所有的 crtc , 并回调 crtc->funcs->late_register();

static int drm_crtc_register_all(struct drm_device *dev)
{
    struct drm_crtc *crtc;
    int ret = 0;

    drm_for_each_crtc(crtc, dev) {
        if (crtc->funcs->late_register)
            ret = crtc->funcs->late_register(crtc);
        if (ret)
            return ret;
    }

    return 0;
}
drm_encoder_register_all

遍历 dev 下所有的 encoder , 并回调 encoder->funcs->late_register();

int drm_encoder_register_all(struct drm_device *dev)
{
    struct drm_encoder *encoder;
    int ret = 0;

    drm_for_each_encoder(encoder, dev) {
        if (encoder->funcs->late_register)
            ret = encoder->funcs->late_register(encoder);
        if (ret)
            return ret;
    }

    return 0;
}
drm_connector_register_all

遍历 dev->mode_config.connector_list 上所有的 connector, 如果有未注册的, 则进程注册.

  1. 注册 connector 设备到 drm_class 即创建 /sys/class/drm/card%d(index)-%s(connector->name)
  2. 创建默认的属性文件 status、enable、dpms、modes
  3. 如果设置了 connector->funcs->late_registe 则调用该函数.
int drm_connector_register_all(struct drm_device *dev)
{
    struct drm_connector *connector;
    int ret;

    /* FIXME: taking the mode config mutex ends up in a clash with
     * fbcon/backlight registration */
    list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
        ret = drm_connector_register(connector);
        if (ret)
            goto err;
    }

    return 0;

err:
    mutex_unlock(&dev->mode_config.mutex);
    drm_connector_unregister_all(dev);
    return ret;
}
drm_connector_register
  1. 注册 connector 设备到 drm_class 即 /sys/class/drm/card%d(index)-%s(connector->name) 创建默认的属性文件
  • /sys/class/drm/card%d-%s/status
  • /sys/class/drm/card%d-%s/enable
  • /sys/class/drm/card%d-%s/dpms
  • /sys/class/drm/card%d-%s/modes
  1. 创建 dbug 文件
  2. 调用 connector->funcs->late_registe
  3. drm_mode_object_register
  4. 设置 registered 标志位
int drm_connector_register(struct drm_connector *connector)
{
    int ret = 0;

    if (!connector->dev->registered)
        return 0;

    mutex_lock(&connector->mutex);
    if (connector->registered)
        goto unlock;

    // 注册 connector 设备到 drm_class 即 /sys/class/drm/card%d(index)-%s(connector->name)
    // 创建默认的属性文件
    // /sys/class/drm/card%d-%s/status
    // /sys/class/drm/card%d-%s/enabled
    // /sys/class/drm/card%d-%s/dpms
    // /sys/class/drm/card%d-%s/modes
    ret = drm_sysfs_connector_add(connector);
    if (ret)
        goto unlock;

    // 创建 dbug 文件
    ret = drm_debugfs_connector_add(connector);
    if (ret) {
        goto err_sysfs;
    }

    // 调用 connector->funcs->late_registe
    if (connector->funcs->late_register) {
        ret = connector->funcs->late_register(connector);
        if (ret)
            goto err_debugfs;
    }

    // drm_mode_object_register
    drm_mode_object_register(connector->dev, &connector->base);

    connector->registered = true;
    goto unlock;

err_debugfs:
    drm_debugfs_connector_remove(connector);
err_sysfs:
    drm_sysfs_connector_remove(connector);
unlock:
    mutex_unlock(&connector->mutex);
    return ret;
}
EXPORT_SYMBOL(drm_connector_register);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值