DRM的基本概念

摘要:

DRM 是linux 下的圖形渲染架構(Direct Render Manager) ,  具體的說是顯卡驅動的一種架構(驅動如何玩? 把功能封裝成 open/close/ioctl 等標準接口,應用程序調用這些接口來驅動設備)。

作爲顯卡,最基本的功能就是把用戶的繪圖輸出到顯示屏上,DRM 如何去實現呢,先看看DRM 把“這件事”給你概括的幾個基本要素:

畫布(FrameBuffer) , 繪圖現場(CRTC) , 輸出轉換器(Encoder) , 連接器(Connector) , 然後就到顯示屏了

1 畫布( FrameBuffer )

對計算機來說,FrameBuffer 就是一塊驅動和應用層都能訪問的內存,當然畫圖之前要有一定的格式化,比方說我可以規定什麼樣的色彩模式(RGB24 , I420 , YUUV 等等), 分辨率是多大,還有啥參數,那就要到繪圖現場去看了 :p

2 繪圖現場(CRTC)

簡寫翻譯過來是陰級攝像管上下文,在DRM 裏 CRTC 就表示顯示輸出的上下文了,首先 CRTC 內指一個 FrameBuffer 地址, 外連一個Encoder。 它們倆之間如何溝通? 這就是顯示模式(ModeSet)要做的事情,ModeSet 包括了像前面提到的色彩模式 , 還有說顯示的時序(timings , ModeLines 等都代表了這個意西)等, 通常時序可以按以下來表達

PCLK HFPHBP HSWX_RES VFPVBP VSWY_RES

像素時鐘 水平前回掃水平後回掃 水平同步頭水平有效長度 垂直前回掃垂直後回掃 垂直同步頭垂直有效長度

一個CRTC 可以連接多個 Encoder , 幹啥用,實現複製屏幕功能。

3 輸出轉換器(Encoder )

想想 CRT 這種土疙瘩就夠複雜了,我們的顯卡很牛奔的可以連接各種不同的設備,顯然輸出需要不同的信號轉換器,將內存的像素轉換成顯示器需要的信號(DVID , VGA , YPbPr , CVBS 等等……)

4 連接器 (Connector )

不是指物理線,回到DRM 這是一個抽象的數據結構 ,代表連接的顯示設備,從這裏我們可以得到設備的EDID , DPMS 連接狀態等.

5 顯示面(Planner)

咦,怎麼多出來一個。我也很吶悶,以上的東東不夠地幹活? 其實很多創新往往源於人對現實界的不滿足。你又要看文字學習,又要看電影打遊戲, 還有厲害的可以一邊聊天一邊看電影。 這裏對立出來兩個概念,像文字交互這種小範圍更新的Graphics 模式,和全幅更新速度奇快的 Video 模式,這兩種模式將顯卡的使用拉上了兩個極端。

於是 Planner 的概念就發揮了很好的作用,它給視頻刷新提供了一條綠色通道,偶不和圖形搞在一起了,偶是一個新的圖層(或overlay),可以疊加在Graphic之上或之下,偶還可以縮放…

文檔上說 Planner 也在 FrameBuffer 上,這個沒關係,這裏我們看出來 CRTC 裏要顯示的東東應該是一種組合(blending)了。

看懂了概念,下一篇來分析具體的數據結構和接口。

參考文檔:

http://manpages.ubuntu.com/manpages/utopic/man7/drm-kms.7.html

http://events.linuxfoundation.org/sites/events/files/lcjpcojp13_pinchart.pdf

http://landley.net/kdocs/htmldocs/drm.html

http://events.linuxfoundation.org/sites/events/files/slides/brezillon-drm-kms.pdf

http://elinux.org/images/7/71/Elce11_dae.pdf

二 上一篇介紹了 linux 的顯示驅動drm 的架構,在這裏按一定順序回顧一下:

1 我把顯示器連到顯卡的DVI輸出口, 這個連接抽象成 Connector

2 在 DVI 的 Connector 上驅動會分配 DVI 信號的 Encoder , 如果沒分配, connector 資源上會找到 所有可用的 encoders

3 encoder 是爲圖像掃描現場 crtc 服務的, 驅動可能會給encoder 分配crtc , 或者從 encoder 的 possible_crtc 上能找到可用的

4 crtc 掃描現場要配置顯示圖像的物理內存區 fb

5 fb -> crtc -> encoder - > connector 這種關係綁定之後,繪圖工作已經開始, 你可以在fb 上任意寫畫,然後立馬得到顯示!

6 然而爲了避免圖像撕裂,可以建立多 fb (緩衝) 通過 pageFlip 操作來刷新畫圖。

7 當然還有專爲video 刷新用的plane , plane 也要綁定到 crtc 才能工作。

二 總結 + drm api 的使用:

api 使用參考 David Herrmann [email protected] 的 drm-howto 以及 weston 還有 drm自帶的 modetest 程序

drm api 核心配置就是要綁定一個 crtc 的關係 fb -> crtc -> encoder - > connector

我們來看“一”的回顧,咋是按照逆向的順序?哈哈其實這樣才順理成章, api 如何用,往下看:

1 首先要打開drm 驅動模塊,然後獲取所有的資源

fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);

drmModeRes res = drmModeGetResources(fd);

res 裏有啥, res 裏告訴了有幾個connector , 幾個 encoder , 幾個 crtc 等以及他們的id , 完全不成套!

2 從 connector 開始, 順藤摸瓜

先獲取 connecotr 的具體資源

drmModeConnector * conn = drmModeGetConnector(fd, res->connectors[i]);

conn資源裏重要的有兩部分,一部分是通過線纜讀出來顯示器的 "modes " 如 1920x1080@60 等, 當然你要選一個最喜歡的

另一部分是encoder 按照一章節的2順序開始找 encoder (看 drmModeConnector 的定義) :p

3 給encoder 尋找合適的 crtc

按照一章節的 3 順序找 crtc

4 爲 crtc 創建fb

drm 的例子 modetest 寫的很詳細, ARGB 可以直接 drmModeAddFB , 多平面的fb 可以用 drmModeAddFB2

5 綁定

核心四元組 < fb , crtc , conn , mode >

int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId,
uint32_t x, uint32_t y, uint32_t *connectors, int count,

         drmModeModeInfoPtr mode);

6 pageFlip

extern int drmModePageFlip(int fd, uint32_t crtc_id, uint32_t fb_id,
uint32_t flags, void *user_data);

這個的玩法得好好記下,

首先 poll (drm_fd ) 可以收到 drm 的 POLLIN 消息, 消息裏面無非兩種一種是 VBLANK , 一種是 pageFlip complete

其次 收到 消息後必須要 調用drmHandleEvent(drm_fd , &evctx); 來處理消息 ,記得必須把你的 pageFlip 處理函數填到evctx裏,

pageFlip 處理函數裏幹啥呢, pageFlip 一幀結束了,當然要 pageFlip 下一幀!

7 plane

plane 的玩法沒搞明白,嘗試了下,使用多緩衝 調 drmModeSetPlane 來換頁可以實現視頻的播放,但遇到兩個問題:

其一是 SetPlane 的時機, 如何等待一個顯示器的場同步? 看了 vblank 的code 感覺 vblank 是針對一個顯卡的同步,但我有多個顯示器呢?

其二是SetPlane 的運行耗時,我的 i3 cpu 執行一次用了 22 ~ 23 ms , 莫名其妙。

本想着plane 的yuv 通道可以節省資源,有這兩大問題在就沒法辦了, pageFlip 的 fb 只能跟顯示器相同顏色空間,並且通常是 ARGB !!

發佈了229 篇原創文章 · 獲贊 60 · 訪問量 79萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章