内核级显示模式设置
内核级显示模式设置 (KMS) ,作用是可以在内核级别而不是最终用户级别切换显示分辨率和颜色深度。
Linux 内核的 KMS 实现支持在 framebuffer 中使用原生分辨率和即时终端(tty)切换。KMS 使用了更新的技术(例如 DRI2),可以减少失真、增强3D性能,甚至使用内核空间节能功能。
背景
以前,设定显卡是 X 服务器的工作。所以虚拟终端不可能提供漂亮的图像效果。同时,每次使用Ctrl+Alt+F1~7从X切换到虚拟终端时,x服务器必须将显卡的控制权交给内核,这个流程显得低效并且会导致闪烁。将控制权切回到X服务器同样是一个“痛苦”的过程。
使用内核模式设置后,内核可以设定显卡的模式。这样开机启动即可看到漂亮的显示画面,在 X 图形界面 和 终端 之间也可以快速切换,还有其他的一些优点。
配置
不管使用什么方法,都需要先“禁用”下列项:
- 所有引导加载程序中的
vga=选项,这个选项与 KMS 的原生分辨率设置冲突。 - 所有
video=选项,这个选项会启动帧缓冲,导致驱动冲突。
KMS 晚启动
Intel, Nouveau, ATI 和 AMDGPU 驱动已经为所有芯片组提供了 KMS 支持。这些驱动也会自动启用 KMS,无须额外操作。
闭源的 NVIDIA 驱动从 364.12 开始支持 KMS,并且从 560.35.03-5 版本开始默认启用。旧版本需要手动启用。
KMS 早启动
KMS通常是在initramfs 阶段之后开始初始化,但是你也可以在initramfs的阶段启用KMS。将所需的视频驱动模块加入 initramfs 配置文件:
-
AMDGPU 驱动加入
amdgpu; 老的 ATI 驱动加入radeon -
Intel 卡加入
i915 - 开源的 Nouveau 驱动加入
nouveau - 对于树外 NVIDIA 驱动,加入
nvidia nvidia_modeset nvidia_uvm nvidia_drm。见 NVIDIA#DRM 内核级显示模式设置 以获取详细信息。 - Matrox 显卡加入
mgag200。 -
QEMU 显卡使用(qemu 选项
-vga type或 libvirt<video><model type='type'>[2]):-
bochs对应std(qemu) 和vga/bochs(libvirt), -
virtio-gpu对应virtio, -
qxl对应qxl, -
vmwgfx对应vmware(qemu) 和vmvga(libvirt), -
cirrus对应cirrus。
-
-
VirtualBox 显示控制器:
-
vmwgfx对应 VMSVGA, -
vboxvideo对应 VBoxVGA 或 VBoxSVGA。
-
根据使用的 initramfs 生成器,具体的配置方法略有不同。
mkinitcpio
使用树内模块时,可以将 kms 加入到 /etc/mkinitcpio.conf 的 HOOKS 数组中(自 mkinitcpio v33 起为默认设置),或将模块名称加入 MODULES 数组。
对于树外 NVIDIA 模块,必须将模块的名字加入到 MODULES 数组中,如下所示:
/etc/mkinitcpio.conf
MODULES=(... nvidia nvidia_modeset nvidia_uvm nvidia_drm ...)
如果你在使用 #强制设置显示模式与 EDID 的方法,则也需要将选定的文件集成到 initramfs 去:
/etc/mkinitcpio.conf
FILES=(/usr/lib/firmware/edid/your_edid.bin)
完成这一切后,重新生成 initramfs。
Booster
如果您在使用 Booster,可通过以下的方式改变设置来加载需要的模块。
/etc/booster.yaml
modules_force_load: i915
如果您在使用 #强制设置显示模式与 EDID 的方法,则也需要将选定的文件集成到 Booster 镜像中:
/etc/booster.yaml
extra_files: /usr/lib/firmware/edid/your_edid.bin
之后,重新生成 Booster 镜像。
Dracut
Dracut 可通过 --force_drivers 命令行选项,或配置文件中的 force_drivers+="" 设置,在 initramfs 阶段使用 modprobe 提前加载驱动。例如:
/etc/dracut.conf.d/myflags.conf
# ... force_drivers+=" amdgpu " # ...
如果您在使用 #强制设置显示模式与 EDID 的方法,则也应包含自定义文件:
/etc/dracut.conf.d/myflags.conf
install_items+=" /usr/lib/firmware/edid/your_edid.bin "
之后,重新生成 Dracut 镜像。
问题解决
字体太小
Linux 控制台#字体介绍了如何在终端中使用大字体。软件仓库中的 (terminus-font包) 字体提供了很多字号,比如更大一些的 ter-132b。
或者,可以#禁用 KMS 以切换为更低的分辨率,使得字体外观显得更大一些。另一种选择是 KMSCON。
强制设置显示模式与 EDID
如果无法自动配置您本机的分辨率,或者根本无法侦测到显示器;您的显示器很有可能没有或发送了破损的 EDID 文件。内核将尝试捕获这种情况并设置为某一通用分辨率。
如果您拥有贵显示器的 EDID 文件,您只需明确地强制设置即可(详见下文)。但是,您在大多数情况下可能无法直接取得一个健全的 EDID 文件。您可能需要提取现有 EDID 文件中的信息并试图修复它,从而获得了一个新的 EDID 文件。
在内核编译期间,可以通过遵循 上游文档 来生成用于各种分辨率和配置的新EDID二进制文件(另请参见 此处 的简短指南)。 这篇文章 也详细概述了其他解决方案。在大多数情况下,提取现有文件比较容易。例如从Windows下的驱动程序中提取EDID,如果您的显示器在那里运行良好。或者您可以使用来自 read-edid包 包的 get-edid(1) 命令,从具有相同设置的类似显示器中提取。可以从 /sys/class/drm/*/edid 中查找一下。
在您的EDID准备就绪后,请将它放置于特定的文件夹。例如一个在 /usr/lib/firmware/ 下名为 edid 的文件夹,您可以将您的二进制文件放入其中。
请在内核参数中设置下述参数来在启动时加载:
drm.edid_firmware=edid/你的edid.bin
用下述参数来设定特定的连接器:
drm.edid_firmware=VGA-1:edid/你的edid.bin
如果你希望设置多个 edid 文件,用下面的参数:
drm.edid_firmware=VGA-1:edid/你的edid.bin,VGA-2:edid/你的另一个edid.bin
如果您要进行#KMS 早启动,则必须在 initramfs 中包含自定义 EDID 文件,否则可能会遇到问题。
启动后,也可以通过写入 /sys/module/drm/parameters/edid_firmware 修改 drm.edid_firmware 参数:
# echo edid/你的edid.bin > /sys/module/drm/parameters/edid_firmware
此更改只会对新连接的显示器生效;已经连接的显示器会继续使用原来的 EDID 设置。对于外接显示器,重新插拔即可使更改生效。
如果内核不处于锁定模式,可以使用 debugfs 在启动后加载 EDID,而不必通过内核命令行参数。这对于更换连接器上的显示器或测试 EDID 特别有用。准备好上述 EDID 文件后执行:
# cat correct-edid.bin > /sys/kernel/debug/dri/0/HDMI-A-2/edid_override
若要取消覆盖:
# echo -n reset > /sys/kernel/debug/dri/0/HDMI-A-2/edid_override
如果显示器支持热插拔,还可以触发热插拔事件,让显示器使用刚刚加载的新 EDID(例如写入 edid_override 的 EDID),无需实际重新插拔显示器或重新启动:
# echo 1 > /sys/kernel/debug/dri/0/HDMI-A-2/trigger_hotplug
Forcing modes
来自 nouveau wiki:
用内核命令行可以强制设置使用的模式。但是 DRM 内核选项的文档很少,使用的方法位于:
- https://docs.kernel.org/fb/modedb.html
- https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/drm/drm_fb_helper.c
格式:
video=<driver>:<conn>:<xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
-
<driver>:指定启动时的视频模式。 -
<conn>:指定视频连接类型,例如 VGA、DVI、HDMI 等;可通过/sys/class/drm/查看可用连接器。 -
<xres>:水平分辨率(像素)。 -
<yres>:垂直分辨率(像素)。 -
[M]:使用 VESA 协调视频时序(CVT)计算模式时序,而不是从数据库中查找模式。 -
[R]:在使用 CVT 时为数字显示器启用减少消隐时间计算,缩短水平及垂直消隐间隔以节省带宽。 -
[-<bpp>]:颜色深度,即每像素位数(例如-24表示 24 位色)。 -
[@<refresh>]:刷新率(Hz)。 -
[i]:启用隔行扫描模式。 -
[m]:在 CVT 计算中添加边距(水平分辨率的 1.8%,向下取整为 8 的倍数;垂直分辨率的 1.8%)。 -
[e]:强制启用输出。 -
[D]:强制启用数字输出(例如 DVI-I 连接器)。 -
[d]:强制关闭输出。
可以使用多个 video= 设置不同输出的分辨率,例如要强制 DVI 使用 1024x768 85 Hz,同时禁用 TV-out:
video=DVI-I-1:1024x768@85 video=TV-1:d
也可以在模式之后用逗号分隔并传入其他选项:
video=DVI-I-1:720x480,rotate=180
有效选项包括:
-
margin_top、margin_bottom、margin_left、margin_right(整数):边距的像素数,通常用于处理电视的过扫描问题。 -
reflect_x(布尔值):沿 X 轴进行镜像反射。 -
reflect_y(布尔值):沿 Y 轴进行镜像反射。 -
rotate(整数):将初始 framebuffer 旋转指定角度,可用值为 0、90、180、270。 -
tv_mode:模拟电视制式,可用值包括 "NTSC"、"NTSC-443"、"NTSC-J"、"PAL"、"PAL-M"、"PAL-N" 或 "SECAM"。 -
panel_orientation:可用值为 "normal"、"upside_down"、"left_side_up" 或 "right_side_up"。仅适用于 KMS 驱动,用于设置 KMS 连接器的 "panel orientation" 属性,向 KMS 用户提供显示面板方向的提示。
要获取当前连接器的状态,使用下面命令:
$ for p in /sys/class/drm/*/status; do con=${p%/status}; echo -n "${con#*/card?-}: "; cat $p; done
DVI-I-1: connected HDMI-A-1: disconnected VGA-1: disconnected
禁用 KMS
有时需要禁用 KMS。只需在内核参数中加入 nomodeset 即可,设置方法请阅读内核参数页面。
使用 nomodeset 内核参数的同时,Intel 卡需要添加 i915.modeset=0,Nvidia 卡需要添加 nouveau.modeset=0。Nvidia Optimus 双显卡系统,需要添加三个内核参数:
nomodeset i915.modeset=0 nouveau.modeset=0