1.什么是radeon
简介
Radeon(中文名称称为镭龙™)是一个英文产品的商标。Radeon是AMD公司出品的显示芯片的一种系列。俗称A卡。
全称一般写作:AMD Radeon HD xxxx ,例如台式机显卡型号:AMD Radeon HD 6450。HD7000系列以后,AMD启用新的AMD Radeon R9/R7 xxx命名旗下新的显示芯片。
出产型号
AMD Radeon系列的台式机显卡有:
AMD Radeon RX 6000系列显卡
AMD Radeon RX 5000系列显卡
AMD Radeon VII 显卡
AMD Radeon RX 500系列显卡和Radeon RX Vega
AMD Radeon RX 400和Radeon 400系列显卡 [1]
AMD Radeon R9/R7 300 系列/FURY系列显卡 [2]
AMD Radeon R9/R7 200 系列显卡
AMDRadeon HD 8000 系列显卡(OEM专供)
AMD Radeon HD 7000 系列显卡
AMD Radeon HD 6000 系列显卡
ATI Radeon HD 5000 系列显卡
————————————————
原文链接:https://blog.csdn.net/phmatthaus/article/details/113841251
2.什么是drm
DRM
DRM,全称为Direct Rendering Manager。DRM是Linux目前主流的图形显示框架,相比FB架构,DRM更能适应当前日益更新的显示硬件。比如FB原生不支持多层合成,不支持VSYNC,不支持DMA-BUF,不支持异步更新,不支持fence机制等等,而这些功能DRM原生都支持。同时DRM可以统一管理GPU和Display驱动,使得软件架构更为统一,方便管理和维护。
DRM是一个内核级的设备驱动,既可以编译到内核中也可以作为标准模块进行加载。DRM最初是在FreeBSD中出现的,后来被移植到Linux系统中,并成为Linux系统的标准部分。
DRM从模块上划分,可以简单分为3部分:libdrm、KMS、GEM。
libdrm
对底层接口进行封装,向上层提供通用的API接口,主要是对各种IOCTL接口进行封装。
KMS
Kernel Mode Setting,所谓Mode setting。其实说白了就两件事:更新画面和设置显示参数。
更新画面:显示buffer的切换,多图层的合成方式,以及每个图层的显示位置。
设置显示参数:包括分辨率、刷新率、电源状态(休眠唤醒)等。
包含元素:FB,CRTC,ENCODER,CONNECTOR,PLANE,VBLANK,property。
GEM
Graphic Execution Manager,主要负责显示buffer的分配和释放,也是GPU唯一用到DRM的地方。
包含元素:DUMB,PRIME,fence。
- FB(FrameBuffer)(画布,帧缓冲/帧缓存)
对计算机来说,FrameBuffer 就是一块驱动和应用层都能访问的内存,是唯一一个和硬件无关的基本元素。当然画图之前要有一定的格式化,比方说我可以规定什么样的色彩模式(RGB24 , I420 , YUUV 等等),分辨率是多大等等。
- CRTC(绘图现场)
简写翻译过来是阴级摄像管上下文,在DRM 里 CRTC 就表示显示输出的上下文了。对显示buffer进行扫描,并产生时序信号的硬件模块,通常指Display Controller。首先 CRTC 内指一个 FrameBuffer 地址,外连一个Encoder。
它们俩之间如何沟通?这就是显示模式(ModeSet)要做的事情。ModeSet 包括了像前面提到的色彩模式,还有说显示的时序(timings , ModeLines 等都代表了这个意思)等,通常时序可以按以下来表达:
PCLK HFP HBP HSWX_RES VFP VBP VSWY_RES
像素时钟 水平前回扫 水平后回扫 水平同步头 水平有效长度 垂直前回扫 垂直后回扫 垂直同步头 垂直有效长度
一个CRTC 可以连接多个 Encoder ,实现复制屏幕功能。
- Encoder(输出转换器)
负责将CRTC输出的timing时序转换成外部设备所需要的信号的模块,如HDMI转换器或DSI Controller。
显卡可以连接各种不同的设备,显然输出需要不同的信号转换器,将内存的像素转换成显示器需要的信号(DVID , VGA , YPbPr , CVBS 等等……)。
- Connector(连接器 )
连接物理显示设备的连接器,如HDMI、DisplayPort、DSI总线,通常和Encoder驱动绑定在一起。
不是指物理线,回到DRM,这是一个抽象的数据结构 ,代表连接的显示设备,从这里我们可以得到设备的EDID、DPMS连接状态等。
- PLANE(硬件图层)
有的Display硬件支持多层合成显示,但所有的Display Controller至少要有1个plane。
- VBLANK (垂直消隐,场消隐)
软件和硬件的同步机制,RGB时序中的垂直消影区,软件通常使用硬件VSYNC来实现。
- property (属性)
任何你想设置的参数,都可以做成property,是DRM驱动中最灵活、最方便的Mode setting机制。
- DUMB
只支持连续物理内存,基于kernel中通用CMA API实现,多用于小分辨率简单场景。
- PRIME
连续、非连续物理内存都支持,基于DMA-BUF机制,可以实现buffer共享,多用于大内存复杂场景。
- fence
buffer同步机制,基于内核dma_fence机制实现,用于防止显示内容出现异步问题。
————————————————
原文链接:https://blog.csdn.net/phmatthaus/article/details/113841377
3.Radeon初始化
解析DRM代码,以从底层介绍显卡驱动的初始化过程,显卡类型是AMD的radeon r600以后的系列显卡。基本的过程就是驱动载入,硬件初始化,设置硬件独立的模块(如内存管理器),设置显示(分辨率等)。 代码如下:
drivers/gpu/drm/radeon/radeon_drv.c
module_init(radeon_init);
module_exit(radeon_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL and additional rights");
static int __init radeon_init(void)
{
if (vgacon_text_force() && radeon_modeset == -1) {
DRM_INFO("VGACON disable radeon kernel modesetting.\n");
radeon_modeset = 0;
}
/* set to modesetting by default if not nomodeset */
if (radeon_modeset == -1)
radeon_modeset = 1;
if (radeon_modeset == 1) {
DRM_INFO("radeon kernel modesetting enabled.\n");
driver = &kms_driver;
pdriver = &radeon_kms_pci_driver;
driver->driver_features |= DRIVER_MODESET;
driver->num_ioctls = radeon_max_kms_ioctl;
radeon_register_atpx_handler();
} else {
DRM_ERROR("No UMS support in radeon module!\n");
return -EINVAL;
}
return pci_register_driver(pdriver);
}
static void __exit radeon_exit(void)
{
pci_unregister_driver(pdriver);
radeon_unregister_atpx_handler();
}
重点说一下这行代码:driver->driver_features |= DRIVER_MODESET;。driver指针指向了&kms_driver,这一句的意思是给kms_driver.driver_features添加上DRIVER_MODESET标志位,以告知DRM Coce当前驱动支持modesetting操作。
radeon_init函数中调用pci_register_driver(pdriver)注册pdriver,pdriver实际指向了radeon_kms_pci_driver,初始化内容如下:
static struct drm_driver *driver;
static struct pci_driver *pdriver;
static struct pci_driver radeon_kms_pci_driver = {
.name = DRIVER_NAME,
.id_table = pciidlist,
.probe = radeon_pci_probe,
.remove = radeon_pci_remove,
.shutdown = radeon_pci_shutdown,
.driver.pm = &radeon_pm_ops,
};
驱动和设备匹配之后调用radeon_pci_probe,源码如下:
static int radeon_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
unsigned long flags = 0;
int ret;
if (!ent)
return -ENODEV; /* Avoid NULL-ptr deref in drm_get_pci_dev */
flags = ent->driver_data;
if (!radeon_si_support) {
switch (flags & RADEON_FAMILY_MASK) {
case CHIP_TAHITI:
case CHIP_PITCAIRN:
case CHIP_VERDE:
case CHIP_OLAND:
case CHIP_HAINAN:
dev_info(&pdev->dev,
"SI support disabled by module param\n");
return -ENODEV;
}
}
if (!radeon_cik_support) {
switch (flags & RADEON_FAMILY_MASK) {
case CHIP_KAVERI:
case CHIP_BONAIRE:
case CHIP_HAWAII:
case CHIP_KABINI:
case CHIP_MULLINS:
dev_info(&pdev->dev,
"CIK support disabled by module param\n");
return -ENODEV;
}
}
if (vga_switcheroo_client_probe_defer(pdev))
return -EPROBE_DEFER;
/* Get rid of things like offb */
ret = radeon_kick_out_firmware_fb(pdev);
if (ret)
return ret;
return drm_get_pci_dev(pdev, ent, &kms_driver);
}
radeon_si_support和radeon_cik_support由启动参数得来,在/etc/default/grub文件中的GRUB_CMDLINE_LINUX_DEFAULT=一行可以添加诸如"modprobe.blacklist=radeon radeon.si_support=1 radeon.cik_support=1"的语句,其中的si_support和cik_support实际上就是设置了这里的radeon_si_support和radeon_cik_support。两个模块参数的初始化代码如下:
int radeon_si_support = 1;
MODULE_PARM_DESC(si_support, "SI support (1 = enabled (default), 0 = disabled)");
module_param_named(si_support, radeon_si_support, int, 0444);
int radeon_cik_support = 1;
MODULE_PARM_DESC(cik_support, "CIK support (1 = enabled (default), 0 = disabled)");
module_param_named(cik_support, radeon_cik_support, int, 0444);
radeon_pci_probe函数最后调用drm_get_pci_dev(pdev, ent, &kms_driver),kms_driver的初始化内容如下:
static struct drm_driver kms_driver = {
.driver_features =
DRIVER_USE_AGP |
DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM |
DRIVER_PRIME | DRIVER_RENDER,
.load = radeon_driver_load_kms,
.open = radeon_driver_open_kms,
.postclose = radeon_driver_postclose_kms,
.lastclose = radeon_driver_lastclose_kms,
.unload = radeon_driver_unload_kms,
.get_vblank_counter = radeon_get_vblank_counter_kms,
.enable_vblank = radeon_enable_vblank_kms,
.disable_vblank = radeon_disable_vblank_kms,
.get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos,
.get_scanout_position = radeon_get_crtc_scanout_position,
.irq_preinstall = radeon_driver_irq_preinstall_kms,
.irq_postinstall = radeon_driver_irq_postinstall_kms,
.irq_uninstall = radeon_driver_irq_uninstall_kms,
.irq_handler = radeon_driver_irq_handler_kms,
.ioctls = radeon_ioctls_kms,
.gem_free_object_unlocked = radeon_gem_object_free,
.gem_open_object = radeon_gem_object_open,
.gem_close_object = radeon_gem_object_close,
.dumb_create = radeon_mode_dumb_create,
.dumb_map_offset = radeon_mode_dumb_mmap,
.fops = &radeon_driver_kms_fops,
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
.gem_prime_export = radeon_gem_prime_export,
.gem_prime_import = drm_gem_prime_import,
.gem_prime_pin = radeon_gem_prime_pin,
.gem_prime_unpin = radeon_gem_prime_unpin,
.gem_prime_res_obj = radeon_gem_prime_res_obj,
.gem_prime_get_sg_table = radeon_gem_prime_get_sg_table,
.gem_prime_import_sg_table = radeon_gem_prime_import_sg_table,
.gem_prime_vmap = radeon_gem_prime_vmap,
.gem_prime_vunmap = radeon_gem_prime_vunmap,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.date = DRIVER_DATE,
.major = KMS_DRIVER_MAJOR,
.minor = KMS_DRIVER_MINOR,
.patchlevel = KMS_DRIVER_PATCHLEVEL,
};
drm_get_pci_dev函数代码如下:
drivers/gpu/drm/drm_pci.c
/**
* drm_get_pci_dev - Register a PCI device with the DRM subsystem
* @pdev: PCI device
* @ent: entry from the PCI ID table that matches @pdev
* @driver: DRM device driver
*
* Attempt to gets inter module "drm" information. If we are first
* then register the character device and inter module information.
* Try and register, if we fail to register, backout previous work.
*
* NOTE: This function is deprecated, please use drm_dev_alloc() and
* drm_dev_register() instead and remove your &drm_driver.load callback.
*
* Return: 0 on success or a negative error code on failure.
*/
int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
struct drm_driver *driver)
{
struct drm_device *dev;
int ret;
DRM_DEBUG("\n");
dev = drm_dev_alloc(driver, &pdev->dev);
if (IS_ERR(dev))
return PTR_ERR(dev);
ret = pci_enable_device(pdev);
if (ret)
goto err_free;
dev->pdev = pdev;
#ifdef __alpha__
dev->hose = pdev->sysdata;
#endif
if (drm_core_check_feature(dev, DRIVER_MODESET))
pci_set_drvdata(pdev, dev);
drm_pci_agp_init(dev);
ret = drm_dev_register(dev, ent->driver_data);
if (ret)
goto err_agp;
/* No locking needed since shadow-attach is single-threaded since it may
* only be called from the per-driver module init hook. */
if (drm_core_check_feature(dev, DRIVER_LEGACY))
list_add_tail(&dev->legacy_dev_list, &driver->legacy_dev_list);
return 0;
err_agp:
drm_pci_agp_destroy(dev);
pci_disable_device(pdev);
err_free:
drm_dev_put(dev);
return ret;
}
EXPORT_SYMBOL(drm_get_pci_dev);
这是一个比较关键的函数,重点讲一下。不过这一篇的内容比较多了,留在下一篇详细说明吧。
————————————————
原文链接:https://blog.csdn.net/phmatthaus/article/details/113849206
4.drm_get_pci_dev函数详解1
上一篇讲到了drm_get_pci_dev函数,这是一个比较关键的函数,其中调用了几个函数,现在一一进行详细说明。再把函数的完整代码贴出来,在drivers/gpu/drm/drm_pci.c中:
/**
* drm_get_pci_dev - Register a PCI device with the DRM subsystem
* @pdev: PCI device
* @ent: entry from the PCI ID table that matches @pdev
* @driver: DRM device driver
*
* Attempt to gets inter module "drm" information. If we are first
* then register the character device and inter module information.
* Try and register, if we fail to register, backout previous work.
*
* NOTE: This function is deprecated, please use drm_dev_alloc() and
* drm_dev_register() instead and remove your &drm_driver.load callback.
*
* Return: 0 on success or a negative error code on failure.
*/
int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
struct drm_driver *driver)
{
struct drm_device *dev;
int ret;
DRM_DEBUG("\n");
dev = drm_dev_alloc(driver, &pdev->dev);
if (IS_ERR(dev))
return PTR_ERR(dev);
ret = pci_enable_device(pdev);
if (ret)
goto err_free;
dev->pdev = pdev;
#ifdef __alpha__
dev->hose = pdev->sysdata;
#endif
if (drm_core_check_feature(dev, DRIVER_MODESET))
pci_set_drvdata(pdev, dev);
drm_pci_agp_init(dev);
ret = drm_dev_register(dev, ent->driver_data);
if (ret)
goto err_agp;
/* No locking needed since shadow-attach is single-threaded since it may
* only be called from the per-driver module init hook. */
if (drm_core_check_feature(dev, DRIVER_LEGACY))
list_add_tail(&dev->legacy_dev_list, &driver->legacy_dev_list);
return 0;
err_agp:
drm_pci_agp_destroy(dev);
pci_disable_device(pdev);
err_free:
drm_dev_put(dev);
return ret;
}
EXPORT_SYMBOL(drm_get_pci_dev);
先说第1个函数:drm_dev_alloc。
调用处:dev = drm_dev_alloc(driver, &pdev->dev);
源码如下:
drivers/gpu/drm/drm_drv.c
/**
* drm_dev_alloc - Allocate new DRM device
* @driver: DRM driver to allocate device for
* @parent: Parent device object
*
* Allocate and initialize a new DRM device. No device registration is done.
* Call drm_dev_register() to advertice the device to user space and register it
* with other core subsystems. This should be done last in the device
* initialization sequence to make sure userspace can't access an inconsistent
* state.
*
* The initial ref-count of the object is 1. Use drm_dev_get() and
* drm_dev_put() to take and drop further ref-counts.
*
* Note that for purely virtual devices @parent can be NULL.
*
* Drivers that wish to subclass or embed &struct drm_device into their
* own struct should look at using drm_dev_init() instead.
*
* RETURNS:
* Pointer to new DRM device, or ERR_PTR on failure.
*/
struct drm_device *drm_dev_alloc(struct drm_driver *driver,
struct device *parent)
{
struct drm_device *dev;
int ret;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return ERR_PTR(-ENOMEM);
ret = drm_dev_init(dev, driver, parent);
if (ret) {
kfree(dev);
return ERR_PTR(ret);
}
return dev;
}
EXPORT_SYMBOL(drm_dev_alloc);
这个函数的参数struct drm_driver *driver其实就是指向了kms_driver。
函数完成的事情也挺好理解,就是先分配了一个struct drm_device 结构,并使用dev指向这个结构,其实就是新建了一个drm设备。
接下来调用drm_dev_init函数对于这个新建的设备进行初始化。代码如下:
drivers/gpu/drm/drm_drv.c:
/**
* drm_dev_init - Initialise new DRM device
* @dev: DRM device
* @driver: DRM driver
* @parent: Parent device object
*
* Initialize a new DRM device. No device registration is done.
* Call drm_dev_register() to advertice the device to user space and register it
* with other core subsystems. This should be done last in the device
* initialization sequence to make sure userspace can't access an inconsistent
* state.
*
* The initial ref-count of the object is 1. Use drm_dev_get() and
* drm_dev_put() to take and drop further ref-counts.
*
* Note that for purely virtual devices @parent can be NULL.
*
* Drivers that do not want to allocate their own device struct
* embedding &struct drm_device can call drm_dev_alloc() instead. For drivers
* that do embed &struct drm_device it must be placed first in the overall
* structure, and the overall structure must be allocated using kmalloc(): The
* drm core's release function unconditionally calls kfree() on the @dev pointer
* when the final reference is released. To override this behaviour, and so
* allow embedding of the drm_device inside the driver's device struct at an
* arbitrary offset, you must supply a &drm_driver.release callback and control
* the finalization explicitly.
*
* RETURNS:
* 0 on success, or error code on failure.
*/
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);
dev->dev = get_device(parent);
dev->driver = driver;
INIT_LIST_HEAD(&dev->filelist);
INIT_LIST_HEAD(&dev->filelist_internal);
INIT_LIST_HEAD(&dev->clientlist);
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->clientlist_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;
}
if (drm_core_check_feature(dev, DRIVER_RENDER)) {
ret = drm_minor_alloc(dev, DRM_MINOR_RENDER);
if (ret)
goto err_minors;
}
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);
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;
}
}
/* Use the parent device name as DRM device unique identifier, but fall
* back to the driver name for virtual devices like 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_fs_inode_free(dev->anon_inode);
err_free:
put_device(dev->dev);
mutex_destroy(&dev->master_mutex);
mutex_destroy(&dev->ctxlist_mutex);
mutex_destroy(&dev->clientlist_mutex);
mutex_destroy(&dev->filelist_mutex);
mutex_destroy(&dev->struct_mutex);
return ret;
}
EXPORT_SYMBOL(drm_dev_init);
函数说明已经说得很清楚了,drm_dev_init函数初始化了一个新的DRM设备。其实就是给上边新分配的struct drm_device *dev赋值。
传入的参数在这两句程序中用到:
dev->dev = get_device(parent);
dev->driver = driver;
其余的注意事项在函数注释中也已经说得很清楚了,直接看函数注释就好,这里就不再翻译成中文了。
通过drm_dev_alloc函数中的drm_dev_init函数和drm_get_pci_dev 函数中的drm_dev_register函数,DRM框架为我们做了如下事情:
创建设备节点:/dev/dri/card0
创建sysfs节点:/sys/class/drm/card0
创建debugfs节点:/sys/kernel/debug/dri/0
————————————————
原文链接:https://blog.csdn.net/phmatthaus/article/details/113863698
5.drm_get_pci_dev函数详解2
上一篇讲到了drm_get_pci_dev函数的第1个函数:drm_dev_alloc。
不知读者还有没有印象,前一篇文章中讲drm_get_pci_dev函数的第1个函数drm_dev_alloc的时候,在函数注释中有这样一段话:
/*******************************************************************************************
* Initialize a new DRM device. No device registration is done.
* Call drm_dev_register() to advertice the device to user space and register it
* with other core subsystems. This should be done last in the device
* initialization sequence to make sure userspace can't access an inconsistent
* state.
********************************************************************************************/
注释的大致意思是说,注册DRM设备但是没有初始化,需要在初始化的最后调用drm_dev_register()进行注册。
这一篇就来讲这个drm_get_pci_dev函数的第2个函数:drm_dev_register。
调用处:ret = drm_dev_register(dev, ent->driver_data);
源码如下:
drivers/gpu/drm/drm_drv.c
/**
* drm_dev_register - Register DRM device
* @dev: Device to register
* @flags: Flags passed to the driver's .load() function
*
* Register the DRM device @dev with the system, advertise device to user-space
* and start normal device operation. @dev must be allocated via drm_dev_alloc()
* previously.
*
* Never call this twice on any device!
*
* NOTE: To ensure backward compatibility with existing drivers method this
* function calls the &drm_driver.load method after registering the device
* nodes, creating race conditions. Usage of the &drm_driver.load methods is
* therefore deprecated, drivers must perform all initialization before calling
* drm_dev_register().
*
* RETURNS:
* 0 on success, negative error code on failure.
*/
int drm_dev_register(struct drm_device *dev, unsigned long flags)
{
struct drm_driver *driver = dev->driver;
int ret;
mutex_lock(&drm_global_mutex);
ret = drm_minor_register(dev, DRM_MINOR_RENDER);
if (ret)
goto err_minors;
ret = drm_minor_register(dev, DRM_MINOR_PRIMARY);
if (ret)
goto err_minors;
ret = create_compat_control_link(dev);
if (ret)
goto err_minors;
dev->registered = true;
if (dev->driver->load) {
ret = dev->driver->load(dev, flags);
if (ret)
goto err_minors;
}
if (drm_core_check_feature(dev, DRIVER_MODESET))
drm_modeset_register_all(dev);
ret = 0;
DRM_INFO("Initialized %s %d.%d.%d %s for %s on minor %d\n",
driver->name, driver->major, driver->minor,
driver->patchlevel, driver->date,
dev->dev ? dev_name(dev->dev) : "virtual device",
dev->primary->index);
goto out_unlock;
err_minors:
remove_compat_control_link(dev);
drm_minor_unregister(dev, DRM_MINOR_PRIMARY);
drm_minor_unregister(dev, DRM_MINOR_RENDER);
out_unlock:
mutex_unlock(&drm_global_mutex);
return ret;
}
EXPORT_SYMBOL(drm_dev_register);
重点讲一下这一段程序:
if (dev->driver->load) {
ret = dev->driver->load(dev, flags);
if (ret)
goto err_minors;
}
dev就是drm_dev_alloc函数中新分配并初始化的那个dev。dev->driver就是前一篇文章中最后提到的dev->driver = driver;,也就是说dev->driver实际上指向了kms_driver。那么dev->driver->load不言而喻,当然就指向struct drm_driver kms_driver 中的
.load = radeon_driver_load_kms,
这个函数是所有和GPU初始化相关的内容的起始点 。下一篇我们重点介绍。
在此顺便列出
DRM_INFO("Initialized %s %d.%d.%d %s for %s on minor %d\n",
driver->name, driver->major, driver->minor,
driver->patchlevel, driver->date,
dev->dev ? dev_name(dev->dev) : "virtual device",
dev->primary->index);
中各个成员的值:
driver->name为kms_driver中的.name = DRIVER_NAME,DRIVER_NAME宏定义的值根据不同的驱动而不同,在radeon驱动中为如下定义(drivers/gpu/drm/radeon/radeon_drv.h中):
#define DRIVER_NAME "radeon"
如果是在amdgpu驱动中,则为(drivers/gpu/drm/amd/amdgpu/amdgpu_drv.h中):
#define DRIVER_NAME "amdgpu"
如果是在nouveau驱动中,则为(drivers/gpu/drm/nouveau/nouveau_drv.h中):
#define DRIVER_NAME "nouveau"
如果是在loongson驱动中,则为(drivers/gpu/drm/loongson/loongson_drv.c中):
#define DRIVER_NAME "loongson-drm"
driver->major为kms_driver中的.major = KMS_DRIVER_MAJOR,KMS_DRIVER_MAJOR宏定义的值同样根据不同的驱动而不同,在radeon驱动中为如下定义(drivers/gpu/drm/radeon/radeon_drv.c中):
#define KMS_DRIVER_MAJOR 2
如果是在amdgpu驱动中,则为(drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c中):
#define KMS_DRIVER_MAJOR 3
注意,这个宏定义只在radeon和amdgpu驱动中有,其他驱动中无此宏定义。
driver->minor为kms_driver中的.minor = KMS_DRIVER_MINOR,KMS_DRIVER_MINOR宏定义的值同样根据不同的驱动而不同,在radeon驱动中为如下定义(drivers/gpu/drm/radeon/radeon_drv.c中):
#define KMS_DRIVER_MINOR 50
如果是在amdgpu驱动中,则为(drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c中):
#define KMS_DRIVER_MINOR 27
注意,这个宏定义同样只在radeon和amdgpu驱动中有,其他驱动中无此宏定义。
driver->patch_level为kms_driver中的.patchlevel = KMS_DRIVER_PATCHLEVEL,KMS_DRIVER_PATCHLEVEL宏定义只存在于radeon驱动(drivers/gpu/drm/radeon/radeon_drv.c)和amdgpu驱动(drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c)中,且值相同:
#define KMS_DRIVER_PATCHLEVEL 0
driver->date为kms_driver中的.date = DRIVER_DATE,DRIVER_DATE宏定义的值根据不同的驱动而不同,在radeon驱动中为如下定义(drivers/gpu/drm/radeon/radeon_drv.h中):
#define DRIVER_DATE "20080528"
如果是在amdgpu驱动中,则为(drivers/gpu/drm/amd/amdgpu/amdgpu_drv.h中):
#define DRIVER_DATE "20150101"
如果是在nouveau驱动中,则为(drivers/gpu/drm/nouveau/nouveau_drv.h中):
#define DRIVER_DATE "20120801"
如果是在loongson驱动中,则为(drivers/gpu/drm/loongson/loongson_drv.c中):
#define DRIVER_DATE "20180328"
————————————————
原文链接:https://blog.csdn.net/phmatthaus/article/details/113865835
6.radeon_driver_load_kms函数详解1
上一篇引出了radeon_driver_load_kms这个函数,这篇文章开始详细解析这个函数。
radeon_driver_load_kms函数是所有和GPU初始化相关的内容的起始点,调用radeon_device_init()来初始化芯片的非显示部分(asic init, CP, writeback等),调用radeon_modeset_init()来初始化显示部分(CRTC、connector、encoder、hotplug detect等)。 源码如下:
drivers/gpu/drm/radeon/radeon_kms.c
/**
* radeon_driver_load_kms - Main load function for KMS.
*
* @dev: drm dev pointer
* @flags: device flags
*
* This is the main load function for KMS (all asics).
* It calls radeon_device_init() to set up the non-display
* parts of the chip (asic init, CP, writeback, etc.), and
* radeon_modeset_init() to set up the display parts
* (crtcs, encoders, hotplug detect, etc.).
* Returns 0 on success, error on failure.
*/
int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags)
{
struct radeon_device *rdev;
int r, acpi_status;
#ifdef CONFIG_CPU_LOONGSON3
turn_off_lvds();
#endif
rdev = kzalloc(sizeof(struct radeon_device), GFP_KERNEL);
if (rdev == NULL) {
return -ENOMEM;
}
dev->dev_private = (void *)rdev;
/* update BUS flag */
if (pci_find_capability(dev->pdev, PCI_CAP_ID_AGP)) {
flags |= RADEON_IS_AGP;
} else if (pci_is_pcie(dev->pdev)) {
flags |= RADEON_IS_PCIE;
} else {
flags |= RADEON_IS_PCI;
}
if ((radeon_runtime_pm != 0) &&
radeon_has_atpx() &&
((flags & RADEON_IS_IGP) == 0) &&
!pci_is_thunderbolt_attached(dev->pdev))
flags |= RADEON_IS_PX;
/* radeon_device_init should report only fatal error
* like memory allocation failure or iomapping failure,
* or memory manager initialization failure, it must
* properly initialize the GPU MC controller and permit
* VRAM allocation
*/
r = radeon_device_init(rdev, dev, dev->pdev, flags);
if (r) {
dev_err(&dev->pdev->dev, "Fatal error during GPU init\n");
goto out;
}
/* Again modeset_init should fail only on fatal error
* otherwise it should provide enough functionalities
* for shadowfb to run
*/
r = radeon_modeset_init(rdev);
if (r)
dev_err(&dev->pdev->dev, "Fatal error during modeset init\n");
#ifdef CONFIG_CPU_LOONGSON3
turn_on_lvds();
#endif
/* Call ACPI methods: require modeset init
* but failure is not fatal
*/
if (!r) {
acpi_status = radeon_acpi_init(rdev);
if (acpi_status)
dev_dbg(&dev->pdev->dev,
"Error during ACPI methods call\n");
}
if (radeon_is_px(dev)) {
dev_pm_set_driver_flags(dev->dev, DPM_FLAG_NEVER_SKIP);
pm_runtime_use_autosuspend(dev->dev);
pm_runtime_set_autosuspend_delay(dev->dev, 5000);
pm_runtime_set_active(dev->dev);
pm_runtime_allow(dev->dev);
pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
}
out:
if (r)
radeon_driver_unload_kms(dev);
return r;
}
函数一开始先分配struct radeon_device结构体实例,即新建了一个struct radeon_device 设备。
struct radeon_device的定义如下:
drivers/gpu/drm/radeon/radeon.h
/*
* Core structure, functions and helpers.
*/
typedef uint32_t (*radeon_rreg_t)(struct radeon_device*, uint32_t);
typedef void (*radeon_wreg_t)(struct radeon_device*, uint32_t, uint32_t);
struct radeon_device {
struct device *dev;
struct drm_device *ddev;
struct pci_dev *pdev;
struct rw_semaphore exclusive_lock;
/* ASIC */
union radeon_asic_config config;
enum radeon_family family;
unsigned long flags;
int usec_timeout;
enum radeon_pll_errata pll_errata;
int num_gb_pipes;
int num_z_pipes;
int disp_priority;
/* BIOS */
uint8_t *bios;
bool is_atom_bios;
uint16_t bios_header_start;
struct radeon_bo *stolen_vga_memory;
/* Register mmio */
resource_size_t rmmio_base;
resource_size_t rmmio_size;
/* protects concurrent MM_INDEX/DATA based register access */
spinlock_t mmio_idx_lock;
/* protects concurrent SMC based register access */
spinlock_t smc_idx_lock;
/* protects concurrent PLL register access */
spinlock_t pll_idx_lock;
/* protects concurrent MC register access */
spinlock_t mc_idx_lock;
/* protects concurrent PCIE register access */
spinlock_t pcie_idx_lock;
/* protects concurrent PCIE_PORT register access */
spinlock_t pciep_idx_lock;
/* protects concurrent PIF register access */
spinlock_t pif_idx_lock;
/* protects concurrent CG register access */
spinlock_t cg_idx_lock;
/* protects concurrent UVD register access */
spinlock_t uvd_idx_lock;
/* protects concurrent RCU register access */
spinlock_t rcu_idx_lock;
/* protects concurrent DIDT register access */
spinlock_t didt_idx_lock;
/* protects concurrent ENDPOINT (audio) register access */
spinlock_t end_idx_lock;
void __iomem *rmmio;
radeon_rreg_t mc_rreg;
radeon_wreg_t mc_wreg;
radeon_rreg_t pll_rreg;
radeon_wreg_t pll_wreg;
uint32_t pcie_reg_mask;
radeon_rreg_t pciep_rreg;
radeon_wreg_t pciep_wreg;
/* io port */
void __iomem *rio_mem;
resource_size_t rio_mem_size;
struct radeon_clock clock;
struct radeon_mc mc;
struct radeon_gart gart;
struct radeon_mode_info mode_info;
struct radeon_scratch scratch;
struct radeon_doorbell doorbell;
struct radeon_mman mman;
struct radeon_fence_driver fence_drv[RADEON_NUM_RINGS];
wait_queue_head_t fence_queue;
u64 fence_context;
struct mutex ring_lock;
struct radeon_ring ring[RADEON_NUM_RINGS];
bool ib_pool_ready;
struct radeon_sa_manager ring_tmp_bo;
struct radeon_irq irq;
struct radeon_asic *asic;
struct radeon_gem gem;
struct radeon_pm pm;
struct radeon_uvd uvd;
struct radeon_vce vce;
uint32_t bios_scratch[RADEON_BIOS_NUM_SCRATCH];
struct radeon_wb wb;
struct radeon_dummy_page dummy_page;
bool shutdown;
bool need_dma32;
bool need_swiotlb;
bool accel_working;
bool fastfb_working; /* IGP feature*/
bool needs_reset, in_reset;
struct radeon_surface_reg surface_regs[RADEON_GEM_MAX_SURFACES];
const struct firmware *me_fw; /* all family ME firmware */
const struct firmware *pfp_fw; /* r6/700 PFP firmware */
const struct firmware *rlc_fw; /* r6/700 RLC firmware */
const struct firmware *mc_fw; /* NI MC firmware */
const struct firmware *ce_fw; /* SI CE firmware */
const struct firmware *mec_fw; /* CIK MEC firmware */
const struct firmware *mec2_fw; /* KV MEC2 firmware */
const struct firmware *sdma_fw; /* CIK SDMA firmware */
const struct firmware *smc_fw; /* SMC firmware */
const struct firmware *uvd_fw; /* UVD firmware */
const struct firmware *vce_fw; /* VCE firmware */
bool new_fw;
struct r600_vram_scratch vram_scratch;
int msi_enabled; /* msi enabled */
struct r600_ih ih; /* r6/700 interrupt ring */
struct radeon_rlc rlc;
struct radeon_mec mec;
struct delayed_work hotplug_work;
struct work_struct dp_work;
struct work_struct audio_work;
int need_recover;
struct delayed_work recover_work;
int num_crtc; /* number of crtcs */
struct mutex dc_hw_i2c_mutex; /* display controller hw i2c mutex */
bool has_uvd;
bool has_vce;
struct r600_audio audio; /* audio stuff */
struct notifier_block acpi_nb;
/* only one userspace can use Hyperz features or CMASK at a time */
struct drm_file *hyperz_filp;
struct drm_file *cmask_filp;
/* i2c buses */
struct radeon_i2c_chan *i2c_bus[RADEON_MAX_I2C_BUS];
/* debugfs */
struct radeon_debugfs debugfs[RADEON_DEBUGFS_MAX_COMPONENTS];
unsigned debugfs_count;
/* virtual memory */
struct radeon_vm_manager vm_manager;
struct mutex gpu_clock_mutex;
/* memory stats */
atomic64_t vram_usage;
atomic64_t gtt_usage;
atomic64_t num_bytes_moved;
atomic_t gpu_reset_counter;
/* ACPI interface */
struct radeon_atif atif;
struct radeon_atcs atcs;
/* srbm instance registers */
struct mutex srbm_mutex;
/* clock, powergating flags */
u32 cg_flags;
u32 pg_flags;
struct dev_pm_domain vga_pm_domain;
bool have_disp_power_ref;
u32 px_quirk_flags;
/* tracking pinned memory */
u64 vram_pin_size;
u64 gart_pin_size;
struct mutex mn_lock;
DECLARE_HASHTABLE(mn_hash, 7);
};
分配内存之后,紧接着就是dev->dev_private = (void *)rdev;
这句程序完成了struct drm_device和struct radeon_device的衔接。
————————————————
原文链接:https://blog.csdn.net/phmatthaus/article/details/113868281
7.radeon_driver_load_kms函数详解2
继续在radeon_driver_load_kms函数中徜徉。本篇解析radeon_driver_load_kms函数中的两大核心函数之一:radeon_device_init。
调用处代码:
r = radeon_device_init(rdev, dev, dev->pdev, flags);
源码如下:
drivers/gpu/drm/radeon/radeon_device.c
/**
* radeon_device_init - initialize the driver
*
* @rdev: radeon_device pointer
* @pdev: drm dev pointer
* @pdev: pci dev pointer
* @flags: driver flags
*
* Initializes the driver info and hw (all asics).
* Returns 0 for success or an error on failure.
* Called at driver startup.
*/
int radeon_device_init(struct radeon_device *rdev,
struct drm_device *ddev,
struct pci_dev *pdev,
uint32_t flags)
{
int r, i;
int dma_bits;
bool runtime = false;
rdev->shutdown = false;
rdev->dev = &pdev->dev;
rdev->ddev = ddev;
rdev->pdev = pdev;
rdev->flags = flags;
rdev->family = flags & RADEON_FAMILY_MASK;
rdev->is_atom_bios = false;
rdev->usec_timeout = RADEON_MAX_USEC_TIMEOUT;
rdev->mc.gtt_size = 512 * 1024 * 1024;
rdev->accel_working = false;
/* set up ring ids */
for (i = 0; i < RADEON_NUM_RINGS; i++) {
rdev->ring[i].idx = i;
}
rdev->fence_context = dma_fence_context_alloc(RADEON_NUM_RINGS);
DRM_INFO("initializing kernel modesetting (%s 0x%04X:0x%04X 0x%04X:0x%04X 0x%02X).\n",
radeon_family_name[rdev->family], pdev->vendor, pdev->device,
pdev->subsystem_vendor, pdev->subsystem_device, pdev->revision);
/* mutex initialization are all done here so we
* can recall function without having locking issues */
mutex_init(&rdev->ring_lock);
mutex_init(&rdev->dc_hw_i2c_mutex);
atomic_set(&rdev->ih.lock, 0);
mutex_init(&rdev->gem.mutex);
mutex_init(&rdev->pm.mutex);
mutex_init(&rdev->gpu_clock_mutex);
mutex_init(&rdev->srbm_mutex);
init_rwsem(&rdev->pm.mclk_lock);
init_rwsem(&rdev->exclusive_lock);
init_waitqueue_head(&rdev->irq.vblank_queue);
mutex_init(&rdev->mn_lock);
hash_init(rdev->mn_hash);
r = radeon_gem_init(rdev);
if (r)
return r;
radeon_check_arguments(rdev);
/* Adjust VM size here.
* Max GPUVM size for cayman+ is 40 bits.
*/
rdev->vm_manager.max_pfn = radeon_vm_size << 18;
/* Set asic functions */
r = radeon_asic_setup(rdev);
if (r)
return r;
/* all of the newer IGP chips have an internal gart
* However some rs4xx report as AGP, so remove that here.
*/
if ((rdev->family >= CHIP_RS400) &&
(rdev->flags & RADEON_IS_IGP)) {
rdev->flags &= ~RADEON_IS_AGP;
}
if (rdev->flags & RADEON_IS_AGP && radeon_agpmode == -1) {
radeon_agp_disable(rdev);
}
/* Set the internal MC address mask
* This is the max address of the GPU's
* internal address space.
*/
if (rdev->family >= CHIP_CAYMAN)
rdev->mc.mc_mask = 0xffffffffffULL; /* 40 bit MC */
else if (rdev->family >= CHIP_CEDAR)
rdev->mc.mc_mask = 0xfffffffffULL; /* 36 bit MC */
else
rdev->mc.mc_mask = 0xffffffffULL; /* 32 bit MC */
/* set DMA mask + need_dma32 flags.
* PCIE - can handle 40-bits.
* IGP - can handle 40-bits
* AGP - generally dma32 is safest
* PCI - dma32 for legacy pci gart, 40 bits on newer asics
*/
rdev->need_dma32 = false;
if (rdev->flags & RADEON_IS_AGP)
rdev->need_dma32 = true;
if ((rdev->flags & RADEON_IS_PCI) &&
(rdev->family <= CHIP_RS740))
rdev->need_dma32 = true;
#ifdef CONFIG_PPC64
if (rdev->family == CHIP_CEDAR)
rdev->need_dma32 = true;
#endif
dma_bits = rdev->need_dma32 ? 32 : 40;
r = pci_set_dma_mask(rdev->pdev, DMA_BIT_MASK(dma_bits));
if (r) {
rdev->need_dma32 = true;
dma_bits = 32;
pr_warn("radeon: No suitable DMA available\n");
}
r = pci_set_consistent_dma_mask(rdev->pdev, DMA_BIT_MASK(dma_bits));
if (r) {
pci_set_consistent_dma_mask(rdev->pdev, DMA_BIT_MASK(32));
pr_warn("radeon: No coherent DMA available\n");
}
rdev->need_swiotlb = drm_get_max_iomem() > ((u64)1 << dma_bits);
/* Registers mapping */
/* TODO: block userspace mapping of io register */
spin_lock_init(&rdev->mmio_idx_lock);
spin_lock_init(&rdev->smc_idx_lock);
spin_lock_init(&rdev->pll_idx_lock);
spin_lock_init(&rdev->mc_idx_lock);
spin_lock_init(&rdev->pcie_idx_lock);
spin_lock_init(&rdev->pciep_idx_lock);
spin_lock_init(&rdev->pif_idx_lock);
spin_lock_init(&rdev->cg_idx_lock);
spin_lock_init(&rdev->uvd_idx_lock);
spin_lock_init(&rdev->rcu_idx_lock);
spin_lock_init(&rdev->didt_idx_lock);
spin_lock_init(&rdev->end_idx_lock);
if (rdev->family >= CHIP_BONAIRE) {
rdev->rmmio_base = pci_resource_start(rdev->pdev, 5);
rdev->rmmio_size = pci_resource_len(rdev->pdev, 5);
} else {
rdev->rmmio_base = pci_resource_start(rdev->pdev, 2);
rdev->rmmio_size = pci_resource_len(rdev->pdev, 2);
}
rdev->rmmio = ioremap(rdev->rmmio_base, rdev->rmmio_size);
if (rdev->rmmio == NULL)
return -ENOMEM;
/* doorbell bar mapping */
if (rdev->family >= CHIP_BONAIRE)
radeon_doorbell_init(rdev);
/* io port mapping */
for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
if (pci_resource_flags(rdev->pdev, i) & IORESOURCE_IO) {
rdev->rio_mem_size = pci_resource_len(rdev->pdev, i);
rdev->rio_mem = pci_iomap(rdev->pdev, i, rdev->rio_mem_size);
break;
}
}
if (rdev->rio_mem == NULL)
DRM_ERROR("Unable to find PCI I/O BAR\n");
if (rdev->flags & RADEON_IS_PX)
radeon_device_handle_px_quirks(rdev);
/* if we have > 1 VGA cards, then disable the radeon VGA resources */
/* this will fail for cards that aren't VGA class devices, just
* ignore it */
vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode);
if (rdev->flags & RADEON_IS_PX)
runtime = true;
if (!pci_is_thunderbolt_attached(rdev->pdev))
vga_switcheroo_register_client(rdev->pdev,
&radeon_switcheroo_ops, runtime);
if (runtime)
vga_switcheroo_init_domain_pm_ops(rdev->dev, &rdev->vga_pm_domain);
r = radeon_asic_init(rdev);
if (r)
goto failed;
r = radeon_gem_debugfs_init(rdev);
if (r) {
DRM_ERROR("registering gem debugfs failed (%d).\n", r);
}
r = radeon_mst_debugfs_init(rdev);
if (r) {
DRM_ERROR("registering mst debugfs failed (%d).\n", r);
}
if (rdev->flags & RADEON_IS_AGP && !rdev->accel_working) {
/* Acceleration not working on AGP card try again
* with fallback to PCI or PCIE GART
*/
radeon_asic_reset(rdev);
radeon_asic_fini(rdev);
radeon_agp_disable(rdev);
r = radeon_asic_init(rdev);
if (r)
goto failed;
}
r = radeon_ib_ring_tests(rdev);
if (r)
DRM_ERROR("ib ring test failed (%d).\n", r);
/*
* Turks/Thames GPU will freeze whole laptop if DPM is not restarted
* after the CP ring have chew one packet at least. Hence here we stop
* and restart DPM after the radeon_ib_ring_tests().
*/
if (rdev->pm.dpm_enabled &&
(rdev->pm.pm_method == PM_METHOD_DPM) &&
(rdev->family == CHIP_TURKS) &&
(rdev->flags & RADEON_IS_MOBILITY)) {
mutex_lock(&rdev->pm.mutex);
radeon_dpm_disable(rdev);
radeon_dpm_enable(rdev);
mutex_unlock(&rdev->pm.mutex);
}
if ((radeon_testing & 1)) {
if (rdev->accel_working)
radeon_test_moves(rdev);
else
DRM_INFO("radeon: acceleration disabled, skipping move tests\n");
}
if ((radeon_testing & 2)) {
if (rdev->accel_working)
radeon_test_syncing(rdev);
else
DRM_INFO("radeon: acceleration disabled, skipping sync tests\n");
}
if (radeon_benchmarking) {
if (rdev->accel_working)
radeon_benchmark(rdev, radeon_benchmarking);
else
DRM_INFO("radeon: acceleration disabled, skipping benchmarks\n");
}
return 0;
failed:
/* balance pm_runtime_get_sync() in radeon_driver_unload_kms() */
if (radeon_is_px(ddev))
pm_runtime_put_noidle(ddev->dev);
if (runtime)
vga_switcheroo_fini_domain_pm_ops(rdev->dev);
return r;
}
前文已说过,radeon_device_init函数的作用是初始化芯片的非显示部分(asic init, CP, writeback等)。
这个函数首先会初始化一大堆的驱动需要使用的结构,然后调用radeon_asic_init(),这个函数用于设置电路相关的一些函数指针,比如睡眠/恢复调用,硬件重置,设置和处理中断请求,设置和获取时钟等等。
————————————————
原文链接:https://blog.csdn.net/phmatthaus/article/details/113877928
8.radeon_driver_load_kms函数详解3
目前我们的轨迹为:radeon_driver_load_kms --> radeon_device_init 。本篇来到: radeon_device_init --> radeon_gem_init。源码如下:
drivers/gpu/drm/radeon/radeon_gem.c
int radeon_gem_init(struct radeon_device *rdev)
{
INIT_LIST_HEAD(&rdev->gem.objects);
return 0;
}
终于看到一个短的函数了!那么我们就有篇幅展开一个相关知识——内核链表。
在Linux内核中,提供了一个用来创建双向循环链表的结构 list_head。虽然linux内核是用C语言写的,但是list_head的引入,使得内核数据结构也可以拥有面向对象的特性,通过使用操作list_head 的通用接口很容易实现代码的重用,有点类似于C++的继承机制。内核链表的相关代码在include/linux/list.h中,下面分别来看。
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
static inline void INIT_LIST_HEAD(struct list_head *list)
{
WRITE_ONCE(list->next, list);
list->prev = list;
}
struct list_head的定义在include/linux/types.h中
struct list_head {
struct list_head *next, *prev;
};
需要注意的一点是,头结点head是不使用的,这点需要注意。
使用list_head组织的链表的结构如下图所示:
介绍了内核链表的相关知识之后,回到radeon_gem_init函数中来。
INIT_LIST_HEAD(&rdev->gem.objects);
这一句代码中rdev是struct radeon_device的实例,struct radeon_device结构体中(参见linux内核radeon gpu源码解析6)的有一个gem成员:
struct radeon_gem gem;
其定义为:
drivers/gpu/drm/radeon/radeon.h
struct radeon_gem {
struct mutex mutex;
struct list_head objects;
};
这样函数的作用就很明确了,初始化内核链表rdev->gem.objects。
————————————————
版权声明:本文为CSDN博主「蓝天居士」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/phmatthaus/article/details/113878681