在之前的文章中,更多的是介紹DRM各組件的初始化流程、註冊方式、管理機制等相關內容。
通過前面的文章大家可以了解到DRM component以及bind機制,圖顯處理器、HDMI、MIPI DSI、LVDS等圖顯IP初始化流程,如何註冊出下圖所示的DRM各個組件。

初始化工作並不是結束而僅僅是一個開始,此時圖顯IP已經處於準備就緒的狀態,最終要按照用戶需求來工作。
在後面文章中,我們會着重介紹DRM架構如何高效、便捷的支撐用戶來管理和使用圖顯設備。
與用戶層相關的內容當中,最重要的當屬KMS(Kernel mode-setting)和GEM*(Graphics Execution Manager)。KMS是保證圖顯IP正常工作的前提,它涉及到圖顯系統的參數配置,因此,我們首先介紹KMS相關內容。
2. 啥是KMS?為啥需要圖顯系統在實際使用過程中,一定要按照用戶指定的模式工作並達到預期的顯示效果,以滿足不同用戶的喜好,適配不同的應用場景。
用戶會提交各種各樣的圖顯系統的模式配置信息,比如屏幕分辨率以及色彩屬性等均屬於模式配置的範疇。

在沒有引入KMS之前,由於圖顯系統涉及到圖顯外設、fb、DRM core、用戶空間X服務、用戶空間DRI等模塊,任何一個模塊都可能引入故障,所以,實現圖顯系統(顯卡)的模式配置是非常難並且有較大的失敗風險。
例如,當Linux系統起來後,X.org已經完成了初始化,按照X.org的規則配置顯卡,此時它就可以操控底層的硬件設備。但是,當我們從X.org切換到了VT console之後,需要按照VT console的規則重新初始化圖顯系統,這種附加的工作量極易引入軟件層面的bug。
另外一方面我們要知道,模式配置是在屏幕正常工作的情況下進行的,圖像顯示是一種動態行為,而我們的模式配置需要在不打斷屏幕當前工作的前提下,將用戶最新的配置信息提交給硬件設備。因此,我們必須保證參數的正確性並且顯示效果不受影響,只能成功,不能失敗。
基於此,DRM架構下必須實現一套完美的模式配置和圖顯設備管理的流程及方式,而它,被我們稱之為KMS。
kernel 2.6.29引入了KMS機制:
committer Dave Airlie <airlied@linux.ie> 2008-12-29 17:47:23 +1000commit f453ba0460742ad027ae0c4c7d61e62817b3e7ef (patch)tree 29e6ecacd6e8971aa62e1825d77f2c1876ac3eb2parent de151cf67ce52ed2d88083daa5e60c7858947329 (diff)download linux-f453ba0460742ad027ae0c4c7d61e62817b3e7ef.tar.gzDRM: add mode setting supportAdd mode setting support to the DRM layer.This is a fairly big chunk of work that allows DRM drivers to providefull output control and configuration capabilities to userspace. It wasmotivated by several factors: - the fb layer's APIs aren't suited for anything but simple configurations - coordination between the fb layer, DRM layer, and various userspace drivers is poor to non-existent (radeonfb excepted) - user level mode setting drivers makes displaying panic & oops messages more difficult - suspend/resume of graphics state is possible in many more configurations with kernel level supportThis commit just adds the core DRM part of the mode setting APIs.Driver specific commits using these new structure and APIs will follow.Co-authors: Jesse Barnes <jbarnes@virtuousgeek.org>, Jakob Bornecrantz <jakob@tungstengraphics.com>Contributors: Alan Hourihane <alanh@tungstengraphics.com>, Maarten Maathuis <madman2003@gmail.com>Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>Signed-off-by: Eric Anholt <eric@anholt.net>Signed-off-by: Dave Airlie <airlied@redhat.com>3. KMS機制及初始化3.1 KMS實現機制我們都知道圖顯處理器、圖顯外設等都屬於顯卡的範疇,在DRM架構下通過struct drm_device代表顯卡設備。對於KMS而言,需要DRM master在初始化過程中調用drm_mode_config_init()函數,初始化struct drm_device中的mode_config成員以及註冊用戶空間所需的API接口。
/***DRMdevicestructure.Thisstructurerepresentacompletecardthat*maycontainmultipleheads.*/structdrm_device{structlist_headlegacy_dev_list;intif_version;......structdrm_mode_configmode_config;/**<Currentmodeconfig*/......};查看kernel代碼可以發現各SoC廠商的圖顯系統驅動代碼均調用了drm_mode_config_init():
rk@ubuntu:~/OK3399-linux-release/kernel$grep-Rn"drm_mode_config_init"drivers/gpu/drm/drivers/gpu/drm/rcar-du/rcar_du_kms.c:750:drm_mode_config_init(dev);drivers/gpu/drm/amd/amdgpu/amdgpu_device.c:1511:drm_mode_config_init(adev->ddev);drivers/gpu/drm/cirrus/cirrus_mode.c:575:drm_mode_config_init(cdev->dev);drivers/gpu/drm/rockchip/rockchip_drm_fb.h:25:voidrockchip_drm_mode_config_init(structdrm_device*dev);drivers/gpu/drm/rockchip/rockchip_drm_fb.c:406:voidrockchip_drm_mode_config_init(structdrm_device*dev)drivers/gpu/drm/rockchip/rockchip_drm_drv.c:1451:drm_mode_config_init(drm_dev);drivers/gpu/drm/rockchip/rockchip_drm_drv.c:1453:rockchip_drm_mode_config_init(drm_dev);Binaryfiledrivers/gpu/drm/rockchip/rockchipdrm.omatchesBinaryfiledrivers/gpu/drm/rockchip/rockchip_drm_drv.omatches.....drivers/gpu/drm/drm_crtc.c:6193:voiddrm_mode_config_init(structdrm_device*dev)drivers/gpu/drm/drm_crtc.c:6222:EXPORT_SYMBOL(drm_mode_config_init);......rk@ubuntu:~/OK3399-linux-release/kernel$我們在上面的代碼片段中發現drm_mode_config_init()的定義位於drm_crtc.c中,那這是為什麼呢?
在了解這個原因之前,先回顧一下DRM圖顯系統的數據流。
圖顯系統的數據流概況為fb->plane->crtc->encoder->connector,如下圖所示:

首先由用戶層申請framebuffer內存,將圖形圖像數據按照特定的像素格式填充進內存空間。對於支持多圖層的圖顯處理器而言,它們均具備多個plane,我們可以指定framebuffer和plane之間的綁定關係,也就是說指定plane從哪個framebuffer中獲取到圖顯數據。當我們的圖顯系統也就是顯卡可以支持多屏顯示時,需要多個crtc組件,該圖中僅列出1個,代表當前圖顯系統在同一時刻僅支持一個顯示屏,像HDMI、VGA等顯示屏均支持熱插拔功能,因此僅具備1個crtc同樣能夠豐富我們的應用場景。
從CRTC作為DRM時序控制組件以及圖顯數據流中所處的位置這兩點也不難發現,其承擔DRM的模式配置功能也就順利成章了。因此,drm_mode_config_init()需要由drm_crtc實現。
3.2 KMS的初始化關於DRM KMS的初始化需要了解以下三點:
本文依然是以RK3399為例內核版本是4.4.189
❞「何時初始化」從前文說明中可知,由DRM master驅動完成drm_mode_config_init()的調用,對於RK3399而言,master設備為DRM device,並不是其圖顯處理器VOP。
staticintrockchip_drm_bind(structdevice*dev){structdrm_device*drm_dev;......drm_dev=drm_dev_alloc(&rockchip_drm_driver,dev);if(!drm_dev)return-ENOMEM;......drm_mode_config_init(drm_dev);rockchip_drm_mode_config_init(drm_dev);rockchip_drm_create_properties(drm_dev);/*Trytobindallsubdrivers.*/ret=component_bind_all(dev,drm_dev);......}以上代碼表明,drm_mode_config_init()的執行時間點非常靠前。介於DRM device初始化之後,各圖顯IP初始化之前。前者提供了KMS的主體,後者需要基於此例化KMS的各種資源。
「初始化哪些資源」

前三點通過調用drm_mode_config_init()實現。具體如下:
voiddrm_mode_config_init(structdrm_device*dev){mutex_init(&dev->mode_config.mutex);......INIT_LIST_HEAD(&dev->mode_config.fb_list);......drm_modeset_lock_all(dev);drm_mode_create_standard_properties(dev);drm_modeset_unlock_all(dev);......}此處提到了DRM property,它是支撐KMS能夠實現原子操作的前提,在此處不做展開描述,後面會針對DRM property做專門介紹。
為了支持DRM KMS 模式配置自適應以及由於硬件IP的特性限制,需要對分辨率以及framebuffer閾值進行初始化。當用戶進行模式配置時,首先要檢查用戶提交的信息是否合法,而判定依據即來源於此。
dev->mode_config.min_width=0;dev->mode_config.min_height=0;/**setmaxwidthandheightasdefaultvalue(4096x4096).*thisvaluewouldbeusedtocheckframebuffersizelimitation*atdrm_mode_addfb().*/dev->mode_config.max_width=8192;dev->mode_config.max_height=8192;dev->mode_config.async_page_flip=true;「註冊哪些KMS的API接口函數」用戶空間通過ioctl調用此類KMS API接口,實現KMS的信息檢查及提交。該初始化同樣由DRM device(DRM master)完成註冊。例如RK3399的DRM驅動:
voidrockchip_drm_mode_config_init(structdrm_device*dev){......dev->mode_config.funcs=&rockchip_drm_mode_config_funcs;}KMS funcs包括:
structdrm_mode_config_funcs{structdrm_framebuffer*(*fb_create)(structdrm_device*dev,structdrm_file*file_priv,structdrm_mode_fb_cmd2*mode_cmd);void(*output_poll_changed)(structdrm_device*dev);int(*atomic_check)(structdrm_device*dev,structdrm_atomic_state*a);int(*atomic_commit)(structdrm_device*dev,structdrm_atomic_state*a,boolasync);structdrm_atomic_state*(*atomic_state_alloc)(structdrm_device*dev);void(*atomic_state_clear)(structdrm_atomic_state*state);void(*atomic_state_free)(structdrm_atomic_state*state);};其中,最主要的是fb_create、atomic_check、atomic_commit,例如RK3399註冊了以上3個回調函數,另外註冊了1個output_poll_changed回調函數。
staticconststructdrm_mode_config_funcsrockchip_drm_mode_config_funcs={.fb_create=rockchip_user_fb_create,.output_poll_changed=rockchip_drm_output_poll_changed,.atomic_check=drm_atomic_helper_check,.atomic_commit=rockchip_drm_atomic_commit,};關於drm_mode_config_funcs各成員,我會在DRM KMS流程的文章中詳細介紹。
以上便是此文的所有內容,介紹了KMS的前世今生以及實現機制,作為底層驅動開發者而言,KMS的註冊機制至關重要,而對於上層應用開發者而言,了解DRM KMS能夠支持哪些功能亦不可少。
掃碼添加我的微信

歡迎點讚、轉發、在看,點擊關注,