當(dāng)代顯卡設(shè)備驅(qū)動(dòng)包括了顯示、GPU計(jì)算兩個(gè)主要功能,在DRM框架下的顯卡驅(qū)動(dòng)通常有內(nèi)核驅(qū)動(dòng)模塊(KMD)、用戶空間驅(qū)動(dòng)模塊(UMD)組成,KMD主要是和顯卡硬件交互,UMD則完成2D、3D圖形的渲染、視頻硬件界面。UMD與KMD配合將顯卡的功能最大限度發(fā)揮出來。
下面我們以Linux虛擬環(huán)境中常用的qxl顯卡為例來看添加一個(gè)新的顯卡驅(qū)動(dòng),都要完成哪些內(nèi)容。
目前除去一些片內(nèi)的顯示芯片,常見顯卡板卡都是pci接口,所以這里注冊一個(gè)pci驅(qū)動(dòng)。
static int __init qxl_init(void)
{ ... qxl_driver.num_ioctls = qxl_max_ioctls; return pci_register_driver(&qxl_pci_driver);
}
pci驅(qū)動(dòng)的結(jié)構(gòu)中,probe函數(shù)是pci設(shè)備的初始化過程,這個(gè)pci設(shè)備作為一個(gè)顯卡設(shè)備會(huì)被初始化化。
static struct pci_driver qxl_pci_driver = {
.name = DRIVER_NAME,
.id_table = pciidlist,
.probe = qxl_pci_probe,
.remove = qxl_pci_remove,
.driver.pm = qxl_pm_ops,
};
在pci設(shè)備初始化過程中,將設(shè)備初始化為DRM框架下的一個(gè)顯示驅(qū)動(dòng)設(shè)備。如下代碼中,這列舉出了qxl_pci_probe核心功能函數(shù),完整源碼參考Uos源碼倉庫源碼包或Uos社區(qū)內(nèi)核源碼。
static int
qxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{ ... ret = pci_enable_device(pdev); ... ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, 0, "qxl"); ... ret = qxl_device_init(qdev, &qxl_driver, pdev); ... ret = qxl_modeset_init(qdev); ... ret = drm_dev_register(&qdev->ddev, ent->driver_data); ... }
pci_enable_device()函數(shù)初始化一個(gè)pci設(shè)備,這是在被使用前的必要步驟,通常是設(shè)置底層使能I/O和內(nèi)存。
drm_fb_helper_remove_conflicting_pci_framebuffers()函數(shù)清楚之前使用的framebuffer設(shè)置,從這里開始qxl驅(qū)動(dòng)負(fù)責(zé)顯示framebuffer的管理。 這通常是在啟動(dòng)過程中framebuffer的管理權(quán)限專一的時(shí)候使用。
qxl_device_init()、qxl_modeset_init()是實(shí)際做qxl初始化的部分,這部分要在注冊drm驅(qū)動(dòng)之前完成。這部分是顯卡專有的參數(shù)、功能的準(zhǔn)備,保證注冊為drm設(shè)備后就可以正常工作。qxl_driver是qxl提供的qxl這個(gè)DRM設(shè)備對應(yīng)的DRM驅(qū)動(dòng)。這里需要關(guān)注一下drm_driver結(jié)構(gòu)體里的load函數(shù),通常是顯卡驅(qū)動(dòng)加載kms的入口。
static struct drm_driver qxl_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
DRIVER_ATOMIC | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED, .dumb_create = qxl_mode_dumb_create, .dumb_map_offset = qxl_mode_dumb_mmap, #if defined(CONFIG_DEBUG_FS) .debugfs_init = qxl_debugfs_init, #endif .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_export = drm_gem_prime_export, .gem_prime_import = drm_gem_prime_import, .gem_prime_pin = qxl_gem_prime_pin, .gem_prime_unpin = qxl_gem_prime_unpin, .gem_prime_get_sg_table = qxl_gem_prime_get_sg_table, .gem_prime_import_sg_table = qxl_gem_prime_import_sg_table, .gem_prime_vmap = qxl_gem_prime_vmap, .gem_prime_vunmap = qxl_gem_prime_vunmap, .gem_prime_mmap = qxl_gem_prime_mmap, .gem_free_object_unlocked = qxl_gem_object_free, .gem_open_object = qxl_gem_object_open, .gem_close_object = qxl_gem_object_close, .fops = qxl_fops, .ioctls = qxl_ioctls, .irq_handler = qxl_irq_handler, .name = DRIVER_NAME, .desc = DRIVER_DESC, .date = DRIVER_DATE, .major = 0, .minor = 1, .patchlevel = 0,
};
qxl_fops是用戶層操作設(shè)備節(jié)點(diǎn)/dev/dri/cardX的時(shí)候,對應(yīng)的操作方法。顯卡驅(qū)動(dòng)往往是在這里封裝drm的函數(shù),比如: 自定義qxl_open()函數(shù),做專業(yè)的操作,然后調(diào)用drm_open()。
static const struct file_operations qxl_fops = { .owner = THIS_MODULE, .open = drm_open, .release = drm_release, .unlocked_ioctl = drm_ioctl, .poll = drm_poll, .read = drm_read, .mmap = qxl_mmap,
};
drm_dev_register()函數(shù)將qxl注冊到DRM框架,成為一個(gè)DRM設(shè)備
這是顯卡驅(qū)動(dòng)將自己添加到DRM框架的入口,注冊成功之后,用戶層就可以正常使用這個(gè)顯示設(shè)備做渲染、顯示了。
int drm_dev_register(struct drm_device *dev, unsigned long flags)
{ ... ret = drm_minor_register(dev, DRM_MINOR_RENDER); ... ret = drm_minor_register(dev, DRM_MINOR_PRIMARY); ... if (dev->driver->load) {
ret = dev->driver->load(dev, flags); ... } ... if (drm_core_check_feature(dev, DRIVER_MODESET))
drm_modeset_register_all(dev); ... }
DRM_MINOR_RENDER設(shè)備對應(yīng)的是/dev/dri/renderX, DRM_MINOR_PRIMARY設(shè)備對應(yīng)的是/dev/dri/cardX。
如果drm驅(qū)動(dòng)提供了load方法,調(diào)用load函數(shù)完成kms初始化。
drm_modeset_register_all()函數(shù)完成DRM框架下的crtc、plane、encoder、connecter的初始化。
int drm_modeset_register_all(struct drm_device *dev)
{
int ret;
ret = drm_plane_register_all(dev); if (ret)
goto err_plane;
ret = drm_crtc_register_all(dev); if (ret)
goto err_crtc;
ret = drm_encoder_register_all(dev); if (ret)
goto err_encoder;
ret = drm_connector_register_all(dev); if (ret)
goto err_connector; return 0; ... }
通過上述三個(gè)步驟,顯卡驅(qū)動(dòng)就完成了自身初始化、并將自己添加到DRM顯示框架當(dāng)中。
桌面版Linux圖形顯示系統(tǒng)的層次如下圖所示,這是一個(gè)基于DRM框架的完整架構(gòu),UOS系統(tǒng)的顯示功能也遵循這個(gè)框架層次的。
Linux圖形顯示系統(tǒng)層次結(jié)構(gòu)
UOS支持以完全開源、部分開源已經(jīng)完全閉源的方式提供的顯卡方案進(jìn)行集成。當(dāng)前UOS設(shè)備驅(qū)動(dòng)的集成的技術(shù)架構(gòu)下,推薦顯卡廠商以Vendor的方式提供UMD。KMD可以按照實(shí)際情況是否開源來選擇是集成進(jìn)內(nèi)核主線源碼做核內(nèi)驅(qū)動(dòng),還是以dkms的方式做核外驅(qū)動(dòng)。
上圖藍(lán)色框內(nèi)是廠商方案涉及的部分,通常顯卡驅(qū)動(dòng)方案包括:
顯示服務(wù)的DDX,通常命名方式是xxx_drv.so,存放目/usr/lib/xorg/modules/drivers/
3D渲染的支持庫,通常命名方式是xxx_dri.so, 推薦存放目錄/usr/lib/
特有的DRM API接口庫,通常命名方式是libdrm_xxx.so, 推薦存放目錄/usr/lib/
顯卡必要的固件firmware文件已經(jīng)專有配置
廠商提供的vaapi、vdpau視頻解碼庫
顯卡內(nèi)核驅(qū)動(dòng)模塊
UOS顯卡集成推薦方案層次圖
UOS系統(tǒng)對廠商的方案支持的相關(guān)協(xié)議版本的要求如下表:
軟件模塊 | 限制說明 |
---|---|
KMD | 推薦使用DRM框架,支持DRIVER_ATOMIC特性 |
openGL | UMD支持的openGL協(xié)議版本不低于3.3 |
glvnd | 推薦UMD以一個(gè)Vendor的方式在glvnd框架下加載 |
llvm | 如果適配Uos版本的llvm不滿足UMD要求,需要安裝多版本llvm,需要保證多版本的安全性 |
Xorg | 兼容Xorg的ABI版本 |
3、教程網(wǎng):http://www.downmusicas.net
企業(yè)客服QQ:58053012
網(wǎng)際迅聯(lián)(北京)科技有限公司
|
聯(lián)系我們
|
微信掃一掃 手機(jī)訪問 |