适用对象:Deepin 25 (ostree 不可变系统 + treeland 合成器架构) 症状关键词:开机分辨率变小、重启后要手动调回分辨率、登录界面显示巨大、桌面只占屏幕左上角一小块、拔插 HDMI 线就能恢复 文中所有 【替换】 标记处,请改成你自己机器的对应信息。
【替换】
机器上有两块屏:内屏(笔记本屏,1920x1080)+ 外接 4K 显示器(3840x2160@60,仅使用外接屏)。
故障时 phenomena 分两种,且经常交替出现:
共同点:每次冷开机/重启都出现,手动在控制中心改一次分辨率就恢复,下次开机又复发。拔插 HDMI 线也能恢复。
/usr
cat /etc/os-release
deepin-immutable-ctl status
systemctl status treeland.service
lspci -k | grep -A3 VGA
uname -r
这类问题横跨四层:GRUB → 内核(EDID/DRM) → 合成器(treeland) → 显示布局(dde-daemon)。 每一层都有独立判据。我这次排查最大的教训就是:探针取值方式错了,连续三轮把病灶误判在内核层,而真实问题一半在布局、一半在 greeter 配置。
改 GRUB_GFXMODE 在 Deepin 25 上走不通,三条证据:
GRUB_GFXMODE
# 1) /etc/default/grub.d/11_dde.cfg 会覆盖你改的 /etc/default/grub cat /etc/default/grub.d/11_dde.cfg # 2) 改完会被 deepin-grub2 服务整条覆盖回来 journalctl -b | grep -i "grub-gfx" # 会出现: invalid gfxmode "3840x2160,1920x1080" —— 它的正则只接受单个 宽x高 # 3) 决定性证据:GRUB 阶段根本没有 4K 可选(UEFI GOP 限制) gdbus call --system --dest org.deepin.dde.Grub2 \ --object-path /org/deepin/dde/Grub2 \ --method org.deepin.dde.Grub2.GetAvailableGfxmodes
同时确认内核侧其实是好的(fb0 已经是 4K,说明 GRUB 只影响菜单那 1 秒):
cat /sys/class/graphics/fb0/virtual_size # 3840,2160 → 内核 fb 层没问题
dde-daemon 的显示器 UUID = @HDMI-A-1||v1,其中 hash = md5(EDID 的前 128 字节)。
@HDMI-A-1||v1
# 算当前 EDID 的 UUID 哈希(注意:是前 128 字节,不是整份 256 字节!) head -c 128 /sys/class/drm/card0-【替换:HDMI-A-1】/edid | md5sum # 看显示配置里存了哪些 UUID —— 大于 1 套 = EDID 读取过不同结果,即 EDID 不稳 sudo grep -o '@HDMI-A-[0-9]*|[0-9a-f]\{32\}' /var/lib/dde-daemon/display/config.json | sort -u
完整故障链(这条链解释了所有现象):
冷启动 DDC 读 EDID,个别字节被读坏 → md5 变成配置里不存在的"另一套 UUID" → config.json 查不到该 UUID → dde 回退默认值:低分辨率 + 默认布局 → 登录界面被放大("很大")、桌面只画左上角一小块 → 手动改分辨率 / 拔插 HDMI 时 EDID 重读成功 → 恢复,但下次冷启动再来一遍
Deepin 25 的登录界面由系统级 treeland.service 渲染(运行身份 User=dde,uid 990,HOME=/var/lib/ddm)。关键坑:全机只有一个真合成器,登录后的 dde-shell/treeland-sd 只是客户端。它的日志默认 StandardOutput=null,journal 里一条都没有——先打开日志:
treeland.service
User=dde
StandardOutput=null
sudo mkdir -p /etc/systemd/system/treeland.service.d # drop-in 内容:StandardOutput=journal / StandardError=journal sudo systemctl daemon-reload && sudo systemctl restart treeland.service # 会结束当前会话! journalctl -u treeland.service -b # 若看到 "Ignoring invalid output dconfig for ..." 说明 greeter 的输出配置没写对/没写上
一条命令定性:
gdbus call --system --dest org.deepin.dde.Display1 \ --object-path /org/deepin/dde/Display1 --method org.deepin.dde.Display1.GetConfig # 对照物理现实: for d in /sys/class/drm/card*-*/; do printf "%-18s %-12s %s\n" "$(basename $d)" "$(cat $d/status)" "$(cat $d/enabled)"; done
典型错误配置(本机实测):
DisplayMode = 2 (2=扩展) 组合屏组 Extend: eDP 1920x1080 (0,0) 主屏=True ← 物理上 enabled=disabled(幻影屏!) HDMI 3840x2160 (1920,0) 副屏
物理只开 1 块屏、配置里开着 2 块 → 按"主屏尺寸"布点的 dde-shell/桌面/任务栏只落在 (0,0) 那块 1920x1080 的方块里。**DisplayMode 取值:1=镜像 2=扩展 3=仅一个屏幕。**
另外注意:Display1 是按需激活的 D-Bus 服务,会话早期(登录后 3~23 秒)一定不在位,会话组件不会因它后来上线而自动重排——这就是"每次开机都要手动改一次"的机制。让保存的布局等于物理现实是唯一稳定解法。
Display1
原理:把一份好的 EDID 固定下来,内核每次启动都用它 → 前 128 字节恒定 → UUID 恒定 → 永远命中同一份配置。
前提检查:
zcat /proc/config.gz | grep CONFIG_DRM_LOAD_EDID_FIRMWARE # 必须 =y grep MODULES /etc/initramfs-tools/initramfs.conf # 应为 most
先抓一份好 EDID(在显示正常时抓):
sudo cp /sys/class/drm/card0-【替换:HDMI-A-1】/edid /etc/firmware-edid/【替换:my-monitor.bin】
三件套:
/etc/firmware-edid/【替换:my-monitor.bin】
/etc
/lib/firmware
/etc/initramfs-tools/hooks/edid-hdmi
${DESTDIR}/lib/firmware/edid/
/etc/default/grub
GRUB_CMDLINE_LINUX_DEFAULT
drm.edid_firmware=【替换:HDMI-A-1:my-monitor.bin】 firmware_class.path=/etc/firmware-edid
为什么需要 firmware_class.path=:实测 Deepin 25 上 amdgpu 不是在 initramfs 阶段加载, 而是 switch_root 后由 udev 从真实根 modprobe。那一刻固件搜索路径是真实的 /lib/firmware, 而 /usr(/lib 指向它)是只读镜像,里面没有你塞的文件 → failed to load edid/xxx.bin (-2)。 firmware_class.path 让内核先去 /etc/firmware-edid 找,绕开只读的 /usr。
firmware_class.path=
failed to load edid/xxx.bin (-2)
firmware_class.path
/etc/firmware-edid
然后 sudo update-grub && sudo update-initramfs -u -k all,重启。
sudo update-grub && sudo update-initramfs -u -k all
EDID 里除了 4K,还声明了一堆 720x400/640x480/1024x768 等小模式(established/standard timings + CTA 扩展里的 VIC)。模式列表按 4K 在前、垃圾在尾部排列;treeland 在没有合法输出配置时会落到列表末尾 → 720x400 → 4K 屏放大 5 倍 = 登录界面巨大。
裁剪思路(对标准 EDID 通用,Python 改字节即可):
# base block b[0x23]=b[0x24]=b[0x25]=0 # established timings 清零 → 去掉 720x400@70/640x480/800x600/1024x768 for i in range(8): # standard timings 全写 unused b[0x26+i*2]=b[0x26+i*2+1]=0x01 # DTD 区 0x36-0x7E 一字不动(DTD1=首选分辨率,还有 monitor name/range 描述符,动了坏事) b[127] = (0x100 - sum(b[:127]) & 0xFF) & 0xFF # 重算 base 校验和 # CTA 扩展块(128~255 字节):Video Data Block 只留 4K 的 VIC(HDMI 下 VIC 93~97) # 删掉所有 CTA-DTD 小模式;保留 HDMI VSDB / HDMI Forum VSDB(后者声明 Max TMDS 600MHz, # 删了内核会按 HDMI 1.4 的 280MHz 拒掉 4K60);重算 c[127] 校验和
裁完必须核验:① 两段校验和都对;② DTD 时序区一字未改(用 cmp -l 看差异位置);③ 新 EDID 的 md5 与旧不同。
cmp -l
⚠️ 裁 EDID 会改变 md5 → dde 显示器 UUID 变 → 已存配置全部失配,必须和第 3 步一起做,不能只裁一半。
用 Display1.GetConfig → 只改目标字段 → SetConfig 原样发回(round-trip 是关键,手搓 JSON 极易被 schema 拒掉)。要改四处:
Display1.GetConfig
SetConfig
DisplayMode = 3
Enabled=true, X=0, Y=0, Primary=true
OnlyOneUuid
Enabled=false
改完验证:物理已启用屏数 == 配置里启用屏数。
物理已启用屏数 == 配置里启用屏数
DConfig override 的位置和格式两个都极易写错:
$HOME/.local/share/dsg/configs/...
$HOME/.config/dsg/configs/overrides//.json
magic: "dsg.config.override"
greeter 的 home 是 /var/lib/ddm,所以目标路径:
/var/lib/ddm
/var/lib/ddm/.config/dsg/configs/overrides/org.deepin.dde.treeland/org.deepin.dde.treeland.output.json
格式(每个键从 /usr/share/dsg/configs/org.deepin.dde.treeland/ 的 schema 整份拷贝后只改 value):
/usr/share/dsg/configs/org.deepin.dde.treeland/
value
{ "magic": "dsg.config.override", "version": "1.0", "contents": { "width": { "value": 【替换:3840】, "serial": 0, "flags": [], "permissions": "readwrite", "visibility": "public" }, "height": { "value": 【替换:2160】, "serial": 0, "flags": [], "permissions": "readwrite", "visibility": "public" }, "refresh": { "value": 【替换:60000】, "serial": 0, "flags": [], "permissions": "readwrite", "visibility": "public" }, "scale": { "value": 【替换:1.75】, "serial": 0, "flags": [], "permissions": "readwrite", "visibility": "public" } } }
注意:refresh 单位是 mHz(60Hz = 60000);width/height/refresh = 0 表示"从未保存过模式"。 文件属主 dde:dde,权限 a+rX。appid/resource 名以本机 /usr/share/dsg/configs/org.deepin.dde.treeland/ 下的 schema 为准。
refresh
dde:dde
a+rX
cat /sys/module/drm/parameters/edid_firmware # 非 (null) = 内核真加载了 dmesg | grep -i "failed to load edid" # 空 = 加载成功
sudo grep -o '@HDMI-A-[0-9]*|[0-9a-f]\{32\}' /var/lib/dde-daemon/display/config.json | sort -u
/var/lib/ddm/.config/dsg/configs/overrides/...
事后用 wlr-randr 看到的永远是修好的值,必须在开机时采样。装一个 systemd oneshot 探针(After=treeland.service,每秒记录一次),重启后回看 60 秒曲线:
wlr-randr
After=treeland.service
cat /sys/class/graphics/fb0/virtual_size # 每秒都应稳定在 3840,2160,无跳变
合格的判据:fb0 从第 1 秒起就是目标分辨率且全程无跳变。
failed to load edid (-2)
dde-dconfig -u 990 get ...
HDMI-A-1@crtc-N=-
deepin-grub2
宽x高
cmp 强制EDID /sys/.../edid
dmesg
failed to load edid
ls /boot/initrd.img-* | head -1
[ -s /sys/.../edid ]
[ -e ]
cat
debugfs
fb0
busctl --system list-names
NameHasOwner
busctl list
dde-dconfig set
org.deepin.dde.Appearance1.SetScaleFactor
.config/dsg/configs/overrides/
e
drm.edid_firmware=
Ctrl+X
config.json
老 4K 显示器(HDMI VSDB Max TMDS 只报 280MHz)的链路处在临界点,冷启动极易握手失败。即使做完上面全部修复,也建议:
HDMI-A-1
ls /sys/class/drm/
my-monitor.bin
cat /sys/class/drm/card0-*/modes
systemctl cat treeland.service
User=
getent passwd <用户名>
以上是我在 Deepin 25 上从误判到确诊、跨 GRUB/内核/合成器/布局四层排查的完整记录。如果你的现象是"登录界面巨大"或"桌面只占左上角",建议直接从 3.4 的幻影屏检查入手——那是最快能定性的一条命令。
我在25.1.1时遇到过,明明是1280x800的屏,显示的应该是1920x1200的分辨率,还无法调整。屏幕显示的东西非常小,但截图时会变成只有左上角显示
Featured Collection
Popular Ranking
Popular Events
Deepin 25.3.0+treeland 开机/重启后分辨率变小、画面只占左上角 —— 完整诊断与修复报告
一、问题表现
机器上有两块屏:内屏(笔记本屏,1920x1080)+ 外接 4K 显示器(3840x2160@60,仅使用外接屏)。
故障时 phenomena 分两种,且经常交替出现:
共同点:每次冷开机/重启都出现,手动在控制中心改一次分辨率就恢复,下次开机又复发。拔插 HDMI 线也能恢复。
二、运行环境
/usr只读)cat /etc/os-release、deepin-immutable-ctl status查看systemctl status treeland.servicelspci -k | grep -A3 VGAuname -r的输出三、判断方法(先分阶段,别瞎猜)
这类问题横跨四层:GRUB → 内核(EDID/DRM) → 合成器(treeland) → 显示布局(dde-daemon)。
每一层都有独立判据。我这次排查最大的教训就是:探针取值方式错了,连续三轮把病灶误判在内核层,而真实问题一半在布局、一半在 greeter 配置。
3.0 先确认是"哪种小"
3.1 排除 GRUB 这条路(很多人第一步就走错)
改
GRUB_GFXMODE在 Deepin 25 上走不通,三条证据:同时确认内核侧其实是好的(fb0 已经是 4K,说明 GRUB 只影响菜单那 1 秒):
3.2 内核层:确认 EDID 竞态(真正根因之一)
dde-daemon 的显示器 UUID =
@HDMI-A-1||v1,其中 hash = md5(EDID 的前 128 字节)。完整故障链(这条链解释了所有现象):
3.3 合成器层:treeland(greeter)的输出配置
Deepin 25 的登录界面由系统级
treeland.service渲染(运行身份User=dde,uid 990,HOME=/var/lib/ddm)。关键坑:全机只有一个真合成器,登录后的 dde-shell/treeland-sd 只是客户端。它的日志默认StandardOutput=null,journal 里一条都没有——先打开日志:3.4 布局层:检查"幻影屏"(桌面只占左上角的直接原因)
一条命令定性:
典型错误配置(本机实测):
物理只开 1 块屏、配置里开着 2 块 → 按"主屏尺寸"布点的 dde-shell/桌面/任务栏只落在 (0,0) 那块 1920x1080 的方块里。**DisplayMode 取值:1=镜像 2=扩展 3=仅一个屏幕。**
另外注意:
Display1是按需激活的 D-Bus 服务,会话早期(登录后 3~23 秒)一定不在位,会话组件不会因它后来上线而自动重排——这就是"每次开机都要手动改一次"的机制。让保存的布局等于物理现实是唯一稳定解法。四、修复方法(四步,每步可独立回滚)
第 1 步:强制 EDID(固定指纹,治"UUID 漂移")
原理:把一份好的 EDID 固定下来,内核每次启动都用它 → 前 128 字节恒定 → UUID 恒定 → 永远命中同一份配置。
前提检查:
先抓一份好 EDID(在显示正常时抓):
三件套:
/etc/firmware-edid/【替换:my-monitor.bin】(放
/etc而不是/lib/firmware:ostree 的/usr只读,/etc可写且升级保留)/etc/initramfs-tools/hooks/edid-hdmi,把文件拷进${DESTDIR}/lib/firmware/edid//etc/default/grub的GRUB_CMDLINE_LINUX_DEFAULT追加:然后
sudo update-grub && sudo update-initramfs -u -k all,重启。第 2 步:裁掉 EDID 里的垃圾低分辨率模式(治"合成器落到 720x400")
EDID 里除了 4K,还声明了一堆 720x400/640x480/1024x768 等小模式(established/standard timings + CTA 扩展里的 VIC)。模式列表按 4K 在前、垃圾在尾部排列;treeland 在没有合法输出配置时会落到列表末尾 → 720x400 → 4K 屏放大 5 倍 = 登录界面巨大。
裁剪思路(对标准 EDID 通用,Python 改字节即可):
裁完必须核验:① 两段校验和都对;② DTD 时序区一字未改(用
cmp -l看差异位置);③ 新 EDID 的 md5 与旧不同。第 3 步:重写显示布局(治"幻影屏",画面只占左上角)
用
Display1.GetConfig→ 只改目标字段 →SetConfig原样发回(round-trip 是关键,手搓 JSON 极易被 schema 拒掉)。要改四处:DisplayMode = 3(仅一个屏幕)Enabled=true, X=0, Y=0, Primary=trueOnlyOneUuid指向裁剪后新 EDID 的 UUID(有多种 EDID 指纹时逐个屏组处理)Enabled=false、外接屏挪回 (0,0)(双保险)改完验证:
物理已启用屏数 == 配置里启用屏数。第 4 步:给 greeter 写输出配置(治"登录界面巨大")
DConfig override 的位置和格式两个都极易写错:
$HOME/.local/share/dsg/configs/...(这是 dconfig-daemon 的缓存,应用不读)$HOME/.config/dsg/configs/overrides//.jsonmagic: "dsg.config.override"+ contents 下每键带元数据(见下)greeter 的 home 是
/var/lib/ddm,所以目标路径:格式(每个键从
/usr/share/dsg/configs/org.deepin.dde.treeland/的 schema 整份拷贝后只改value):五、验证方法
5.1 逐项体检(重启后跑一遍)
/var/lib/ddm/.config/dsg/configs/overrides/...,键齐全)5.2 开机全程采样(取证"故障只存在开机几十秒内")
事后用
wlr-randr看到的永远是修好的值,必须在开机时采样。装一个 systemd oneshot 探针(After=treeland.service,每秒记录一次),重启后回看 60 秒曲线:合格的判据:fb0 从第 1 秒起就是目标分辨率且全程无跳变。
5.3 验证时的三个"噪音",别误判为故障
failed to load edid (-2)记录来自历史启动;本次启动 fb0 正常即说明成功。dde-dconfig -u 990 get ...读 greeter 配置报 "No Permission" 只是该 CLI 的读取权限限制,treeland 读 override 文件不受影响。HDMI-A-1@crtc-N=-(取不到模式)是 sysfs/debugfs 解析的正常现象,分辨率以 fb0 为准。六、踩坑清单(每条都真实付出过时间)
GRUB_GFXMODE必被覆盖:deepin-grub2服务的正则只接受单个宽x高,写逗号列表被判非法后整条回写。且 UEFI GOP 本身就给不出 4K,这条路不通。cmp 强制EDID /sys/.../edid是无效判据:两者本来就该相等,据此判断"已生效"会误判。唯一有效判据是dmesg里有无failed to load edid。/usr;必须用firmware_class.path=。uname -r那份:ls /boot/initrd.img-* | head -1按字母序会校验错内核,得出假通过。[ -s /sys/.../edid ]永远为假:sysfs 文件 stat 大小恒为 0。只能[ -e ]+cat后数长度。debugfs里直接抓第一个 mode 拿到的常是物理关闭的内屏 CRTC(720x400 文本模式),会把整个排查方向带偏。fb0是滞后指标:尺寸在 fbdev 创建时定死,不跟随后续模式变化;它和 debugfs 矛盾时先怀疑自己的取值方式。busctl --system list-names不是有效子命令:静默无输出,会误判"服务不存在"。用NameHasOwner或busctl list。dde-dconfig set只写存储不通知运行中的桌面:改缩放要走会话 DBus(org.deepin.dde.Appearance1.SetScaleFactor)。.config/dsg/configs/overrides/下且带 magic+元数据。七、回滚与兜底
e,删掉含drm.edid_firmware=的那段,Ctrl+X启动。内核找不到固件只会打 warning 后回退真实 EDID,不会变砖。/etc/default/grub里加的两个参数 + 删 greeter override 文件 + 恢复config.json备份 +sudo update-grub && sudo update-initramfs -u -k all。八、物理层建议(不改配置的那部分)
老 4K 显示器(HDMI VSDB Max TMDS 只报 280MHz)的链路处在临界点,冷启动极易握手失败。即使做完上面全部修复,也建议:
附:需要替换的本机信息汇总
HDMI-A-1(连接器名)ls /sys/class/drm/里 status=connected 的那个my-monitor.bin(EDID 文件名)cat /sys/class/drm/card0-*/modes里你的首选systemctl cat treeland.service看User=,getent passwd <用户名>以上是我在 Deepin 25 上从误判到确诊、跨 GRUB/内核/合成器/布局四层排查的完整记录。如果你的现象是"登录界面巨大"或"桌面只占左上角",建议直接从 3.4 的幻影屏检查入手——那是最快能定性的一条命令。