本文主要是介绍drm 驱动系列- 第一章 drm_device,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
文章目录
- 1. 注册 drm_dev
- 1.1 connector 属性文件
- 2. 源码阅读
- 2.1 drm_dev_init
- 1) drm_minor_alloc
- 2) drm_sysfs_minor_alloc
- 3) drm_minor_get_slot
- 4) drm_gem_init
- 2.2 drm_dev_register
- 1) drm_minor_register
- 2) modeset_register_all
- drm_plane_register_all
- drm_crtc_register_all
- drm_encoder_register_all
- drm_connector_register_all
- drm_connector_register
作者: 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_RENDERstruct 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; // 设置 driverINIT_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/enderD192if (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_devidr_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 - 192r = 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";elseminor_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_minorkdev->parent = minor->dev->dev; // 设置父设备为所属 drm_devkdev->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_managerdev->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_PRIMARYret = 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_MODESETif (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_minorminor = *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/modesret = drm_sysfs_connector_add(connector);if (ret)goto unlock;// 创建 dbug 文件ret = drm_debugfs_connector_add(connector);if (ret) {goto err_sysfs;}// 调用 connector->funcs->late_registeif (connector->funcs->late_register) {ret = connector->funcs->late_register(connector);if (ret)goto err_debugfs;}// drm_mode_object_registerdrm_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);
这篇关于drm 驱动系列- 第一章 drm_device的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!