内核模式设置
内核模式设置 (KMS) 是一种在内核空间而非用户空间设置显示分辨率和深度的方法。
Linux 内核对 KMS 的实现可在帧缓冲(framebuffer)中启用原生分辨率,并允许瞬间切换控制台 (tty)。KMS 还启用了较新的技术(如 DRI2),这有助于减少伪影并提高 3D 性能,甚至实现内核空间的省电功能。
背景
此前,配置显卡是 X 服务器的工作。因此,在虚拟控制台中很难实现精美的图形界面。此外,每次从 X 切换到虚拟控制台(Ctrl+Alt+F2)时,服务器必须将显卡的控制权交给内核,这一过程缓慢且会导致闪烁。当控制权交回 X 服务器(当 X 运行在 VT7 时按下 Alt+F7)时,同样会发生这种“痛苦”的过程。
通过内核模式设置 (KMS),内核现在能够设置显卡的模式。这使得引导过程中的精美图形、虚拟控制台与 X 之间的快速切换等成为可能。
配置
首先,请注意无论您使用任何方法,都应始终禁用以下内容:
- 您引导加载程序中的任何
vga=选项,因为这些选项会与 KMS 启用的原生分辨率产生冲突。 - 任何启用与驱动程序冲突的帧缓冲的
video=行。
延迟 KMS 启动
Intel, Nouveau, ATI 和 AMDGPU 驱动程序已经为所有芯片组自动启用了 KMS,因此您无需进行任何操作。
专有的 NVIDIA 驱动程序支持 KMS(自 364.12 版本起),但必须手动启用。
早期 KMS 启动
KMS 通常在 initramfs 阶段之后初始化。但是,可以在 initramfs 阶段启用 KMS。将视频驱动程序所需的模块添加到 initramfs 配置文件中:
- 对于 AMDGPU 使用
amdgpu,或者在使用旧版 ATI 驱动程序时使用radeon。 - 对于 Intel 显卡使用
i915。 - 对于开源 Nouveau 驱动程序使用
nouveau。 - 对于外部 NVIDIA 驱动程序使用
nvidia nvidia_modeset nvidia_uvm nvidia_drm。详见 NVIDIA#DRM kernel mode setting。 - 对于 Matrox 显卡使用
mgag200。 - 取决于所使用的 QEMU 图形(qemu 选项
-vga type或 libvirt<video><model type='type'>[2]):std(qemu) 和vga/bochs(libvirt) 使用bochs,virtio使用virtio-gpu,qxl使用qxl,vmware(qemu) 和vmvga(libvirt) 使用vmwgfx,cirrus使用cirrus。
- 取决于 VirtualBox 图形控制器:
- VMSVGA 使用
vmwgfx, - VBoxVGA 或 VBoxSVGA 使用
vboxvideo。
- VMSVGA 使用
Initramfs 的配置指令根据您使用的 initramfs 生成器而略有不同。
mkinitcpio
对于内置模块,可以使用 /etc/mkinitcpio.conf 中 HOOKS 数组里的 kms(自 mkinitcpio v33 起的默认设置),或将模块名称放入 MODULES 数组中。
对于外部 NVIDIA 模块,您必须按如下方式将模块名称放入 MODULES 数组中:
/etc/mkinitcpio.conf
MODULES=(... nvidia nvidia_modeset nvidia_uvm nvidia_drm ...)
intel_agp 在从休眠恢复时可能会导致问题(显示器无信号)。详见 [3]。如果您使用的是 #强制模式和 EDID 方法,您还应该将自定义文件嵌入到 initramfs 中。
/etc/mkinitcpio.conf
FILES=(/usr/lib/firmware/edid/your_edid.bin)
Booster
如果您使用 Booster,可以通过更改此配置来加载所需模块:
/etc/booster.yaml
modules_force_load: i915
如果您使用的是 #强制模式和 EDID 方法,您还应该将自定义文件嵌入到您的 booster 镜像中。
/etc/booster.yaml
extra_files: /usr/lib/firmware/edid/your_edid.bin
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 "
故障排除
我的字体太小了
关于如何将控制台字体更改为大字体,请参阅 Linux console#Fonts。Terminus 字体 (terminus-font) 有多种尺寸可用,例如较大的 ter-132b。
或者,禁用模式设置可能会切换到较低的分辨率,使字体看起来更大。另一个选择是 KMSCON。
强制模式和 EDID
如果您的原生分辨率没有自动配置,或者根本没有检测到显示设备,那么您的显示器可能没有发送 EDID 文件,或者发送的是错误的文件。内核会尝试捕获这种情况并设置一个最典型的分辨率。
如果您拥有显示器的 EDID 文件,只需显式强制执行它(见下文)。然而,通常人们无法直接获得正确的文件,因此有必要提取现有文件并进行修复,或者生成一个新文件。
通过遵循上游文档(另请参阅此处的简短指南),可以在内核编译期间为各种分辨率和配置生成新的 EDID 二进制文件。其他解决方案在本文中详细阐述:Creating and using edid.bin via xorg.conf。在大多数情况下,提取现有文件更容易,例如,如果您的显示器在 Windows 下运行良好,您可能会在相应的驱动程序中成功提取 EDID;或者如果有相同设置的类似显示器可以工作,您可以使用 read-edid 包中的 get-edid(1)。您也可以尝试查看 /sys/class/drm/*/edid。
准备好 EDID 后,将其放在一个目录下,例如 /usr/lib/firmware/ 下名为 edid 的目录,并将二进制文件复制进去。
要在启动时加载它,请在内核命令行中指定以下内容:
drm.edid_firmware=edid/your_edid.bin
为了仅将其应用于特定的连接器,请使用:
drm.edid_firmware=VGA-1:edid/your_edid.bin
如果您想设置多个 edid 文件,请使用:
drm.edid_firmware=VGA-1:edid/your_edid.bin,VGA-2:edid/your_other_edid.bin
如果您在使用早期 KMS,您必须将自定义 EDID 文件包含在 initramfs 中,否则会出现问题。
drm.edid_firmware 参数的值也可以在启动后通过写入 /sys/module/drm/parameters/edid_firmware 来更改。
# echo edid/your_edid.bin > /sys/module/drm/parameters/edid_firmware
这仅对新插入的显示器生效,已经插入的屏幕将继续使用其现有的 EDID 设置。但对于外部显示器,重新拔插即可看到效果。
如果在内核不在锁定模式 (lockdown mode)下,您可以使用 debugfs 而非内核命令行参数在启动后加载 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_override 的)新 EDID,这样您就无需物理拔插显示器或重启。
# echo 1 > /sys/kernel/debug/dri/0/HDMI-A-2/trigger_hotplug
强制模式
video= 命令行指定分辨率对于所有 Wayland 合成器 都是有用的。摘自nouveau wiki:
- 可以在内核命令行中强制执行模式。不幸的是,video 命令行选项在 DRM 方面文档匮乏。关于如何使用它的片段可以在以下地址找到:
格式为:
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 时为数字显示启用缩减消隐(reduced blanking)计算。这会减少水平和垂直消隐间隔以节省带宽。[-<bpp>]: 指定颜色深度或每像素位数(例如,-24 表示 24 位色)。[@<refresh>]: 指定刷新率(Hz)。[i]: 启用隔行扫描模式。[m]: 在 CVT 计算中添加边距(xres 的 1.8% 向下取整至 8 像素,以及 yres 的 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(整数): 边距的像素数,通常用于处理电视上的过扫描 (overscan)。reflect_x(布尔值): 在 X 轴上进行轴对称。reflect_y(布尔值): 在 Y 轴上进行轴对称。rotate(整数): 将初始帧缓冲旋转 x 度。有效值为 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 连接器上设置“面板方向”属性,作为 kms 用户的提示。
要获取连接器的名称和当前状态,可以使用以下 shell 单行命令:
$ 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")。