本系列文章基于linux 5.15
一、drm_dev_alloc
用于分配并初始化一个新的 DRM 设备(即drm_device),初始化主要调用drm_dev_init函数
1.1drm_dev_init
drm_device的初始化操作,但是并不会注册,函数定义在drivers/gpu/drm/drm_drv.c
其主要的作用:
- 对应成员变量的简单初始化
- 检测 drm_driver 的 driver_features 标志位是否设置 DRIVER_RENDER , 有则创建对应的设备
dev/dri/enderD(128 - 192) - 创建一个 DRM_MINOR_PRIMARY 子设备, 每个 drm_device 必须有一个默认的 DRM_MINOR_PRIMARY 设备dev/dri/card(0 - 64)
- 检测drm_device的 drm_driver 的 driver_features 标志位是否设置 DRIVER_GEM ,如果设置了则会为我们分配并创建一个默认的起始偏移地址为 DRM_FILE_PAGE_OFFSET_START内存大小为 DRM_FILE_PAGE_OFFSET_SIZE的 vma_offset_manager
- 将父设备名称用作 DRM 设备的唯一标识符 unique(drm_device的成员变量),没有父设备则使用驱动程序名称作为 unique唯一标识符.
static int drm_dev_init(struct drm_device *dev,const struct drm_driver *driver,struct device *parent)
{int ret;if (!drm_core_init_complete) {DRM_ERROR("DRM core is not initialized\n");return -ENODEV;}if (WARN_ON(!parent))return -EINVAL;kref_init(&dev->ref);dev->dev = get_device(parent);dev->driver = driver;INIT_LIST_HEAD(&dev->managed.resources);spin_lock_init(&dev->managed.lock);/* no per-device feature limits by default */dev->driver_features = ~0u;drm_legacy_init_members(dev);INIT_LIST_HEAD(&dev->filelist);INIT_LIST_HEAD(&dev->filelist_internal);INIT_LIST_HEAD(&dev->clientlist);INIT_LIST_HEAD(&dev->vblank_event_list);spin_lock_init(&dev->event_lock);mutex_init(&dev->struct_mutex);mutex_init(&dev->filelist_mutex);mutex_init(&dev->clientlist_mutex);mutex_init(&dev->master_mutex);ret = drmm_add_action(dev, drm_dev_init_release, NULL);if (ret)return ret;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;}/*检测 drm_driver 的 driver_features 标志位是否设置 DRIVER_RENDER , 有则创建对应的设备dev/dri/enderD(128 - 192)*/if (drm_core_check_feature(dev, DRIVER_RENDER)) {ret = drm_minor_alloc(dev, DRM_MINOR_RENDER);if (ret)goto err;}/*创建一个 DRM_MINOR_PRIMARY 子设备, 每个 drm_device 必须有一个默认的 DRM_MINOR_PRIMARY 设备dev/dri/card(0 - 64)*/ret = drm_minor_alloc(dev, DRM_MINOR_PRIMARY);if (ret)goto err;ret = drm_legacy_create_map_hash(dev);if (ret)goto err;drm_legacy_ctxbitmap_init(dev);
/*检测drm_device的 drm_driver 的 driver_features 标志位是否设置 DRIVER_GEM ,
如果设置了则会为我们分配并创建一个默认的起始偏移地址为 DRM_FILE_PAGE_OFFSET_START内存大小为 DRM_FILE_PAGE_OFFSET_SIZE的 vma_offset_manager*/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;}}ret = drm_dev_set_unique(dev, dev_name(parent));if (ret)goto err;return 0;err:drm_managed_release(dev);return ret;
}
1.1.1drm_minor_alloc
根据传入的 type 在 drm_minors_idr 链表中申请一个可用的 id, 并且使用这个 id 作为次设备号, 创建对应的 minor 设备.
- DRM_MINOR_PRIMARY id 范围 0 - 64 ==> dev/dri/card0 - dev/dri/card64
- 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 = drmm_kzalloc(dev, sizeof(*minor), GFP_KERNEL);if (!minor)return -ENOMEM;minor->type = type;minor->dev = dev;idr_preload(GFP_KERNEL);spin_lock_irqsave(&drm_minor_lock, flags);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)return r;minor->index = r;r = drmm_add_action_or_reset(dev, drm_minor_alloc_release, minor);if (r)return r;minor->kdev = drm_sysfs_minor_alloc(minor);if (IS_ERR(minor->kdev))return PTR_ERR(minor->kdev);*drm_minor_get_slot(dev, type) = minor;return 0;
}
1.1.1.1drm_sysfs_minor_alloc
用于为 DRM minor 分配并初始化一个 struct device,其作用如下:
struct device *drm_sysfs_minor_alloc(struct drm_minor *minor)
{const char *minor_str;struct device *kdev;int r;
/*根据 minor->type 的值,选择对应的设备名称格式字符串: a.如果是渲染节点(DRM_MINOR_RENDER),则使用b. "renderD%d"。 如果是普通节点(DRM_MINOR_PRIMARY),则使用 "card%d"。*/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);/*通过 MKDEV 宏将主设备号 DRM_MAJOR 和次设备号 minor->index 组合成设备号*/kdev->devt = MKDEV(DRM_MAJOR, minor->index);kdev->class = drm_class;kdev->type = &drm_sysfs_device_minor;kdev->parent = minor->dev->dev;kdev->release = drm_sysfs_release;dev_set_drvdata(kdev, minor);/*使用 dev_set_name 设置设备名称,格式为 "renderD%d" 或 "card%d",其中 %d 是 minor->index。*/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);
}