RK3288_Android7.1平臺基於DRM框架的LCD開發

概述

rk3288在kernel4.4開始使用的顯示框架是基於DRM的。DRM全稱是Direct Rendering Manager,管理進行顯示輸出的, buffer分配, 幀緩衝.
libdrm庫提供了一系列友好的控制封裝, 使用戶可以方便的進行顯示的控制, 但並不是 只能通過libdrm庫來控制drm, 用戶可以直接操作drm的ioctl或者是使用framebuffer的 接口實現顯示操作。

設備節點

DRM 的設備節點爲 “/dev/dri/cardX”, X爲0-15的數值.
默認使用的是/dev/dri/card0

節點控制hdmi和lvds的顯示開關

	HDMI:
	echo on > /sys/class/drm/card0-HDMI-A-1/status //打開hdmiout顯示                     
	echo off > /sys/class/drm/card0-HDMI-A-1/status //關閉hdmiout顯示                       
	cat /sys/class/drm/card0-HDMI-A-1/status //打開是connected,關閉是disconnected

	LVDS:
	ls -dl /sys/class/drm/*/st*  && cat /sys/class/drm/card0-LVDS-1/st* 

	echo on > /sys/class/drm/card0-LVDS-1/st*  //開
	echo off > /sys/class/drm/card0-LVDS-1/st*  //關

	cat /proc/device-tree/display-subsystem/route/route-lvds/status

HDMI/DP相關

1、首先需要在對應dts文件使能對應顯示設備節點

打開顯示設備執行相關hdmi的probe函數,註冊顯示設備驅動,如打開 HDMI 需要添加:

&hdmi {
status = "okay";
};

使能顯示接口組件
display-subsystem 註冊會把所有打開的設備以組件的形式加在一起,等所有的組件加載完畢後,統一進行 bind/unbind(綁定或者解綁)

基於component框架, 在probe階段解析dts中各個設備的信息, 加到component match 列表中, 等所有設備加
載完畢後, 就會引發master設備的bind.
drm/rockchip爲什麼需要使用Component?
drm下掛了許多的設備, 啓動順序經常會引發各種問題:
1.一個驅動完全有可能因爲等另一個資源的準備, 而probe deferral, 導致順序不定
2.子設備沒有加載好, 主設備就加載了, 導致設備無法工作
3.子設備相互之間可能有時序關係,不定的加載順序,可能帶來有些時候設備能工作,有些時候又不能工作
4.現在編kernel是多線程編譯的,編譯的前後順序也會影響驅動的加載順序.
這時就需要有一個統一管理的機制, 將所有設備統合起來, 按照一個統一的順序加載,
Display-subsystem正是用來解決這個問題的, 依賴於component的驅動, 通過這個驅動,
可以把所有的設備以組件的形式加在一起, 等所有的組件加載完畢後, 統一進行bind/unbind.

開機 LOGO
如果uboot logo未開啓,那kernel階段也無法顯示開機logo,只能等到android啓動後才能看到顯示;在dts裏面將對應的route使能即可打開uboot logo支持,比如:

打開hdmi的uboot logo顯示:

 &route_hdmi {
	status = "okay"; //okay打開,disabled關閉
	connect = <&vopb_out_hdmi>; //vopb_out_hdmi對應hdmi關聯vopb
};

2、當顯示設備節點打開時,顯示接口對應vopb和vopl的ports都會打開,需要關閉用不到的那個VOP。

比如hdmi綁定到vopb需要添加:

&hdmi_in_vopl {
status = "disabled";
};
反之若綁定到 vopl 則添加:
&hdmi_in_vopb {
status = "disabled";
};

如果平臺只有一個 VOP,可以跳過。

3、查看當前可用顯示分辨率列表

rk3288:/ # cat /sys/class/drm/card0-HDMI-A-1/modes                            
3840x2160p30
3840x2160p30
3840x2160p25
3840x2160p24
1920x1080p60
1920x1080p60
1920x1080p50
1280x720p60
1280x720p50
800x600p75
800x600p72
720x576p50
720x480p60

4、查看當前分辨率

rk3288:/ # cat /sys/class/drm/card0-HDMI-A-1/mode                         
3840x2160p24

5、查看vop狀態(rk3288/3399平臺有2vop,vopb支持4k,vopl支持2k)

說明:
ACTIVE 表示正在運行中,如果是VOP [ff940000.vop]: DISABLED,表示vop沒在運行。;
ff930000對應vopb的寄存器vopb: vop@ff930000;
ff940000對應vopb的寄存器vopl: vop@ff940000;
3840x2160p24是對應當前顯示分辨率和頻率

130|rk3288:/ $ cat /d/dri/0/summary
VOP [ff930000.vop]: ACTIVE //active表示正在運行中,ff930000對應vopb的寄存器vopb: vop@ff930000
    Connector: HDMI-A //對應屏的接口名稱
        overlay_mode[0] bus_format[100a] output_mode[f] color_space[3]
    Display mode: 3840x2160p24 //對應當前顯示分辨率
        clk[297000] real_clk[297000] type[40] flag[5]
        H: 3840 5116 5204 5500
        V: 2160 2168 2178 2250
    win0-0: ACTIVE
        format: AB24 little-endian (0x34324241) SDR[0] color_space[0]
        csc: y2r[0] r2r[0] r2y[0] csc mode[0]
        zpos: 0
        src: pos[0x0] rect[1920x1080]
        dst: pos[0x0] rect[3840x2160]
        buf[0]: addr: 0x0378c000 pitch: 7680 offset: 0
    win1-0: DISABLED
    win2-0: DISABLED
    win2-1: DISABLED
    win2-2: DISABLED
    win2-3: DISABLED
    win3-0: DISABLED
    win3-1: DISABLED
    win3-2: DISABLED
    win3-3: DISABLED
    post: sdr2hdr[0] hdr2sdr[0]
    pre : sdr2hdr[0]
    post CSC: r2y[0] y2r[0] CSC mode[1]
VOP [ff940000.vop]: ACTIVE
    Connector: LVDS
        overlay_mode[0] bus_format[100a] output_mode[0] color_space[0]
    Display mode: 1280x720p60
        clk[74250] real_clk[74250] type[8] flag[5]
        H: 1280 1390 1430 1650
        V: 720 725 730 750
    win0-0: ACTIVE
        format: AB24 little-endian (0x34324241) SDR[0] color_space[0]
        csc: y2r[0] r2r[0] r2y[0] csc mode[0]
        zpos: 0
        src: pos[0x0] rect[1280x720]
        dst: pos[0x0] rect[1280x720]
        buf[0]: addr: 0x05e3c000 pitch: 5120 offset: 0
    win1-0: DISABLED
    win2-0: DISABLED
    win2-1: DISABLED
    win2-2: DISABLED
    win2-3: DISABLED
    win3-0: DISABLED
    win3-1: DISABLED
    win3-2: DISABLED
    win3-3: DISABLED
    post: sdr2hdr[0] hdr2sdr[0]
    pre : sdr2hdr[0]
    post CSC: r2y[0] y2r[0] CSC mode[1]

6、查看HDMI當前輸出狀態

rk3288:/ # cat /d/dw-hdmi/status                                               
PHY: enabled                    Mode: HDMI
Pixel Clk: 297000000Hz          TMDS Clk: 297000000Hz
Color Format: RGB               Color Depth: 8 bit
Colorimetry: ITU.BT709          EOTF: Unsupported

HDMI Output Status 表示當前 PHY 狀態,只有當 PHY 使能的時候纔會有後續打印。
Pixel Clk 表示當前輸出的像素時鐘。
TMDS Clk 表示當前輸出 HDMI 符號率。
Color Format 表示輸出的顏色格式,取值 RGB、YUV444、YUV422、YUV420。
Color Depth 表示輸出的顏色深度,取值 8bit、10bit、12bit、16bit。
Colorimery 表示輸出的顏色標準,取值 ITU.BT601、ITU.BIT709、ITU.BT2020
EOTF 表示輸出的 HDR 電光轉換曲線方式,這裏不支持。

7、查看HDMI的插拔狀態:

rk3288:/ $ cat sys/class/switch/hdmi/state

8、綁定PLL只有RK3399需要)

一般是隻有rk3399需要,但是我之前調試的rk3288雙hdmi out輸出的時候也設置綁定了一下

rk3399 的hdmi所綁定的vop時鐘需要掛載到vpll上,若是雙顯,需將另一個vop時鐘掛
到cpll,這樣可以分出任意dclk的頻率;如當hdmi綁定到vopb時配置

&vopb {
assigned-clocks = <&cru DCLK_VOP0_DIV>;
assigned-clock-parents = <&cru PLL_VPLL>;
};
&vopl {
assigned-clocks = <&cru DCLK_VOP1_DIV>;
assigned-clock-parents = <&cru PLL_CPLL>;
}

當hdmi綁定到vopl時配置:
&vopb {
assigned-clocks = <&cru DCLK_VOP0_DIV>;
assigned-clock-parents = <&cru PLL_CPLL>;
};
&vopl {
assigned-clocks = <&cru DCLK_VOP1_DIV>;
assigned-clock-parents = <&cru PLL_VPLL>;
};

查看顯示時鐘:
rk3288:/ # cat /sys/kernel/debug/clk/clk_summary | grep vop
dclk_vop1 0 2 74250000 0 0
dclk_vop0 2 3 297000000 0 0
hclk_vop0 4 5 49500000 0 0
hclk_vop1 0 4 49500000 0 0
aclk_vop1 0 4 297000000 0 0
aclk_vop0 4 5 297000000 0 0

關於dclk:
即pixel clock, 像素時鐘, 該時鐘由具體的顯示timing決定, 如果dclk不正確, 可能導致fps不對或直接不顯示.edp, mipi, lvds等顯示接口對應dclk的容忍性較好, 有些偏差也不影響正常顯示.
但hdmi, dp等高清顯示接口,是有嚴格要求的, 這類顯示接口的頻率要給的很精準.
像素時鐘,跟數據傳輸有關,液晶面板分辨率越高,像素時鐘信號的頻率也越高。在一行內,像素時鐘的個數與液晶面板一行內所具有的像素數量相等。例如,對於1024×768的液晶面板,
一行有1024個像素,則在一行中(對應於有效視頻區間)像素時鐘的個數也是1024個。

aclk_vop:
如果該時鐘頻率太低, 可能會導致顯示出現抖動, 另外如果aclk 沒有使能的話, 訪問vop的寄存器也可能引發總線掛死

hclk_vop:
如果該時鐘未使能, 不能訪問vop的寄存器, 一但訪問vop寄存器, 會造成總線掛死.

9、主副顯示器設置:

可以在產品配置目錄下的system.prop裏(如 device/rockchip/rk3288/rk3288_mid/system.prop)添加如下兩個屬性:
sys.hwc.device.primary = HDMI-A //設置顯示接口做爲主顯,這裏設置HDMI接口爲主顯
sys.hwc.device.extend = LVDS //設置顯示接口做爲副顯,這裏設置LVDS接口爲副顯

默認情況下(即以上屬性未配置時),不支持熱拔插設備(如 CVBS/MIPI/LVDS 等)會作爲主顯,支持熱插拔設備(如HDMI/DP等)會作爲次顯。通常主、副顯只配置一個顯示接口,
例如RK3399MID SDK默認採用的配置, HDMI作爲主顯示,EDP作爲副顯示。
關於接口名稱可以參見hardware/rockchip/hwcomposer/drmresources.cpp裏的定義:

struct type_name connector_type_names[] = {
{ DRM_MODE_CONNECTOR_Unknown, “unknown” },//未知接口
{ DRM_MODE_CONNECTOR_VGA, “VGA” }, //VGA
{ DRM_MODE_CONNECTOR_DVII, “DVI-I” },//DVI,暫不支持
{ DRM_MODE_CONNECTOR_DVID, “DVI-D” },//DVI,暫不支持
{ DRM_MODE_CONNECTOR_DVIA, “DVI-A” },//DVI,暫不支持
{ DRM_MODE_CONNECTOR_Composite, “composite” },//不支持
{ DRM_MODE_CONNECTOR_SVIDEO, “s-video” },//S端子
{ DRM_MODE_CONNECTOR_LVDS, “LVDS” },//LVDS
{ DRM_MODE_CONNECTOR_Component, “component” },//分量信號YPbPr
{ DRM_MODE_CONNECTOR_9PinDIN, “9-pin DIN” },//不支持
{ DRM_MODE_CONNECTOR_DisplayPort, “DP” },//DP
{ DRM_MODE_CONNECTOR_HDMIA, “HDMI-A” },//HDMI A型口
{ DRM_MODE_CONNECTOR_HDMIB, “HDMI-B” },//HDMI B型口,不支持
{ DRM_MODE_CONNECTOR_TV, “TV” },// CVBS
{ DRM_MODE_CONNECTOR_eDP, “eDP” },//EDP
{ DRM_MODE_CONNECTOR_VIRTUAL, “Virtual” },//不支持
{ DRM_MODE_CONNECTOR_DSI, “DSI” },//MIPI
};

10、主副屏接口查詢:

rk3288:/ $ getprop | grep sys.hwc.device.main //查詢當前主顯的輸出接口
[sys.hwc.device.main]: [HDMI-A-1]

rk3288:/ $ getprop | grep sys.hwc.device.aux //查詢當前副顯的輸出接口
[sys.hwc.device.aux]: [LVDS]

11、通過persist.sys.resolution.main以及persist.sys.resolution.aux設置主副屏分辨率,

每次設置完更新 sys.display.timeline(每次加 1)使分辨率生效,例子如下(可以getprop | grep sys.display.timeline查看下sys.display.timeline屬性):

設置 4k60:
setprop persist.sys.resolution.main 3840x2160@60
setprop sys.display.timeline 1

設置 1080p60:
setprop persist.sys.resolution.main 1920x1080@60
setprop sys.display.timeline 2

12、如何輸出特殊分辨率

相關文件:
kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
kernel/drivers/gpu/drm/drm_edid.c

如上修改:
1、在edid_cea_modes數據的最後定義特殊的分辨率(drm_edid.c文件中)。
2、把def_mode數組的第一個值改成特殊分辨率對應的 vic(vic在drivers/gpu/drm/drm_edid.c文件中edid_cea_modes結構體中).
3、edid = NULL;強制把edid賦爲NULL,不管有沒有讀到edid都強制按def_modes來顯示。

13、modetest

modetest是libdrm源碼自帶的調試工具, 可以對drm進行一些基礎的調試

獲取modetest:

mmm external/libdrm/tests
編譯得到:
zwei@ubt144c:/work/zwei/czd/rk3288_7.1_mid$ find out/target/product/rk3288/ -name modetest
out/target/product/rk3288/symbols/system/bin/modetest
out/target/product/rk3288/system/bin/modetest

14、開機起來設置分辨率流程分析:

首先會通過i2c獲取屏的edid信息。
EDID的全稱是Extended Display Identification Data(擴展顯示標識數據),共有128字節。其中包含有關顯示器及其性能的參數,包括供應商信息、最大圖像大小、顏色設置、廠商預設置、
頻率範圍的限制以及顯示器名和序列號的字符串等等。形象地說,EDID就是顯示器的身份證、戶口本、技能證書等證件的集合,目的就是告訴別人我是誰,我從哪來,我能幹什麼。

如何查看edid信息?
cat sys/class/drm/card0-HDMI-A-1/edid > /data/edid.bin //然後把edid.bin拷貝出來
或者:cat sys/class/drm/card0-HDMI-A-1/edid |busybox hexdump

驅動文件清單
通用屏配置驅動:
drivers/gpu/drm/panel/panel-simple.c //.c文件裏面包含mipi的時序參數配置和mipi一些接口還有背光的使能關閉等,配置屏參可以在此文件或者dts

Core:
drivers/gpu/drm/rockchip/rockchip_drm_drv.c
framebuffer drivers/gpu/drm/rockchip/rockchip_drm_fb.c
gem drivers/gpu/drm/rockchip/rockchip_drm_gem.c

vop:
drivers/gpu/drm/rockchip/rockchip_drm_vop.c
drivers/gpu/drm/rockchip/rockchip_vop_reg.c

lvds:
drivers/gpu/drm/rockchip/rockchip_lvds.c

rga:
drivers/gpu/drm/rockchip/rockchip_drm_rga.c

mipi:
drivers/gpu/drm/rockchip/dw-mipi-dsi.c

hdmi:
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
drivers/gpu/drm/bridge/dw-hdmi.c

inno hdmi:
drivers/gpu/drm/rockchip/inno_hdmi.c

edp:
drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c

dp:
drivers/gpu/drm/rockchip/cdn-dp-core.c
drivers/gpu/drm/rockchip/cdn-dp-reg.c

具體相關接口分析:

基於component框架, 在probe階段解析dts中各個設備的信息, 加到component match 列表中, 等所有設備加
載完畢後, 就會引發master設備的bind.

Rockchip drm主設備驅動:drivers/gpu/drm/rockchip/rockchip_drm_drv.c
drivers/gpu/drm/rockchip/rockchip_drm_drv.h

VOP驅動:drivers/gpu/drm/rockchip/rockchip_drm_vop.c
drivers/gpu/drm/rockchip/rockchip_vop_reg.c

圖層接口:
static const struct drm_plane_helper_funcs plane_helper_funcs = {
// 預先對圖層進行處理
.prepare_fb = vop_plane_prepare_fb,
// 圖層顯示完成後的處理
.cleanup_fb = vop_plane_cleanup_fb,
// 在顯示前進行參數檢查
.atomic_check = vop_plane_atomic_check,
// 更新圖層參數
.atomic_update = vop_plane_atomic_update,
// 關閉圖層
.atomic_disable = vop_plane_atomic_disable,
};

VOP接口:
static const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = {
// 使能vop, 在這裏面會將timing配好
.enable = vop_crtc_enable,
// 關閉vop
.disable = vop_crtc_disable,
// 對timing進行檢查修正
.mode_fixup = vop_crtc_mode_fixup,
// 在一幀顯示開始前做的處理
.atomic_begin = vop_crtc_atomic_begin,
// 檢查顯示的參數
.atomic_check = vop_crtc_atomic_check,
// 提交硬件顯示
.atomic_flush = vop_crtc_atomic_flush,
};

接口執行順序:
vop_plane_atomic_check //在顯示前進行參數檢查
vop_plane_prepare_fb //預先對圖層進行處理
vop_plane_atomic_update //更新圖層參數
vop_crtc_atomic_flush //提交硬件顯示
vop_plane_cleanup_fb //圖層顯示完成後的處理

對應文件:
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
./drivers/gpu/drm/drm_edid.c

調用get_modes獲取分辨率的時候會去調用


.get_modes = dw_hdmi_connector_get_modes
									->edid = drm_get_edid(connector, hdmi->ddc); //獲取edid
										->drm_do_get_edid
											->drm_do_probe_ddc_edid //嘗試通過i2獲取edid信息,成功時爲0,失敗時爲-1。
												->drm_get_displayid //如果edid存在,則獲取顯示id

如果獲取到edid,則調用:

				 hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid); //檢測顯示器是否是HDMI,是就返回true,否則return false;
					->drm_find_cea_extension //
						->drm_find_edid_extension //在edid中搜索CEA擴展塊
							->if (cea_db_is_hdmi_vsdb(&edid_ext[i]))
									return true; //因爲HDMI標識符在特定於供應商的塊中,所以從CEA擴展的所有數據塊中搜索它。
				 
                 hdmi->sink_has_audio = drm_detect_monitor_audio(edid); //檢測顯示器audio音頻功能,如果顯示器支持音頻,就返回true,否則return false;
				 
                 drm_mode_connector_update_edid_property(connector, edid); //更新連接器的edid屬性
				 
                 cec_notifier_set_phys_addr_from_edid(hdmi->cec_notifier, edid); 
				 
                 ret = drm_add_edid_modes(connector, edid); //從連接的顯示器中讀取到的edid數據添加分辨率[mode](如果mode可用),
*edid spec說 mode(分辨率)應該按以下順序優先選擇:

	*-首選 詳細 mode(分辨率)

	*-來自基礎塊的其他詳細 mode(分辨率)

	*-擴展塊的詳細 mode(分辨率)

	*-CVT 3字節代碼 mode(分辨率)

	*-標準定時代碼

	*-已建立的時間代碼

	*-根據GTF或CVT範圍信息推斷的 mode(分辨率)


     /* Store the ELD */
    drm_edid_to_eld(connector, edid); //從edid構建ELD, 填充ELD(類似於edid的數據)緩衝區以傳遞給音頻驅動程序。這個HDCP和端口字段留給圖形驅動程序填寫。

    drm_mode_connector_update_hdr_property(connector, metedata); //原子替換現有blob屬性

如果獲取不到edid,則調用:

				 hdmi->sink_is_hdmi = true;
				 hdmi->sink_has_audio = true;			 
				 for (i = 0; i < sizeof(def_modes); i++) { //去遍歷def_mode數組的值,他們對應在edid_cea_modes裏面的分辨率[mode]-->取自CEA-861規範。
					mode = drm_display_mode_from_vic_index(connector, def_modes, 31, i); //調用該接口來設置def_modes數組中第一個分辨率值
						->調用vic = svd_to_vic(video_db[video_index]); // vic值對應 edid_cea_modes 中的分辨率參數
							->if (!drm_valid_cea_vic(vic))
								->vic > 0 && vic < ARRAY_SIZE(edid_cea_modes); 則設置 edid_cea_modes 裏面相應的分辨率
									->newmode = drm_mode_duplicate(dev, &edid_cea_modes[vic]);
										->drm_mode_copy(nmode, mode);分配和複製現有模式, 返回:成功時指向複製模式的指針,錯誤時爲空。
																								  


## 15、Dclk 頻率(像素時鐘)
只要是數字信號處理電路,就必須有時鐘信號。在液晶面板中,像素時鐘是一個非常重要的時鐘信號。像素時鐘信號的頻率與液晶面板的工作模式有關,液晶面板分辨率越高,像素時鐘信號的頻率也越高。
在一行內,像素時鐘的個數與液晶面板一行內所具有的像素數量相等。例如,對於1024×768的液晶面板,一行有1024個像素,則在一行中(對應於有效視頻區間)像素時鐘的個數也是1024個。
無論對TTL接口液晶面板,還是對LVDS接口面板,像素時鐘信號都有以下兩個方面的作用:
(1)指揮RGB信號按順序傳輸。像素時鐘信號就像指揮員指揮隊伍時發出的口令“一、二,一、二……”,數字RGB信號在像素時鐘信號的作用下,按照一定的順序,由驅動板傳輸到液晶面板中,使各電路按照
一定的節拍協調地工作。
(2)確保數據傳輸的正確性。無論是驅動板電路,還是液晶面板電路,在讀取數字RGB信號時,都是在像素時鐘的作用與控制下進行的,各電路只有在像素時鐘的下降沿(或上升沿)到來時纔對數字RGB數
據進行讀取,以確保讀取數據的正確性。

																								  
## HAL層代碼目錄:	/hardware/rockchip/hwcomposer(待分析)	

																					  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
























																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
																								  
























發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章