1.概要
2.内容
在 Linux 内核的 Direct Rendering Manager (DRM) 框架中,视频模式(也称为显示模式)是指显示器或显示设备的工作参数集合,包括分辨率、刷新率、同步信号的位置等。这些参数共同决定了图像如何在屏幕上显示。
视频模式的主要参数
-
分辨率:指屏幕上的像素数量,通常用水平像素数和垂直像素数表示,例如 1280x720 或 1920x1080。
-
刷新率:指屏幕每秒刷新的次数,以赫兹(Hz)为单位。较高的刷新率可以减少图像的闪烁感,提供更流畅的视觉体验。
-
同步信号:包括水平同步信号(HSYNC)和垂直同步信号(VSYNC),用于确保图像在屏幕上的稳定显示。同步信号的位置和极性(正或负)也是视频模式的重要参数。
-
像素时钟:指驱动显示器所需的时钟频率,以千赫兹(kHz)为单位。这个参数决定了图像数据在屏幕上更新的速度。
-
前端和后端消隐:在水平同步信号和垂直同步信号之间,以及活跃视频区域前后的时间间隔,用于确保图像数据的正确传输和显示。
DRM 中的视频模式
在 DRM 框架中,视频模式由 drm_mode_modeinfo
结构体表示。这个结构体包含了描述一个显示模式所需的所有参数。开发者可以使用 DRM_MODE
宏来初始化这个结构体,从而方便地定义一个视频模式。
示例
以下是一个使用 DRM_MODE
宏定义 1280x720 @ 24Hz 视频模式的示例:
#define DRM_MODE(type, clock, hdisplay, hsync_start, hsync_end, htotal, vdisplay, vsync_start, vsync_end, vtotal, flags) \
{ \
.type = (type), \
.clock = (clock), \
.hdisplay = (hdisplay), \
.hsync_start = (hsync_start), \
.hsync_end = (hsync_end), \
.htotal = (htotal), \
.vdisplay = (vdisplay), \
.vsync_start = (vsync_start), \
.vsync_end = (vsync_end), \
.vtotal = (vtotal), \
.flags = (flags), \
}
struct drm_mode_modeinfo mode = DRM_MODE(
DRM_MODE_TYPE_DRIVER,
59400, /* 像素时钟 */
1280, /* 水平分辨率 */
3040, /* 水平同步信号开始前的像素数 */
3080, /* 水平同步信号结束后的像素数 */
3300, /* 水平总像素数 */
720, /* 垂直分辨率 */
725, /* 垂直同步信号开始前的行数 */
730, /* 垂直同步信号结束后的行数 */
750, /* 垂直总行数 */
0, /* 保留字段 */
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC /* 同步信号极性 */
);
mode.vrefresh = 24; /* 刷新率 */
mode.picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9; /* 图片宽高比 */
实际应用
在实际应用中,视频模式通常由图形驱动程序或显示服务器(如 X.org、Wayland 等)根据显示器的 EDID(扩展显示标识数据)信息自动检测和配置。然而,在某些情况下,用户或开发者可能需要手动配置视频模式,以满足特定的显示需求。
通过 DRM 框架,开发者可以轻松地定义和管理视频模式,从而为用户提供更丰富的显示选项和更灵活的配置能力。
3.关联链接
ITOP-RK3588 HDMI强制分辨率跳过EDID_rk3588 hdmi强制输出-优快云博客
4.关联知识
1.DRM_MODE是个函数吗
DRM_MODE
在 Linux 内核的 Direct Rendering Manager (DRM) 框架中并不是一个函数,而是一个宏(macro)。宏在 C 语言中是一种预处理器指令,用于在编译时对代码进行文本替换。
在 DRM 的上下文中,DRM_MODE
宏通常用于定义一个 drm_mode_modeinfo
结构体的初始化器。这个结构体包含了描述一个显示模式的所有必要参数,比如分辨率、刷新率、同步信号的位置等。
宏的定义可能类似于以下形式(具体定义可能会根据内核版本有所不同):
#define DRM_MODE(type, clock, hdisplay, hsync_start, hsync_end, htotal, vdisplay, vsync_start, vsync_end, vtotal, flags) \
{ \
.type = (type), \
.clock = (clock), \
.hdisplay = (hdisplay), \
.hsync_start = (hsync_start), \
.hsync_end = (hsync_end), \
.htotal = (htotal), \
.vdisplay = (vdisplay), \
.vsync_start = (vsync_start), \
.vsync_end = (vsync_end), \
.vtotal = (vtotal), \
.flags = (flags), \
}
通过这个宏,开发者可以方便地用一行代码来初始化一个 drm_mode_modeinfo
结构体,而不需要逐行设置每个字段。这种宏的使用方式提高了代码的可读性和简洁性。
在使用时,开发者只需提供宏所需的参数,就可以快速定义一个显示模式。例如,前面提到的代码片段就是用 DRM_MODE
宏来定义一个 1280x720 @ 24Hz 的显示模式。
2.解析举例
/* 60 - 1280x720@24Hz */
{ DRM_MODE(DRM_MODE_TYPE_DRIVER, 59400, 1280, 3040,
3080, 3300, 720, 725, 730, 750, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
这段代码定义了一个视频模式,适用于显示设备,特别是在使用Linux内核的Direct Rendering Manager (DRM) 框架时。这个特定的模式是为1280x720分辨率的屏幕设计的,刷新率为24Hz,通常用于视频播放或特定的显示需求。下面是对这段代码中各个参数的解释:
-
DRM_MODE(DRM_MODE_TYPE_DRIVER, 59400, 1280, 3040, 3080, 3300, 720, 725, 730, 750, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC)
:DRM_MODE_TYPE_DRIVER
: 表示这个模式是由驱动程序定义的,而不是标准的VESA模式。59400
: 是像素时钟频率,单位为KHz。这是驱动显示器所需的时钟速度。1280
: 水平分辨率,即屏幕宽度上的像素数。3040
: 水平总宽度,包括活跃视频区域、前端消隐、后端消隐和同步脉冲的总和。3080
: 水平同步脉冲开始的位置。3300
: 水平总周期结束的位置,包括所有水平消隐和同步时间。720
: 垂直分辨率,即屏幕高度上的像素数。725
: 垂直同步脉冲开始之前的行数,包括活跃视频区域和垂直前端消隐。730
: 垂直同步脉冲开始的位置。750
: 垂直总周期,包括所有垂直消隐和同步时间。0
: 保留字段,通常设置为0。DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC
: 标志位,表示水平同步和垂直同步都是正的(即同步脉冲在活跃视频信号之后)。
-
.vrefresh = 24
: 刷新率设置为24Hz,这是每秒屏幕刷新的次数,适合电影播放,因为电影的标准帧率是24帧/秒。 -
.picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9
: 图片宽高比设置为16:9,这是高清电视和大多数现代显示器的标准宽高比。
这个模式配置适用于需要特定分辨率和刷新率的场合,特别是在视频播放或需要精确控制显示输出的应用中。通过调整这些参数,可以适配不同的显示器或满足特定的显示需求。
{
DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250,
1280, 1390, 1430, 1650, 0,
720, 725, 730, 750, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
.picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,
},
这段代码定义了一个视频显示模式,通常用于Linux内核中的DRM(Direct Rendering Manager)框架,特别是与HDMI或类似接口相关联的显示设备。下面是对这段代码的详细解释:
-
DRM_MODE("1280x720", ...)
:定义了一个显示模式,其分辨率为1280x720像素,这是一种常见的高清(HD)分辨率,也被称为720p。 -
DRM_MODE_TYPE_DRIVER
:指示这个模式是由驱动程序提供的,而不是通过自动探测或其他方式获得的。 -
74250
:这是像素时钟频率,单位为千赫兹(KHz)。像素时钟决定了每秒可以传输多少像素数据,是计算其他时序参数的基础。
接下来的四个数字定义了水平扫描的时序参数:
1280
:有效像素宽度,即实际显示区域的宽度。1390
:水平前沿(Horizontal Front Porch),是从有效像素结束到水平同步信号开始之间的像素数。1430
:水平同步脉冲宽度(Horizontal Sync Pulse Width),即水平同步信号的持续时间。1650
:水平总周期(Horizontal Total),是一个水平扫描线的总像素数,包括有效像素、前沿、同步脉冲和后沿。
紧接着的四个数字定义了垂直扫描的时序参数:
-
720
:有效行数,即实际显示区域的行数。 -
725
:垂直前沿(Vertical Front Porch),是从有效行结束到垂直同步信号开始之间的行数。 -
730
:垂直同步脉冲宽度(Vertical Sync Pulse Width),即垂直同步信号的持续时间。 -
750
:垂直总周期(Vertical Total),是一个完整帧的总行数,包括有效行、前沿、同步脉冲和后沿。 -
0
(在两个时序参数列表之后):通常用于指定刷新率或其他特殊参数,但在这里被设置为0,表示使用默认值或没有特殊参数。 -
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC
:这些标志指定了同步信号的极性。PHSYNC
表示水平同步信号是正的(即信号在高电平时有效),PVSYNC
表示垂直同步信号也是正的。 -
.picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9
:指定了图片的宽高比为16:9,这是高清电视和视频内容的标准宽高比。
总的来说,这段代码定义了一个720p分辨率的显示模式,包括其像素时钟、水平和垂直扫描的时序参数,以及同步信号的极性和图片的宽高比。这些信息对于正确配置和驱动显示设备至关重要。
3.ITOP-RK3588 HDMI强制分辨率跳过EDID
在ITOP-RK3588开发板上,如果需要通过HDMI强制输出特定分辨率并跳过EDID(扩展显示标识数据)的读取,您可以按照以下步骤进行配置和修改。EDID通常用于显示设备之间交换有关其显示能力的信息,但在某些情况下,可能需要强制输出特定的分辨率。
一、修改U-Boot代码
-
定位并修改dw_hdmi_qp.c文件
- 打开路径为
~/3588-linux/u-boot/drivers/video/drm/dw_hdmi_qp.c
的文件。 - 注释掉原有的默认分辨率数组:
// const u8 def_modes_vic[6] = {4, 16, 2, 17, 31, 19};
- 添加您需要的分辨率对应的VIC(视频标识码)值。例如,如果希望强制输出1280x720@60Hz的分辨率,其VIC值为4,则修改为:
const u8 def_modes_vic[1] = {4};
- 打开路径为
-
修改edid.c文件
- 打开路径为
~/3588-linux/u-boot/common/edid.c
的文件。 - 找到
static const struct drm_display_mode edid_cea_modes_1[]
数组,确保其中包含了您需要的分辨率模式。
- 打开路径为
二、修改内核代码
-
定位并修改dw-hdmi-qp.c文件
- 打开路径为
~/3588-linux/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
的文件。 - 在
static const struct drm_display_mode dw_hdmi_default_modes[]
数组中添加或修改为您需要的分辨率模式。例如,添加1280x720@60Hz的分辨率模式:static const struct drm_display_mode dw_hdmi_default_modes[] = { /* 4 - 1280x720@60Hz 16:9 */ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390, 1430, 1650, 0, 720, 725, 730, 750, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, // 其他分辨率模式... };
- 打开路径为
-
修改HDMI连接器的模式获取函数
- 在同一个文件中,找到
static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
函数。 - 注释掉EDID读取的代码部分,使系统不依赖EDID来获取分辨率信息:
static int dw_hdmi_connector_get_modes(struct drm_connector *connector) { struct dw_hdmi_qp *hdmi = container_of(connector, struct dw_hdmi_qp, connector); struct hdr_static_metadata *metadata = &connector->hdr_sink_metadata.hdmi_type1; struct edid *edid; struct drm_display_mode *mode; struct drm_display_info *info = &connector->display_info; void *data = hdmi->plat_data->phy_data; int i, ret = 0; if (!hdmi->ddc) return 0; memset(metadata, 0, sizeof(*metadata)); // 注释掉EDID读取部分 // edid = drm_get_edid(connector, hdmi->ddc); edid = NULL; // 其他代码... return ret; }
- 在同一个文件中,找到
三、修改设备树配置
-
定位并修改topeet_screen_lcds.dts文件
- 打开路径为
~/3588-linux/kernel/arch/arm64/boot/dts/rockchip/topeet_screen_lcds.dts
的文件。 - 确保HDMI接口被正确启用,并配置为所需的模式。根据需要修改或添加相关的HDMI接口配置。
- 打开路径为
四、编译和烧写镜像
- 完成上述修改后,重新编译U-Boot和内核镜像。
- 将编译好的镜像烧写到ITOP-RK3588开发板上。
五、注意事项
- 分辨率兼容性:确保强制输出的分辨率被您的显示设备支持,否则可能导致显示异常。
- 系统稳定性:修改底层驱动和系统配置可能会影响系统的稳定性,建议在测试环境中进行充分验证。
- 备份原始文件:在进行任何修改之前,建议备份原始文件,以便在需要时恢复。
通过上述步骤,您可以在ITOP-RK3588开发板上通过HDMI强制输出特定分辨率,并跳过EDID的读取。这在进行特定显示测试或开发时非常有用。