1. 程式人生 > >Linux DRM(一)Display Server

Linux DRM(一)Display Server

一、Display Server X Windows 和 X Server

    The X Window System (X11, or shortened to simply X) is a windowing system for bitmap displays, common on UNIX-like computer operating systems.     X provides the basic framework for a GUI environment: drawing and moving windows on the display device and interacting with a mouse and keyboard. X does not mandate the user interface – this is handled by individual programs. As such, the visual styling of X-based environments varies greatly; different programs may present radically different interfaces

X Windows 是用於點陣圖顯示的視窗系統,常用於 UNIX 系的作業系統上。它為 GUI 環境提供了基本框架:在顯示裝置上繪製和移動視窗,並與滑鼠和鍵盤進行互動。 它在設計之初,便秉承“提供機制,而非策略”的理念。(是的,和 Unix 設計哲學相同)所以它對使用者介面不作要求,比如它提供了生成視窗(window)的方法,卻對視窗呈現方式不作任何要求。 另一個設計特點是它是基於 Client/Server 網路模型。不論本地還是遠端應用程式,都統一通過 C/S 模型來運作。

我們看一下 X Windows 的架構圖(圖來自 imtx.me 作者 TualatriX):

拿一個簡單的應用場景舉例。點選按鈕引發按鈕更新動作。 1. 核心捕獲滑鼠點選事件併發送給 X server。 2. X Server 會計算該把這一事件傳送給哪個視窗(事實上,視窗位置是由 Compositor 控制的,X Server 並不能夠正確的計算 Compositor 做過特效變化之後的按鈕的正確位置)。 3. 應用程式對此事件進行處理(將引發按鈕更新動作)。但是,在此之前它得向X Server 傳送繪製請求。 4. X Server 接收到這條繪製請求,然後把它發給視訊驅動來渲染。X 還計算了更新區域,並且這條“垃圾資訊”傳送給了 Compositor。 5. 這時,Compositor 知道它必須要重新合成螢幕上的一塊區域。當然,這還是要向X Server傳送繪製請求的。 6. 開始繪製。但是 X Server 還會去做一些不必要的本職工作(視窗重疊計算、視窗剪裁計算等)。

我們通過一個例項理解了上面的架構圖。 它呈現出了一個缺點: X Client – X Server – Compositor 三者的互動比較耗時,不是很高效。除了互動方面的問題,其實 X Server 還會做一些重複無意義的工作,這裡不再贅述。 有痛點就有解決方案,新一代的 Display Server 呼之欲出。 接下來 Wayland 誕生了。 Wayland

Wayland is A Simple Display Server。 它在誕生之初的使命是用於改善 X Server 的不足並取代它。 但是現在看來它所作的不僅僅是替代 X Window 下的 X Server,也不僅僅是要取代 X Widnow。而是要顛覆 Linux 桌面上的 X Server/X Client 的概念。 以 Compositor/Client 的結構取而代之。 同時它複用了所有 Linux 核心圖形和 I/O 技術:KMS、GEM、DRM、evdev。

其架構圖如下(圖來自 imtx.me 作者 TualatriX):

同樣是上面那個例項,其流程如下: 1. 核心捕獲滑鼠點選事件併發送給Wayland Compositor。 2. 由於是直接發給Wayland Compositor的,所以Wayland Compositor會正確地計算出按鈕的位置。同時它會把這一事件傳送給按鈕所在的應用程式來處理。 3. 應用程式直接渲染,無需向Wayland Compositor請求。只需在繪製完成之後向Wayland Compositor傳送一條資訊表明這塊區域被更新了。 4. Wayland Compositor收到這條資訊後,立即重新合成整個桌面。

所以基於 Wayland 的整個任務流程非常簡單: 1. 基於Wayland協議,處理evdev的資訊; 2. 通知Client(即應用程式)對相關事件做出反應(至於應用程式想怎麼反應,Compositor不需要過問); 3. 收到Client的狀態更新,重新合成圖形或管理新的圖形佈局。

簡單的說,Waylannd 就是一個去除 X Window 中不必要的設計、充分利用現代 Linux 核心圖形技術(DRM 子系統中的 KMS、GEM、DRM)的一個顯示機制,它的出現是自然而然的,它的使命不是為了消滅X Window,而是將Linux的圖形技術發揮至更高的一個境界。傳統的X Window(即經典X應用、Gtk 1.x/2.x等舊應用),也會在相當長一段時間內得到繼續支援,通過Wayland Client的形式跑在Wayland Compositor上,直到最終升級、取代或被淘汰。

有了以上背景,接下來我們便能更好的理解 DRM Subsystem 了。 二、DRM Subsystem

    In computing, the Direct Rendering Manager (DRM), a subsystem of the Linux kernel, interfaces with the GPUs of modern video cards. DRM exposes an API that user-space programs can use to send commands and data to the GPU, and to perform operations such as configuring the mode setting of the display. DRM was first developed as the kernel space component of the X Server’s Direct Rendering Infrastructure,[1] but since then it has been used by other graphic stack alternatives such as Wayland.     User-space programs can use the DRM API to command the GPU to do hardware-accelerated 3D rendering and video decoding as well as GPGPU computing.     摘自 wipipedia

DRM,英文全稱 Direct Rendering Manager, 即 直接渲染管理器。 它是為了解決多個程式對 Video Card 資源的協同使用問題而產生的。它向用戶空間提供了一組 API,用以訪問操縱 GPU。 fbdev

Linux 早在很久以前就已經有一個名為 fbdev 的 API ,用於管理顯示卡的 framebuffer,但是它不能用於處理 基於視訊卡的 GPU 的 3D 加速的需求。 這些 Video Card 常常需要設定或者管理 卡記憶體(Video RAM)中的一些命令佇列。將命令分配給 GPU,還需要管理 Video RAM 本身的 Buffer 和 Free Space。

在最初的使用者空間的程式(比如 X Server)可以直接管理這些資源,但這些程式通常表現的就彷彿他們是唯一去獲取這些資源的一樣。當有多個程式試圖去以自己的方式同時控制 Video Card 資源時,就會崩潰。 DRM

DRM 的誕生就是用來處理多個程式對 Video Card 資源的協同使用問題。 DRM 獲得對 Video Card 的獨佔訪問許可權,它負責初始化和維護命令佇列、Video RAM 以及其他相關的硬體資源。

要使用 GPU 的程式將請求傳送給 DRM,由 DRM 作為仲裁來避免衝突。 DRM 到現在已經涵蓋了以前由使用者空間程式處理的很多功能,比如 幀快取區的管理和模式設定,記憶體共享物件和記憶體同步。其中一些拓展具有特定的名稱,比如圖形執行管理器 GEM 或者核心模式設定 KMS,這些都是屬於 DRM 子系統。 DRM 同時也負責處理 GPUs 切換的問題。

在下一章,我們會開始介紹 Linux 原始碼中 DRM 的軟體架構。

參考文章: https://en.wikipedia.org/wiki/X_Window_System http://zqdevres.qiniucdn.com/data/20101106130912/index.html https://imtx.me/archives/1573.html https://imtx.me/archives/1574.html https://en.wikipedia.org/wiki/Direct_Rendering_Manager --------------------- 作者:Younix髒羊 來源:CSDN 原文:https://blog.csdn.net/dearsq/article/details/78312052 版權宣告:本文為博主原創文章,轉載請附上博文連結!

在《Linux DRM (一) Display Server》我們瞭解了 DRM 誕生的歷史緣由。 本篇我們朝著 DRM 本尊再走幾步,先介紹幾個 DRM 的基本概念。 一、楔子

上篇文章中我們有講過 DRM 是 linux 下的圖形渲染架構,用來管理顯示輸出、buffer 分配的。 應用程式可以直接操縱 drm 的 ioctl 或者是用 framebuffer 提供的介面進行顯示相關操作。 後來大家覺得這樣太 low 了,乾脆封裝成一個庫吧。於是 libdrm 誕生了,它是一個庫,其中提供了一系列友好的控制封裝,讓我們可以更加方便的進行顯示控制。

要弄明白 DRM 是怎麼把使用者的繪圖輸出到顯示屏上,我們繞不開了解這幾個概念:

    Framebuffer     CRTC     Encoder     Connector     Display Device(LCD)

然後我們會介紹一下 DRM 的一些特性。

最後再用較少的篇幅為大家介紹一下 RK 平臺 DRM Driver 所依賴的 Component Framework。 二、DRM 涉及的基本概念

記住這張圖~

讓我們一起關注一下綠色方框中的五個 Block。 2.1 DRM Framebuffer

它是一塊記憶體區域,我把它理解為一塊畫布,驅動和應用層都能訪問它。畫畫之前需要將它格式化,我們需要設定你要畫油畫還是國畫(色彩模式,比如 RGB24,YUV 等),畫布需要多大(解析度)。 2.2 CRTC

直譯為 陰極攝像管上下文。我要說它的作用是讀取當前掃描緩衝區的畫素資料並藉助於PLL電路從其生成視訊模式定時訊號。你可能就開始嘟囔著抱怨什麼鬼了。簡單點,說話的方式簡單點,簡單的來說他就是顯示輸出的上下文,我把它理解為掃描器。它對內連線 Framebuffer 地址,對外連線 Encoder。 它會掃描你畫布(Framebuffer)上的內容,疊加上 Planes 的內容,傳給 Encoder。 2.3 Planes

直譯為 平面。它和 framebuffer 一樣是記憶體地址。它的作用是幹什麼呢?想象這樣一個場景,筆者正在很不專心地一邊看動作大片一邊寫文章。動作大片每一幀的變化都很大,需要全幅更新,寫文章半天擠不出一個字,基本上不需要更新。筆者的頑皮將顯示卡的使用拉上了兩個極端。一種是全幅高速更新的 Video Mode,一種是文字互動這種小範圍的 Graphics Mode。此時輪到 Planes 出馬了,它給 Video 重新整理提供了高速通道,使 Video 單獨為一個圖層,可以疊加在 Graphic 上或之下,並具有縮放等功能。

看看本節開始的那張圖,Planes 是可以有多個的,所以最後供掃描器(CRTC)掃描的畫(影象)實際上往往是Framebuffer 和 Planes 的組合(Blending)。 2.4 Encoder

直譯為 編碼器。它的作用就是將記憶體的 pixel 畫素 編碼(轉換)為顯示器所需要的訊號。

你可以把它的作用想象為現在要將你的畫在全世界不同的顯示裝置(Display Device)上顯示,自然需要將其轉化為不同的電訊號,比如 DVID、VGA、YPbPr、CVBS、Mipi、eDP 等。

所以我們需要這樣一個 Encoder 來進行訊號轉換的工作。它和 CRTC 之間的互動就是我們所說的 ModeSetting,其中包含了前面提供的色彩模式、還有時序(Timing)等。 2.5 Connector

直譯為 聯結器。Connector 常常對應於物理聯結器 (VGA, DVI, FPD-Link, HDMI, DisplayPort, S-Video …) 他會連線將一個物理顯示輸出裝置 (monitor, laptop panel, …) 。 與當前物理連線的輸出裝置相關的資訊(如連線狀態,EDID資料,DPMS狀態或支援的視訊模式)也儲存在 Connector 內。 三、DRM 中的特性 3.1 GEM

Graphics Execution Manager 因為視訊儲存器大小增加,圖形 API 比如 OpenGL 不斷變複雜。在上下文切換時重新初始化顯示卡狀態的話開銷會相當大。另外,現在 Linux 桌面需要需要一個最佳的方式來和合成管理器共享螢幕外快取。 這些需求導致需要開發新的方法來管理核心中的圖形快取區,即 GEM。 3.1.1 GEM 記憶體管理

GEM 提供了一套 API,其擁有明確的記憶體管理原語。通過 GEM,使用者空間程式可以建立、處理、銷燬 GPU 視訊儲存器中的記憶體物件。這些物件被統一稱為 “GEM 物件”,從使用者空間的角度來看,他們是一直存在的,所以在程式重新獲得 GPU 的時候不需要重新載入他們。當用戶空間的程式需要大量的視訊記憶體空間(來儲存 framebuffer、紋理資料 或是 GPU 所需要的其他資料),它都會使用 GEM API 來為 DRM Driver 請求分配記憶體。這套 API 還會提供填充 buffer 和 釋放 的功能。並且可以在使用者空間的程序(因意外或是正常終止而)關閉 DRM 裝置描述符時釋放相關記憶體。

GEM 也提供了兩個及以上的使用者空間程序訪問同一個 DRM 裝置(也就是共享同一個 GEM 物件)的方法。GEM 提供了一個方法,flink(其實這個方法很不安全),來從 GEM 控制代碼中獲取 GEM 名字。程序通過 IPC 將 GEM 名字(32bit整型)傳遞給另一個程序。接收到 GEM 名字的程序於是可以獲得一個本地的 GEM 控制代碼,用以指向原始的 GEM 物件。 所以如果有一個惡意的第三方應用知道了 GEM 名字,就可以訪問和修改 GEM 物件的內容。所以後來通過引入 DMA-BUF 機制來克服這個缺陷。 3.1.2 GEM 記憶體同步

除了管理視訊儲存空間以外,視訊記憶體管理器另一個重要的任務是處理 GPU 和 CPU 之間的記憶體同步(memory synchronization)問題。 因為現在的記憶體架構非常複雜,通常涉及了系統記憶體、視訊記憶體的多級快取(caches)。因此,視訊記憶體管理器為了確保 GPU 和 CPU 共享資料的一致性還需要負責處理快取的一致性。這意味著視訊記憶體管理系統高度依賴於 GPU 硬體和年記憶體架構因此驅動具有特異性。

GEM 定義了用於記憶體同步的“記憶體域”,而這些記憶體域與GPU無關,它們採用 UMA 儲存器架構設計,使其不再適用於其他記憶體架構,如具有單獨 VRAM 的記憶體架構。因此,對外,DRM 驅動會向用戶空間暴露統一的 GEM API,對內,驅動中會實現更適合特定硬體和記憶體架構的不同的記憶體管理器。 3.2 KMS

Kernel Mode Setting

為了正常工作,視訊卡和圖形介面卡必須負責設定一些模式,包括螢幕解析度、色彩深度、重新整理率等,將其設定為自己或是拓展屏所支援的引數範圍內。這個動作就是 mode-setting,它通常需要訪問圖形硬體 —— 即去操作視訊卡的暫存器 的能力。

在開始使用 framebuffer 之前,或者是由應用程式或者是使用者改變 mode 時,進行 mode-setting。 3.2.1 UMS

最開始,使用者空間想要使用圖形的 framebuffer 也需要負責 mode-setting,因此他們需要訪問 video 硬體的許可權。類 Unix 系統中的 Display Server 比如 X Server 就是一個很好的例子,它的 mode-setting 被放在每種特定的圖形卡各自的 DDX driver 中。

這種方法被稱為 UMS(User space Mode-Setting),有幾個嚴重的問題。它不僅打破了作業系統建立的硬體和程式的隔離,也讓多程序同時進行 mode-setting 時,硬體會產生不一致的狀態。 所以為了避免這些衝突,X Server 成為了實時 mode-settinng 的唯一使用者空間程式,其他使用者空間程式都依賴 X Server 來進行 mode-setting。 最初 mode-setting 是放在 X Server 的啟動過程中,不過後來在其執行過程也可以 mode-setting 。

但是 UMS 有很多問題。 比如在 Linux 系統啟動過程中,Linux 核心必須為 virtual console 設定 “minnimal text” mode。 另外,framebuffer driver 中也包含了配置 framebuffer 裝置 mode 的程式碼。 為了解決這些衝突,有些 Display Server(比如 X Server)通過儲存從圖形環境切換到文字虛擬控制檯時的 mode-setting 狀態,並且在切換回去的時候恢復。 這又導致了新的問題,比如切換時的閃爍,輸出裝置的顯示失敗甚至損毀。 3.2.2 mode-setting

為了解決這些問題,mode-setting 被單獨放到了 Kernel 中,準確的說是 DRM 模組中。 然後,包括 X Server 在內的每個程序都可以命令核心來實現 mode-setting 操作,核心也會確保操作的一致性。這些加入 DRM 模組的新的核心 API 和 程式碼所執行的 mode-setting 的操作被稱為 KMS。

它有太多好處了,第一個就是從 Kernel (console、fbdev)和 Userspace(X Server DDX Driver)幹掉了那些重複的 mode-setting 的程式碼。第二就是對於圖形作業系統不再需要關心 mode-setting 部分程式碼的編寫了。第三就是因為提供了這種單一集中式的模式管理,console 和 X Server 不同例項的切換變得更加容易了。第四就是 mode-setting 放到核心後,可以從啟動過程就開始使用它(這個在曾經也會導致閃爍問題)。

另外,因為 KMS 是核心的一部分,它也就可以去使用 kernel space 的很多資源(比如中斷)。因為交由核心去管理了,在 suspend/resume 後的模式恢復變得更簡單了。核心還讓新顯示裝置的熱插拔更加容易。

由於 mode-setting 和記憶體管理密切相關 ———— framebuffer 基本就是 記憶體buffer ———— 故它和影象記憶體管理緊密整合。這也是為什麼它被放到 DRM 模組而不是獨立作為一個子系統的原因。 3.2.3 KMS Driver

為了不破壞 DRM API 的向後相容性,KMS 為 DRM Driver 提供了一個特殊的特性。任何 DRM Driver 在註冊 DRM Core 的時候需要選擇是否採用 DRIVER_MODESET 標誌,用來表示是否支援 KMS API。 支援 KMS API 的驅動為了和傳統的 DRM Driver 驅動區分往往被稱為 KMS Driver。 3.2.4 KMS Device Mode

KMS 負責塑造和管理輸出裝置,將他們抽象為一系列的硬體模組(這些硬體模組常常會在顯示控制器的顯示輸出管道上)。 這些模組我們前面都有介紹,CRTC、Planes、Encoder、Connector 。但是當時介紹的比較通俗,下面是 Wikipedia 上面的谷歌翻譯版本:

CRTCs:每個 CRTC(來自 CRT 控制器)代表顯示控制器的掃描引擎,指向 framebuffer。 CRTC 的目標是讀取當前掃描緩衝區的畫素資料並藉助於PLL電路從其生成視訊模式定時訊號。 CRTC 的數量決定了硬體可以同時處理多少個獨立的輸出裝置,因此為了使用多頭配置,每個顯示裝置至少需要一個 CRTC。 兩個或多個CRTC也可以在克隆模式下工作,如果它們掃描的是相同的幀緩衝區,便可以將相同的影象傳送到多個輸出裝置。

Connectors: 聯結器表示顯示控制器從要顯示的掃描輸出操作傳送視訊訊號的位置。 通常,KMS 中 Connectors 對應於物理聯結器 (VGA, DVI, FPD-Link, HDMI, DisplayPort, S-Video …) 他會連線將一個物理顯示輸出裝置 (monitor, laptop panel, …) 。 與當前物理連線的輸出裝置相關的資訊(如連線狀態,EDID資料,DPMS狀態或支援的視訊模式)也儲存在聯結器內。

Encoders: 顯示控制器必須使用適合於預期聯結器的格式 對 來自CRTC的視訊模式定時訊號 進行編碼。編碼器代表能夠執行其中一個編碼的硬體塊。 聯結器一次只能從一個編碼器接收訊號,每種型別的聯結器只支援一些編碼。還可能會有其他的物理限制,並不是每個CRTC連線到每個可用的編碼器,限制了CRTC編碼器聯結器的可能組合。

Planes: plane 不是硬體塊,而是包含供給掃描引擎(CRTC)的緩衝器的記憶體物件。 儲存幀緩衝區的平面稱為主平面,每個CRTC必須有一個關聯的 plane ,因為它是 CRTC 確定引數的來源。引數包括,視訊模式 - 顯示解析度(寬和高),畫素大小,畫素格式,重新整理率等。 四、component 框架

RK 平臺的 DRM 還依賴了 component 框架。 下面是核心郵件列表中關於 RK Socs DRM Driver Patch 的討論:https://lkml.org/lkml/2014/12/2/161

在郵件開頭我們可以看到 RK 平臺 DRM Driver 誕生依賴 15 個版本中的主要變化。 其中有提到很重要的一點是其採用了 component 框架。

因為 DRM 下掛載了很多的裝置,啟動順序可能會引發問題:

    驅動因為等待另一個資源的準備,產生 probe deferral 導致順序不定。     子裝置沒有載入好,主裝置就載入了,導致裝置無法工作。     子裝置相互之間可能有時序關係,不定的載入順序,可能帶來有些時候裝置能工作,有些時候又不能工作。     現在編 kernel 是多執行緒編譯的,編譯的前後順序也會影響驅動的載入順序。

對於 RK 平臺因為有用到多個 VOP,需要在所有 VOP 都啟動後再開啟 DRM Driver 的 probe,即推遲 probe。

此時需要一個統一的管理機制,將所有裝置統合起來按統一順序進行載入,等所有元件載入完畢後,在進行他們和 master 的 bind。

更詳細的關於介紹 component 的文章可以參考 component 作者與其他人討論 component framework 的過程: https://patchwork.kernel.org/patch/3431851/

我們對 component 的接觸會止步於 drm master probe 中的component 部分。我們將在下章分析 drm driver 程式碼中單獨用一節分析在 rockchip drm master probe 中 component 的主要邏輯。 參考文章

wikipedia drm:https://en.wikipedia.org/wiki/Direct_Rendering_Manager landley drm:http://www.landley.net/kdocs/htmldocs/drm.html ubuntu drm kms:http://manpages.ubuntu.com/manpages/zesty/en/man7/drm-kms.7.html https://lkml.org/lkml/2014/12/2/161 https://patchwork.kernel.org/patch/3431851/ --------------------- 作者:Younix髒羊 來源:CSDN 原文:https://blog.csdn.net/dearsq/article/details/78394388 版權宣告:本文為博主原創文章,轉載請附上博文連結!