天天看點

修改RK3399 HDMI顯示分辨率

轉載自:

[RK3399] HDMI正常輸出4K30幀 無法輸出60幀以及設定分辨率的流程分析

**

Platform: ROCKCHIP

Chip: RK3399

OS: Android7.1.2

Kernel: 4.4.126*

**

【接上篇】上篇雖然HDMI能正常輸出4K,但是幀率卻達不到60幀,隻能30幀,具體分析如下

按網上和官方的分别試了下修改,均沒成功kernel/driversideo/rockchipmi/rockchip-hdmiv2 中的 rockchip-hdmi.h 檔案,

#define HDMI_VIDEO_DEFAULT_MODE HDMI_1280X720P_60HZ

又在dtsi中加入 rockchip,defaultmode = <4>;也沒有成功。

以上是開機那時候的分辨率。

修改與現象

将輸出特殊分辨率的方法輸出

相關檔案:

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來顯示。

kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c下的函數dw_hdmi_connector_get_modes(),修改def_modes

def_modes[1] = {97};//3840x2160P60
           

燒錄裝置結果:hdmi輸出顯示屏黑屏 cat 裝置結果如下

在這裡插入圖檔描述

特殊分辨率 def_modes[1] = {108};//特殊分辨率 800x1280P60
/* 108 - [email protected] */
kernel/drivers/gpu/drm/drm_edid.c裡edid_cea_modes[]:新增如下特殊分辨率:
{ DRM_MODE(“800x1280”, DRM_MODE_TYPE_DRIVER, 76000, 800, 848,
880, 960, 0, 1280, 1300, 1304, 1314, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
燒錄裝置結果:出圖正常 cat 裝置結果如下
在這裡插入圖檔描述
在這裡插入圖檔描述
           

原因:顯示屏不支援4K60hz,支支援4K30hz,是以導緻裝置分辨率為60hz時黑屏

其工作流程:底層通過edid,讀取到顯示器的分辨率清單,然後回報給Android系統,在系統界面裡顯示出來

設定分辨率的流程分析

先通過i2c擷取屏的edid資訊。

EDID的全稱是Extended Display Identification Data(擴充顯示辨別資料),共有128位元組。其中包含有關顯示器及其性能的參數,包括供應商資訊、最大圖像大小、顔色設定、廠商預設定、

頻率範圍的限制以及顯示器名和序列号的字元串等等。

如何檢視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);配置設定和複制現有模式, 傳回:成功時指向複制模式的指針,錯誤時為空。

           

繼續閱讀