RK平台主要采用 FB 和 DRM 两种显示框架。与此相对应, HDMI 也有两套驱动。
FB:
LINUX 3.10 内核主要采用传统的 FB 框架, HDMI 驱动的路径为:
kernel/drivers/video/rockchip/hdmi/
DRM:
DRM全称是 Direct Rendering Manager 是 DRI ( Direct Rendering Infrastructure ) 框架的一个
组件。LINUX 4.4/4.19 内核采用 DRM框架, HDMI 驱动的路径为:
kernel/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
kernel/drivers/gpu/drm/bridge/synopsys/
&hdmi {
status = "okay";
};
RK3288平台存在两个 VOP:VOPB(支持 4K)、VOPL(只支持 2K)
,两个 VOP
可以分别与两个显示接口绑定(一个显示接口只能和一个 VOP 绑定),且可以相互交换:
当 dts 中显示设备节点打开时,显示接口对应 VOPB和 VOPL 的 ports 都会打开,所以需要关闭用不到的那个 VOP。 比如 HDMI 绑定到 VOPB 需要添加:
&hdmi_in_vopl {
status = "disabled";
};
反之若绑定到 VOPL 则添加:
&hdmi_in_vopb {
status = "disabled";
};
如果 U-Boot logo 未开启,那 kernel 阶段也无法显示开机 logo,只能等到系统启动后才能看到显示。
在 dts 里面将 route_hdmi 使能即可打开 U-Boot logo 支持:
&route_hdmi {
status = "okay"
};
DRM 框架目前代码已经支持了绝大部分分辨率时序,但是部分 HDMI 屏幕旋转的场景下,可能还有一些特殊分辨率不支持。需要在 kernel\drivers\gpu\drm\drm_edid.c 中的 drm_dmt_modes 当末尾新增:
/* 0x58 - [email protected] RB */
{ DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 556188, 4096, 4104,
4136, 4176, 0, 2160, 2208, 2216, 2222, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
参数 | 说明 |
---|---|
4096x2160 | mode name,为分辨率的 hdisplay*vdisplay |
DRM_MODE_TYPE_DRIVER | mode type,配置为DRM_MODE_TYPE_DRIVER |
556188 | 像素时钟 |
4096 | 行有效像素 |
4104 | 行同步起始像素 |
4136 | 行同步结束像素 |
4176 | 一行总像素 |
0 | hskew,通常为 0 |
2160 | 帧有效行 |
2208 | 帧同步开始行 |
2216 | 帧同步结束行 |
2222 | 一帧总行数 |
0 | vscan, 通常为 0 |
vrefresh | 显示设备帧率 |
DRM_MODE_FLAG_PHSYNC I DRM_MODE_FLAG_NVSYNC | hsync 和 vsync 极性,flags 的定义如下:DRM_MODE_FLAG_PHSYNC (1<<0) DRM_MODE_FLAG_NHSYNC (1<<1) DRM_MODE_FLAG_PVSYNC (1<<2) DRM_MODE_FLAG_NVSYNC (1<<3) DRM_MODE_FLAG_INTERLACE (1<<4) |
当需要无视 EDID 的限制,需要强制输出某个分辨率:
diff --git a/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 8165851656..12137d0c68 100644
--- a/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -2497,7 +2497,7 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
connector);
struct edid *edid;
struct drm_display_mode *mode;
- const u8 def_modes[6] = {4, 16, 31, 19, 17, 2};
+ const u8 def_modes[6] = {30, 16, 31, 19, 17, 2}; //把def_mode数组的第一个值对应的是默认显示hdmi的分辨率对应的 vic(dw-hdmi.c文件中)
struct drm_display_info *info = &connector->display_info;
struct hdr_static_metadata *metedata =
&connector->display_info.hdmi.hdr_panel_metadata;
@@ -2507,6 +2507,7 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
return 0;
edid = drm_get_edid(connector, hdmi->ddc);
+ edid = NULL; //edid = NULL; 强制进入 EDID 读取失败的流程,不管有没有读到 EDID 都强制按def_modes 的分辨率来显示。
if (edid) {
dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n",
edid->width_cm, edid->height_cm);
diff --git a/kernel/drivers/gpu/drm/drm_edid.c b/kernel/drivers/gpu/drm/drm_edid.c
index 3fd2dd5c4a..4cac0b7584 100644
--- a/kernel/drivers/gpu/drm/drm_edid.c
+++ b/kernel/drivers/gpu/drm/drm_edid.c
@@ -820,11 +820,11 @@ static const struct drm_display_mode edid_cea_modes[] = {
1592, 1728, 0, 576, 581, 586, 625, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
.vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
- /* 30 - 1440x576@50Hz */
- { DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464,
- 1592, 1728, 0, 576, 581, 586, 625, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
- .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
+ /* 30 - 3840x1448@60Hz */
+ { DRM_MODE("3840x1448", DRM_MODE_TYPE_DRIVER, 297000, 3840, 3888,
+ 3920, 4400, 0, 1448, 1451, 1456, 1538, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
/* 31 - 1920x1080@50Hz */
{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
如果需要强制显示 4K 的分辨率,还需要注释掉以下代码,解除对 4K 分辨率的限制
:
diff --git a/kernel/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/kernel/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index cabddaa6f2..8cf1b19b7c 100644
--- a/kernel/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/kernel/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -490,10 +490,12 @@ dw_hdmi_rockchip_mode_valid(struct drm_connector *connector,
* If sink max TMDS clock < 340MHz, we should check the mode pixel
* clock > 340MHz is YCbCr420 or not.
*/
+#if 0
if (mode->clock > 340000 &&
connector->display_info.max_tmds_clock < 340000 &&
(!drm_mode_is_420(&connector->display_info, mode)|| !connector->ycbcr_420_allowed))
return MODE_BAD;
+#endif
if (!encoder) {
const struct drm_connector_helper_funcs *funcs;
RK3288 默认 HDMI 声卡和 Codec 公用,要确保以下配置打开:
&hdmi_analog_sound {
status = "okay";
}
cat d/dri/0/summary
/sys/class/drm 目录下可以看到驱动注册的各个显卡,
可以看到注册了 card0-HDMI-A-1 和 card0-DSI-1 两种显示设备,分别表示 HDMI 和 DSI。
以 card0-HDMI-A-1 为例,其目录下有以下文件:
1.Enabled:使能状态
2.Status:连接状态
3.Mode:当前输出分辨率
4.Modes:连接设备支持的分辨率列表
5.Audioformat:连接设备支持的音频格式
6.Edid:连接设备的 EDID,可以通过命令cat edid > /data/edid.bin
保存下来。
使用以下命令查看当前 HDMI 输出状态:
cat /sys/kernel/debug/dw-hdmi/status
强制使能 HDMI:
echo on > /sys/class/drm/card0-HDMI-A-1/status 1
强制禁用 HDMI:
echo off > /sys/class/drm/card0-HDMI-A-1/status 1
恢复检测热插拔:
echo detect > /sys/class/drm/card0-HDMI-A-1/status
相关链接文章:[RK3288][Android5.1] HDMI的使用与调试方法 – (二)