环境:Zorin OS 18.1 / Kernel 6.17 / AMD RX 580
问题描述
PC 与 Xbox 共享一台显示器,通过手动切换 HDMI 线使用。当启动 PC 时 HDMI 线未插入显示器,等到反应过来插上时系统已经完成引导,此时显示器提示「请检查线缆」,无任何画面输出,只有强制重启才能恢复。
挂起休眠后恢复同样存在此问题。
根本原因分析
Linux 内核在启动时通过 DRM/KMS 子系统初始化显示输出。amdgpu 驱动在初始化阶段会通过 DDC/I2C 总线读取显示器的 EDID(Extended Display Identification Data),以获取显示器支持的分辨率、刷新率等能力参数。
如果此时 HDMI 线未连接:
- EDID 读取失败,驱动将该输出端口标记为无设备
- 没有 EDID,驱动不知道显示器能接受什么模式,无法初始化输出
- 后续插入 HDMI 线触发的 hotplug 中断,在 amdgpu DCE 11.2 实现下无法完整重新协商模式
内核日志中的表现:
[drm] forcing HDMI-A-1 connector on
*ERROR* EDID err: 2, on connector: HDMI-A-1
*ERROR* Failed to read EDID
*ERROR* No EDID found on connector: HDMI-A-1.
解决思路演进
尝试一:video=HDMI-A-1:D 强制连接器激活
在内核参数中加入 video=HDMI-A-1:D,D 表示 force connected,告知 DRM 子系统无论是否检测到物理设备都保持该端口激活。
结果: 解决了引导阶段的端口禁用问题,但没有 EDID 的情况下系统 fallback 到 640x480 安全分辨率。
尝试二:video=HDMI-A-1:1920x1080@60D 同时指定分辨率
在激活的基础上手动指定目标分辨率。
结果: 失败。内核在没有 EDID 时生成了错误的 modeline(像素时钟 172780 kHz,而标准 1080p@60 应为 148500 kHz),amdgpu 直接拒绝:
amdgpu: [drm] User-defined mode not supported: "1920x1080": 60 172780 ...
随后 gnome-shell 大量报错:
Page flip failed: drmModeAtomicCommit: 无效的参数
Cursor update failed: drmModeAtomicCommit: 无效的参数
尝试三:udev 热插拔规则 + xrandr
写一个 udev 规则,在检测到 DRM 设备变化时执行 xrandr --auto。
结果: 根本性设计错误。udev 运行在系统级守护进程上下文中,没有 DISPLAY 和 XAUTHORITY,xrandr 无法连接到 X 会话,脚本每次超时 59 秒后以 exit code 1 退出:
card0: Spawned process '/usr/local/bin/hdmi-hotplug.sh' [1277] is taking longer than 59s to complete
card0: Process '/usr/local/bin/hdmi-hotplug.sh' failed with exit code 1.
最终方案:EDID 固件 + 强制连接器激活
将显示器的 EDID 预先 dump 出来,作为内核固件安装,让 amdgpu 在没有物理连接时也能读到完整的显示器能力参数。
最终解决方案(完整步骤)
第一步:确认连接器名称
ls /sys/class/drm/
# 示例输出:card1-HDMI-A-1
xrandr | grep connected
# 示例输出:HDMI-1 connected primary 1920x1080+0+0
第二步:趁显示器连接时 dump EDID
sudo apt install read-edid -y
sudo get-edid > /tmp/your_display.bin
# 验证内容
edid-decode /tmp/your_display.bin | head -20
# 确认文件大小(标准 EDID 为 128 字节)
ls -la /tmp/your_display.bin
如果 read-edid 读取失败,用 sysfs 直接读:
sudo cat /sys/class/drm/card1-HDMI-A-1/edid > /tmp/your_display.bin
第三步:安装 EDID 为内核固件
sudo mkdir -p /lib/firmware/edid
sudo cp /tmp/your_display.bin /lib/firmware/edid/your_display.bin
# 将固件打包进 initramfs(重要,否则引导早期读不到)
sudo update-initramfs -u
第四步:修改 GRUB 内核参数
sudo nano /etc/default/grub
找到 GRUB_CMDLINE_LINUX_DEFAULT 这一行,加入两个参数:
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash video=HDMI-A-1:D drm.edid_firmware=HDMI-A-1:edid/your_display.bin"
video=HDMI-A-1:D:强制连接器激活,不因无物理连接而禁用端口drm.edid_firmware=HDMI-A-1:edid/your_display.bin:从固件加载 EDID,给驱动提供模式参数
sudo update-grub
第五步:清理之前错误的 udev 脚本(如有)
sudo rm -f /usr/local/bin/hdmi-hotplug.sh
sudo rm -f /etc/udev/rules.d/95-hdmi-hotplug.rules
sudo udevadm control --reload-rules
验证
重启后检查内核日志,确认 EDID 错误消失:
journalctl -b | grep -i "drm\|hdmi"
修复前的日志(问题状态):
[drm] forcing HDMI-A-1 connector on
*ERROR* EDID err: 2, on connector: HDMI-A-1
*ERROR* No EDID found on connector: HDMI-A-1.
修复后的日志(正常状态):
[drm] forcing HDMI-A-1 connector on
[drm] UVD and UVD ENC initialized successfully.
fbcon: amdgpudrmfb (fb0) is primary device
关于持久性
/lib/firmware/edid/不属于任何内核包,apt 升级内核不会覆盖/etc/default/grub对所有内核版本全局生效- 新内核安装时会自动运行
update-initramfs,/lib/firmware/下的文件会被自动打包进新内核的 initramfs
关于运行时热插拔
引导阶段的问题由上述方案彻底解决。进入桌面后插入 HDMI 线的场景,GNOME/mutter 会自动响应 DRM hotplug 事件并启用输出,无需额外脚本。
如果你的桌面环境不能自动响应,可以考虑用 systemd 用户服务配合 inotifywait 监听 /sys/class/drm/card1-HDMI-A-1/status 状态变化,在用户会话上下文内调用 xrandr --auto(注意不要用 udev 规则调用 xrandr,两者上下文不兼容)。
总结
| 方案 | 结果 | 原因 |
|---|---|---|
video=HDMI-A-1:D | 部分解决 | 激活端口但无 EDID,回退 640x480 |
video=HDMI-A-1:1920x1080@60D | 失败 | 无 EDID 时内核生成错误 modeline,被驱动拒绝 |
| udev + xrandr | 失败 | udev 上下文无法连接 X 会话 |
EDID 固件 + video=D | 成功 | 驱动始终有正确的显示器能力参数 |
核心教训:解决 Linux 显示问题应该优先从驱动获取信息的源头入手,而不是在上层用脚本打补丁。