让 Linux 读懂显示器:EDID 的简述及使用技巧
让操作系统自动识别显示器、为我们推荐最佳分辨率背后所依赖的一项关键技术,就是「EDID」。本文将介绍 EDID 基本概念及其在 Linux 下的使用与调试方法。能够帮助我们在 Linux 操作系统下更好地理解和配置显示设备。
引言:为什么要关心 EDID?
从最早的 CRT 显示器到如今的高分辨率 LCD、OLED 屏甚至是各种 AR/VR头显,显示器时刻承担着人机交互的重任。在操作系统和显示器的沟通过程中,有一份「显示器身份证」极其重要,它就是 EDID(Extended Display Identification Data)。
当我们将显示器接入电脑主机时,操作系统就会读取这份 EDID 以了解显示器支持哪些分辨率、刷新率、色彩特性等,从而自动选择合适的显示模式。了解了 EDID 后,当我们遇到分辨率异常或显示器数据无法识别的问题,就可以有针对性地进行排查和调试了。
EDID 基础:它的重要性与本质
EDID 是什么?
-
基本概念
EDID,是「Extended Display Identification Data」的缩写,由 VESA(Video Electronics Standards Association,视频电子标准协会开发)提出,1.0 版于 1994 年在 DDC 标准 1.0 版中推出。它以一段或多段 128 字节结构来描述显示器的各项属性。可以把 EDID 认为是显示器的身份证,它将显示器的基本信息:分辨率上限、可支持的刷新率范围、生产厂商、型号等等,都浓缩在这小小的空间里。
-
工作原理概览
在生产显示器时,显示器厂家将一个 ROM 添加到他们的产品中,其中包含描述其显示器的信息,并根据指定的格式进行格式化。当电脑和显示器之间建立连接时(无论是通过 HDMI、DVI、DisplayPort 还是老旧的 VGA),显示器会通过 DDC(Display Data Channel)把 EDID 传给显卡或操作系统利用标准化协议来读取和解析该数据。
这样,操作系统知道了“这台显示器是谁”“能显示什么分辨率”等关键信息,操作系统便可以提供显示器和显卡支持的一组分辨率和其他显示参数,用户可以从中进行选择合适的配置进行配置。无需再阅读手册或猜测图形适配器和显示器是否支持特定分辨率。
EDID 发展时间线
从 1994 年 1.0 版本开发至今,随着显示器技术和功能的更迭,最初的 128 字节不够用了,因此指定了新的标准 E-EDID 以及目前最新的 DisplayID 标准以提供每个最大 256 字节的可变长度的数据结构。
日期 | EDID 版本 | 说明 |
---|---|---|
1994年8月 | EDID v1.0 | 定义了通过 E-DDC 线路从视频显示器发送到源的基本数据结构,大小为128字节(已弃用) |
1996年4月 | EDID v1.1 | 定义了数据结构中空间的一些替代用途(已弃用) |
1997-11 | EDID v1.2 EDID 2.0 | 定义了数据结构中空间的一些替代用途(已弃用) 引入了新的256字节数据结构 |
1999年9月 | EDID v1.3 E-EDID v1.0 | E-EDID 标准版 A -为 EDID 1.3定义了可选的额外128字节扩展块,并将EDID 2.0作为可选扩展 |
2000年2月 | E-EDID v1.3 | E-EDID 标准发布版 A - 推出基于 EDID v1.3 的 E-EDID v1.3(用于 HDMI)。(EDID v2.0 已弃用) |
2006年9月 | EDID v1.4 E-EDID v1.4 | E-EDID 标准发布版 A - 引入基于 EDID v1.4 的 E-EDID v1.4。 |
2007年12月 | DisplayID v1.0 | 引入最大256字节可变长度数据结构。旨在取代 E-EDID 标准和 EDID v1.4 |
2009年3月 | DisplayID v1.1 | |
2011年8月 | DisplayID v1.2 | |
2013年6月 | DisplayID v1.3 | |
2017年9月 | DisplayID v2.0 | 添加了通用信息块 |
2021 | DisplayID v2.1 | 与 v2.0 的结构相同,但增加了新的数据块 |
表 1: EDID/E-EDID/DisplayID 历史 |
EDID 信息长什么样?
- 整体来说,EDID 是由固定大小的字节块(较常见的是 128 字节或 256 字节)组成,各字段的位置和长度都有明确的标准定义。
- 其中包含厂商 ID、产品序列号、生产日期、可支持的分辨率和刷新率、色彩特性,以及一些扩展信息(如音频支持、HDR、FreeSync 等)在拓展块中进一步说明。
这里给出我笔记本电脑屏幕的 EDID 十六进制,以供参考:
00ff ffff ffff ff00 09e5 9308 0000 0000
141d 0104 a51e 1478 0287 20a3 554c 9b27
0f50 5400 0000 0101 0101 0101 0101 0101
0101 0101 0101 7a50 70a0 80a0 2850 3020
3a00 28c5 1000 001a 0000 0000 0000 0000
0000 0000 0000 0000 001a 0000 00fe 0042
4f45 2058 530a 2020 2020 2020 0000 00fe
0054 5631 3430 5754 4d2d 4e48 300a 00be
EDID前8个Byte为固定内容,即0x00、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0xFF、0x00。若EDID头码不是上述固定值,则无法被识别。
在 Linux 下如何获取 EDID?
关于如何获取显示器 EDID 信息,这里主要讨论如何获取 EDID 的原始十六进制数据。并且由于 Linux 发行版以及相关工具众多,会优先介绍不使用第三方工具的情况下获取数据。且本文基于 archLinux 环境测试。
1. 从 Xorg 日志中读取
不管怎么说,在 Linux 系统中,我最先接触到 EDID 信息是在 Xorg 日志中,因此通过 Xorg 的日志来获取也是一种较简便的方法:
[skyzcyou@arch-honor ~]$ cat /var/log/Xorg.0.log |grep -A 15 EDID
[ 6.377] (II) modeset(0): EDID for output eDP-1
[ 6.377] (II) modeset(0): Manufacturer: BOE Model: 893 Serial#: 0
[ 6.377] (II) modeset(0): Year: 2019 Week: 20
[ 6.377] (II) modeset(0): EDID Version: 1.4
[ 6.377] (II) modeset(0): Digital Display Input
[ 6.377] (II) modeset(0): 8 bits per channel
[ 6.377] (II) modeset(0): Digital interface is DisplayPort
[ 6.377] (II) modeset(0): Max Image Size [cm]: horiz.: 30 vert.: 20
[ 6.377] (II) modeset(0): Gamma: 2.20
[ 6.377] (II) modeset(0): No DPMS capabilities specified
[ 6.377] (II) modeset(0): Supported color encodings: RGB 4:4:4
[ 6.377] (II) modeset(0): First detailed timing is preferred mode
[ 6.377] (II) modeset(0): Preferred mode is native pixel format and refresh rate
[ 6.377] (II) modeset(0): redX: 0.639 redY: 0.332 greenX: 0.298 greenY: 0.608
[ 6.377] (II) modeset(0): blueX: 0.152 blueY: 0.061 whiteX: 0.312 whiteY: 0.328
[ 6.377] (II) modeset(0): Manufacturer\'s mask: 0
[ 6.377] (II) modeset(0): Supported detailed timing:
[ 6.377] (II) modeset(0): clock: 206.0 MHz Image Size: 296 x 197 mm
[ 6.377] (II) modeset(0): h_active: 2160 h_sync: 2208 h_sync_end 2240 h_blank_end 2320 h_border: 0
--
[ 6.377] (II) modeset(0): EDID (in hex):
[ 6.377] (II) modeset(0): 00ffffffffffff0009e5930800000000
[ 6.377] (II) modeset(0): 141d0104a51e1478028720a3554c9b27
[ 6.377] (II) modeset(0): 0f505400000001010101010101010101
[ 6.377] (II) modeset(0): 0101010101017a5070a080a028503020
[ 6.377] (II) modeset(0): 3a0028c51000001a0000000000000000
[ 6.377] (II) modeset(0): 0000000000000000001a000000fe0042
[ 6.377] (II) modeset(0): 4f452058530a202020202020000000fe
[ 6.377] (II) modeset(0): 00545631343057544d2d4e48300a00be
[ 6.377] (II) modeset(0): Printing probed modes for output eDP-1
[ 6.377] (II) modeset(0): Modeline "2160x1440"x60.0 206.02 2160 2208 2240 2320 1440 1443 1453 1480 +hsync -vsync (88.8 kHz eP)
[ 6.377] (II) modeset(0): Modeline "1920x1440"x60.0 234.00 1920 2048 2256 2600 1440 1441 1444 1500 -hsync +vsync (90.0 kHz d)
[ 6.377] (II) modeset(0): Modeline "1856x1392"x60.0 218.30 1856 1952 2176 2528 1392 1393 1396 1439 -hsync +vsync (86.4 kHz d)
[ 6.377] (II) modeset(0): Modeline "1792x1344"x60.0 204.80 1792 1920 2120 2448 1344 1345 1348 1394 -hsync +vsync (83.7 kHz d)
[ 6.377] (II) modeset(0): Modeline "2048x1152"x120.0 406.50 2048 2220 2444 2840 1152 1153 1156 1193 doublescan -hsync +vsync (143.1 kHz d)
[ 6.377] (II) modeset(0): Modeline "2048x1152"x120.0 302.50 2048 2072 2088 2128 1152 1153 1156 1185 doublescan +hsync -vsync (142.2 kHz d)
终端会输出一段十六进制字符串,就是 EDID 原始数据。借助这份数据,你就能进一步解析它的含义。
2. 从 /sys/devices 读取
# 列出显卡信息
$ lspci |grep VGA
00:02.0 VGA compatible controller: Intel Corporation Alder Lake-P GT2 [Iris Xe Graphics] (rev 0c)
# 查找 edid 文件
$ sudo find /sys/devices/ |grep -i edid
/sys/devices/pci0000:00/0000:00:02.0/drm/card1/card1-HDMI-A-1/edid
/sys/devices/pci0000:00/0000:00:02.0/drm/card1/card1-DP-2/edid
/sys/devices/pci0000:00/0000:00:02.0/drm/card1/card1-eDP-1/edid
/sys/devices/pci0000:00/0000:00:02.0/drm/card1/card1-DP-1/edid
# 以十六进制输出
$ xxd /sys/devices/pci0000:00/0000:00:02.0/drm/card1/card1-eDP-1/edid
00000000: 00ff ffff ffff ff00 09e5 9308 0000 0000 ................
00000010: 141d 0104 a51e 1478 0287 20a3 554c 9b27 .......x.. .UL.'
00000020: 0f50 5400 0000 0101 0101 0101 0101 0101 .PT.............
00000030: 0101 0101 0101 7a50 70a0 80a0 2850 3020 ......zPp...(P0
00000040: 3a00 28c5 1000 001a 0000 0000 0000 0000 :.(.............
00000050: 0000 0000 0000 0000 001a 0000 00fe 0042 ...............B
00000060: 4f45 2058 530a 2020 2020 2020 0000 00fe OE XS. ....
00000070: 0054 5631 3430 5754 4d2d 4e48 300a 00be .TV140WTM-NH0...
3. 从 /sys/class/drm/ 读取
Linux 内核通过 DRM (Direct Rendering Manager) 将 EDID 文件挂载于 /sys/class/drm/
下,如 card0-eDP-1/edid
、card0-HDMI-A-1/edid
等路径。
可以像读取一个普通文件一样获取 EDID:
cat /sys/class/drm/card0-eDP-1/edid | hexdump
这样同样可以得到一长串十六进制原始数据用于后续分析。
4. 使用 read-edid
安装 read-edid
工具,它会提供 read-edid
以及 parse-edid
两个命令行工具:
sudo pacman -S read-edid
输出16进制的 EDID 数据:
$ sudo get-edid | hexdump
This is read-edid version 3.0.2. Prepare for some fun.
Attempting to use i2c interface
No EDID on bus 0
No EDID on bus 1
...
No EDID on bus 13
1 potential busses found: 9
256-byte EDID successfully retrieved from i2c bus 9
Looks like i2c was successful. Have a good day.
0000000 ff00 ffff ffff 00ff e509 0893 0000 0000
0000010 1d14 0401 1ea5 7814 8702 a320 4c55 279b
0000020 500f 0054 0000 0101 0101 0101 0101 0101
0000030 0101 0101 0101 507a a070 a080 5028 2030
0000040 003a c528 0010 1a00 0000 0000 0000 0000
0000050 0000 0000 0000 0000 1a00 0000 fe00 4200
0000060 454f 5820 0a53 2020 2020 2020 0000 fe00
0000070 5400 3156 3034 5457 2d4d 484e 0a30 be00
0000080 0000 0000 0000 0000 0000 0000 0000 0000
*
00000f0 0000 0000 0000 0000 0000 0000 0000 5200
0000100
这里可以注意到,使用 hexdump 输出的十六进制数据与前面几种方法输出高低位是相反的。
这是因为cat
命令和hexdump
都是以字节为单位显示数据的,而hexdump
默认以小端序(little-endian)格式显示数据,即低位字节在前,高位字节在后。
相比之下,xxd
命令默认以大端序(big-endian)格式显示数据,即高位字节在前,低位字节在后。
这里贴一下 get-edid
的输出,可以大概看看出其工作流程:
- 首先会尝试扫描 I2C 总线。
- 对于每个扫描到的总线,尝试通过 I2C 接口与连接的显示器通信,以读取显示器的 EDID 数据。
- 若成功获取到 EDID 数据,便将其显示与输出。
猜测 get-edid
读取方法与 Xorg 是类似的,都是依赖 I2C 接口进行读取。详细需要去阅读相关代码,本文目前只探究其获取与使用方法,用于定位 Linux 显示相关问题。
当然也可以使用 parse-edid
来将其解析为人类可读状态:
sudo get-edid | parse-edid
TODO:
解析 EDID:读懂显示器的身份信息
EDID 的常见使用场景与调试
自动匹配分辨率
当你插入一个新显示器或在笔记本上外接扩展屏时,桌面环境(GNOME、KDE 等)会自动根据 EDID 内的信息设置最佳分辨率。这通常能省掉大量的手动配置,尤其是多显示器场景下,可以轻松搞定扩展桌面、镜像模式、排列位置等。