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