文章目录
作者: baron
drm 学习记录, 基础知识部分请参考何小龙的drm专题, 该系列博客非常详细的介绍了drm 的整个框架. 博主也是通过该系列文章入门, 只是有很多地方不是很理解, 于是参考内核源码进行一些补充. 文章内容引用会该专题部分内容.
drm 子系统的核心数据结构, linux 中的每个 drm 子系统都由一个 drm_device 统一进行描述. drm_device 的核心功能就是提供用户空间和内核中的 kms 进行交互. 他的本质是一个主设备号为 DRM_MAJOR 226
的字符设备, 每一个 drm_minor 都是一个次设备. 总共有三种次设备 primary、control、render.
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_init
和 drm_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/cardx
、dev/dri/controlx
、dev/dri/enderDx
, 用来和 kms 进行交互, 关系如下所示
当我们使用 open
、ioctl
、mmap
等函数接口最终会回调到 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 进行交互…
- 对应成员变量的简单初始化
- 检测 driver 的 driver_features 标志位是否设置 DRIVER_RENDER , 有则创建对应的设备 dev/dri/enderD(128 - 192)
- 创建一个 DRM_MINOR_PRIMARY 子设备, 每个 drm_dev 必须有一个默认的 DRM_MINOR_PRIMARY 设备 dev/dri/card(0 - 64)
- 检测 driver 的 driver_features 标志位是否设置 DRIVER_GEM , 如果设置了则会为我们分配并创建一个默认的起始偏移地址为
DRM_FILE_PAGE_OFFSET_START
内存大小为DRM_FILE_PAGE_OFFSET_SIZE
的 vma_offset_manager - 将父设备名称用作 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
- 根据传入的 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
- 根据 drm_minor_alloc 申请的设备号创建对应名称的设备,可选的设备如下
- DRM_MINOR_PRIMARY ==> card0 --> card64
- DRM_MINOR_CONTROL ==> controlD64 --> controlD128
- DRM_MINOR_RENDER ==> enderD128 --> enderD192
- 设置父设备为所属 drm_dev
- 设置私有数据为对应的 minor
- 设置设备所属设备类 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
- 分配一个 vma_offset_manager 用来管理内存
- 初始化起始偏移地址为 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
-
注册 dev 下对应的 tpye 的设备, 创建对应设备的设备节点 dev/dri/cardx, dev/dri/controlDx, dev/dri/enderx 创建
/sys/kernel/debug/dri
下的调试文件. -
注册 dev 下的 plane、crtc、connector 即回调对应的
xxxx->funcs->late_registe
回调函数, 如果没设置也不会报错 -
注册 connector 设备到
drm_class
即创建/sys/class/drm/card%d(index)-%s(connector->name)
, 创建默认的属性文件status
、enable
、dpms
、modes
, 回调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
- 注册 dev 下的 plane 、crtc
、encoder 设备并调用对应的xxxx->funcs->late_registe
回调函数 - 注册
dev->mode_config.connector_list
上所有的 encoder 创建默认属性节点status
、enable
、dpms
、modes
并调用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, 如果有未注册的, 则进程注册.
- 注册 connector 设备到 drm_class 即创建 /sys/class/drm/card%d(index)-%s(connector->name)
- 创建默认的属性文件 status、enable、dpms、modes
- 如果设置了 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
- 注册 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
- 创建 dbug 文件
- 调用 connector->funcs->late_registe
- drm_mode_object_register
- 设置 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);