nixos/hardware.display: init
This commit is contained in:
parent
8559b460b1
commit
676a51c41f
@ -13,6 +13,9 @@
|
|||||||
- `authelia` has been upgraded to version 4.38. This version brings several features and improvements which are detailed in the [release blog post](https://www.authelia.com/blog/4.38-release-notes/).
|
- `authelia` has been upgraded to version 4.38. This version brings several features and improvements which are detailed in the [release blog post](https://www.authelia.com/blog/4.38-release-notes/).
|
||||||
This release also deprecates some configuration keys, which are likely to be removed in future version 5.0, but they are still supported and expected to be working in the current version.
|
This release also deprecates some configuration keys, which are likely to be removed in future version 5.0, but they are still supported and expected to be working in the current version.
|
||||||
|
|
||||||
|
- `hardware.display` is a new module implementing workarounds for misbehaving monitors
|
||||||
|
through setting up custom EDID files and forcing kernel/framebuffer modes.
|
||||||
|
|
||||||
## New Services {#sec-release-24.11-new-services}
|
## New Services {#sec-release-24.11-new-services}
|
||||||
|
|
||||||
- [Open-WebUI](https://github.com/open-webui/open-webui), a user-friendly WebUI
|
- [Open-WebUI](https://github.com/open-webui/open-webui), a user-friendly WebUI
|
||||||
|
@ -567,6 +567,7 @@
|
|||||||
./services/hardware/bolt.nix
|
./services/hardware/bolt.nix
|
||||||
./services/hardware/brltty.nix
|
./services/hardware/brltty.nix
|
||||||
./services/hardware/ddccontrol.nix
|
./services/hardware/ddccontrol.nix
|
||||||
|
./services/hardware/display.nix
|
||||||
./services/hardware/fancontrol.nix
|
./services/hardware/fancontrol.nix
|
||||||
./services/hardware/freefall.nix
|
./services/hardware/freefall.nix
|
||||||
./services/hardware/fwupd.nix
|
./services/hardware/fwupd.nix
|
||||||
|
130
nixos/modules/services/hardware/display.md
Normal file
130
nixos/modules/services/hardware/display.md
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
# Customizing display configuration {#module-hardware-display}
|
||||||
|
|
||||||
|
This section describes how to customize display configuration using:
|
||||||
|
- kernel modes
|
||||||
|
- EDID files
|
||||||
|
|
||||||
|
Example situations it can help you with:
|
||||||
|
- display controllers (external hardware) not advertising EDID at all,
|
||||||
|
- misbehaving graphics drivers,
|
||||||
|
- loading custom display configuration before the Display Manager is running,
|
||||||
|
|
||||||
|
## Forcing display modes {#module-hardware-display-modes}
|
||||||
|
|
||||||
|
In case of very wrong monitor controller and/or video driver combination you can
|
||||||
|
[force the display to be enabled](https://mjmwired.net/kernel/Documentation/fb/modedb.txt#41)
|
||||||
|
and skip some driver-side checks by adding `video=<OUTPUT>:e` to `boot.kernelParams`.
|
||||||
|
This is exactly the case with [`amdgpu` drivers](https://gitlab.freedesktop.org/drm/amd/-/issues/615#note_1987392)
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
# force enabled output to skip `amdgpu` checks
|
||||||
|
hardware.display.outputs."DP-1".mode = "e";
|
||||||
|
# completely disable output no matter what is connected to it
|
||||||
|
hardware.display.outputs."VGA-2".mode = "d";
|
||||||
|
|
||||||
|
/* equals
|
||||||
|
boot.kernelParams = [ "video=DP-1:e" "video=VGA-2:d" ];
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Crafting custom EDID files {#module-hardware-display-edid-custom}
|
||||||
|
|
||||||
|
To make custom EDID binaries discoverable you should first create a derivation storing them at
|
||||||
|
`$out/lib/firmware/edid/` and secondly add that derivation to `hardware.display.edid.packages` NixOS option:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
hardware.display.edid.packages = [
|
||||||
|
(pkgs.runCommand "edid-custom" {} ''
|
||||||
|
mkdir -p $out/lib/firmware/edid
|
||||||
|
base64 -d > "$out/lib/firmware/edid/custom1.bin" <<'EOF'
|
||||||
|
<insert your base64 encoded EDID file here `base64 < /sys/class/drm/card0-.../edid`>
|
||||||
|
EOF
|
||||||
|
base64 -d > "$out/lib/firmware/edid/custom2.bin" <<'EOF'
|
||||||
|
<insert your base64 encoded EDID file here `base64 < /sys/class/drm/card1-.../edid`>
|
||||||
|
EOF
|
||||||
|
'')
|
||||||
|
];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
There are 2 options significantly easing preparation of EDID files:
|
||||||
|
- `hardware.display.edid.linuxhw`
|
||||||
|
- `hardware.display.edid.modelines`
|
||||||
|
|
||||||
|
## Assigning EDID files to displays {#module-hardware-display-edid-assign}
|
||||||
|
|
||||||
|
To assign available custom EDID binaries to your monitor (video output) use `hardware.display.outputs."<NAME>".edid` option.
|
||||||
|
Under the hood it adds `drm.edid_firmware` entry to `boot.kernelParams` NixOS option for each configured output:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
hardware.display.outputs."VGA-1".edid = "custom1.bin";
|
||||||
|
hardware.display.outputs."VGA-2".edid = "custom2.bin";
|
||||||
|
/* equals:
|
||||||
|
boot.kernelParams = [ "drm.edid_firmware=VGA-1:edid/custom1.bin,VGA-2:edid/custom2.bin" ];
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pulling files from linuxhw/EDID database {#module-hardware-display-edid-linuxhw}
|
||||||
|
|
||||||
|
`hardware.display.edid.linuxhw` utilizes `pkgs.linuxhw-edid-fetcher` to extract EDID files
|
||||||
|
from https://github.com/linuxhw/EDID based on simple string/regexp search identifying exact entries:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
hardware.display.edid.linuxhw."PG278Q_2014" = [ "PG278Q" "2014" ];
|
||||||
|
|
||||||
|
/* equals:
|
||||||
|
hardware.display.edid.packages = [
|
||||||
|
(pkgs.linuxhw-edid-fetcher.override {
|
||||||
|
displays = {
|
||||||
|
"PG278Q_2014" = [ "PG278Q" "2014" ];
|
||||||
|
};
|
||||||
|
})
|
||||||
|
];
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Using XFree86 Modeline definitions {#module-hardware-display-edid-modelines}
|
||||||
|
|
||||||
|
`hardware.display.edid.modelines` utilizes `pkgs.edid-generator` package allowing you to
|
||||||
|
conveniently use [`XFree86 Modeline`](https://en.wikipedia.org/wiki/XFree86_Modeline) entries as EDID binaries:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
hardware.display.edid.modelines."PG278Q_60" = " 241.50 2560 2608 2640 2720 1440 1443 1448 1481 -hsync +vsync";
|
||||||
|
hardware.display.edid.modelines."PG278Q_120" = " 497.75 2560 2608 2640 2720 1440 1443 1448 1525 +hsync -vsync";
|
||||||
|
|
||||||
|
/* equals:
|
||||||
|
hardware.display.edid.packages = [
|
||||||
|
(pkgs.edid-generator.overrideAttrs {
|
||||||
|
clean = true;
|
||||||
|
modelines = ''
|
||||||
|
Modeline "PG278Q_60" 241.50 2560 2608 2640 2720 1440 1443 1448 1481 -hsync +vsync
|
||||||
|
Modeline "PG278Q_120" 497.75 2560 2608 2640 2720 1440 1443 1448 1525 +hsync -vsync
|
||||||
|
'';
|
||||||
|
})
|
||||||
|
];
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Complete example for Asus PG278Q {#module-hardware-display-pg278q}
|
||||||
|
|
||||||
|
And finally this is a complete working example for a 2014 (first) batch of [Asus PG278Q monitor with `amdgpu` drivers](https://gitlab.freedesktop.org/drm/amd/-/issues/615#note_1987392):
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
hardware.display.edid.modelines."PG278Q_60" = " 241.50 2560 2608 2640 2720 1440 1443 1448 1481 -hsync +vsync";
|
||||||
|
hardware.display.edid.modelines."PG278Q_120" = " 497.75 2560 2608 2640 2720 1440 1443 1448 1525 +hsync -vsync";
|
||||||
|
|
||||||
|
hardware.display.outputs."DP-1".edid = "PG278Q_60.bin";
|
||||||
|
hardware.display.outputs."DP-1".mode = "e";
|
||||||
|
}
|
||||||
|
```
|
193
nixos/modules/services/hardware/display.nix
Normal file
193
nixos/modules/services/hardware/display.nix
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.hardware.display;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
meta.doc = ./display.md;
|
||||||
|
meta.maintainers = with lib.maintainers; [
|
||||||
|
nazarewk
|
||||||
|
];
|
||||||
|
|
||||||
|
options = {
|
||||||
|
hardware.display.edid.enable = lib.mkOption {
|
||||||
|
type = with lib.types; bool;
|
||||||
|
default = cfg.edid.packages != null;
|
||||||
|
defaultText = lib.literalExpression "config.hardware.display.edid.packages != null";
|
||||||
|
description = ''
|
||||||
|
Enables handling of EDID files
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
hardware.display.edid.packages = lib.mkOption {
|
||||||
|
type = with lib.types; listOf package;
|
||||||
|
default = [ ];
|
||||||
|
description = ''
|
||||||
|
List of packages containing EDID binary files at `$out/lib/firmware/edid`.
|
||||||
|
Such files will be available for use in `drm.edid_firmware` kernel
|
||||||
|
parameter as `edid/<filename>`.
|
||||||
|
|
||||||
|
You can craft one directly here or use sibling options `linuxhw` and `modelines`.
|
||||||
|
'';
|
||||||
|
example = lib.literalExpression ''
|
||||||
|
[
|
||||||
|
(pkgs.runCommand "edid-custom" {} '''
|
||||||
|
mkdir -p "$out/lib/firmware/edid"
|
||||||
|
base64 -d > "$out/lib/firmware/edid/custom1.bin" <<'EOF'
|
||||||
|
<insert your base64 encoded EDID file here `base64 < /sys/class/drm/card0-.../edid`>
|
||||||
|
EOF
|
||||||
|
''')
|
||||||
|
]
|
||||||
|
'';
|
||||||
|
apply = list:
|
||||||
|
if list == [ ] then null else
|
||||||
|
(pkgs.buildEnv {
|
||||||
|
name = "firmware-edid";
|
||||||
|
paths = list;
|
||||||
|
pathsToLink = [ "/lib/firmware/edid" ];
|
||||||
|
ignoreCollisions = true;
|
||||||
|
}) // {
|
||||||
|
compressFirmware = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
hardware.display.edid.linuxhw = lib.mkOption {
|
||||||
|
type = with lib.types; attrsOf (listOf str);
|
||||||
|
default = { };
|
||||||
|
description = ''
|
||||||
|
Exposes EDID files from users-sourced database at https://github.com/linuxhw/EDID
|
||||||
|
|
||||||
|
Attribute names will be mapped to EDID filenames `<NAME>.bin`.
|
||||||
|
|
||||||
|
Attribute values are lists of `awk` regexp patterns that (together) must match
|
||||||
|
exactly one line in either of:
|
||||||
|
- [AnalogDisplay.md](https://raw.githubusercontent.com/linuxhw/EDID/master/AnalogDisplay.md)
|
||||||
|
- [DigitalDisplay.md](https://raw.githubusercontent.com/linuxhw/EDID/master/DigitalDisplay.md)
|
||||||
|
|
||||||
|
There is no universal way of locating your device config, but here are some practical tips:
|
||||||
|
1. locate your device:
|
||||||
|
- find your model number (second column)
|
||||||
|
- locate manufacturer (first column) and go through the list manually
|
||||||
|
2. narrow down results using other columns until there is only one left:
|
||||||
|
- `Name` column
|
||||||
|
- production date (`Made` column)
|
||||||
|
- resolution `Res`
|
||||||
|
- screen diagonal (`Inch` column)
|
||||||
|
- as a last resort use `ID` from the last column
|
||||||
|
'';
|
||||||
|
example = lib.literalExpression ''
|
||||||
|
{
|
||||||
|
PG278Q_2014 = [ "PG278Q" "2014" ];
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
apply = displays:
|
||||||
|
if displays == { } then null else
|
||||||
|
pkgs.linuxhw-edid-fetcher.override { inherit displays; };
|
||||||
|
};
|
||||||
|
|
||||||
|
hardware.display.edid.modelines = lib.mkOption {
|
||||||
|
type = with lib.types; attrsOf str;
|
||||||
|
default = { };
|
||||||
|
description = ''
|
||||||
|
Attribute set of XFree86 Modelines automatically converted
|
||||||
|
and exposed as `edid/<name>.bin` files in initrd.
|
||||||
|
See for more information:
|
||||||
|
- https://en.wikipedia.org/wiki/XFree86_Modeline
|
||||||
|
'';
|
||||||
|
example = lib.literalExpression ''
|
||||||
|
{
|
||||||
|
"PG278Q_60" = " 241.50 2560 2608 2640 2720 1440 1443 1448 1481 -hsync +vsync";
|
||||||
|
"PG278Q_120" = " 497.75 2560 2608 2640 2720 1440 1443 1448 1525 +hsync -vsync";
|
||||||
|
"U2711_60" = " 241.50 2560 2600 2632 2720 1440 1443 1448 1481 -hsync +vsync";
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
apply = modelines:
|
||||||
|
if modelines == { } then null else
|
||||||
|
pkgs.edid-generator.overrideAttrs {
|
||||||
|
clean = true;
|
||||||
|
passthru.config = modelines;
|
||||||
|
modelines = lib.trivial.pipe modelines [
|
||||||
|
(lib.mapAttrsToList (name: value:
|
||||||
|
lib.throwIfNot (builtins.stringLength name <= 12) "Modeline name must be 12 characters or less"
|
||||||
|
''Modeline "${name}" ${value}''
|
||||||
|
))
|
||||||
|
(builtins.map (line: "${line}\n"))
|
||||||
|
(lib.strings.concatStringsSep "")
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
hardware.display.outputs = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf (lib.types.submodule ({
|
||||||
|
options = {
|
||||||
|
edid = lib.mkOption {
|
||||||
|
type = with lib.types; nullOr str;
|
||||||
|
default = null;
|
||||||
|
description = ''
|
||||||
|
An EDID filename to be used for configured display, as in `edid/<filename>`.
|
||||||
|
See for more information:
|
||||||
|
- `hardware.display.edid.packages`
|
||||||
|
- https://wiki.archlinux.org/title/Kernel_mode_setting#Forcing_modes_and_EDID
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
mode = lib.mkOption {
|
||||||
|
type = with lib.types; nullOr str;
|
||||||
|
default = null;
|
||||||
|
description = ''
|
||||||
|
A `video` kernel parameter (framebuffer mode) configuration for the specific output:
|
||||||
|
|
||||||
|
<xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
|
||||||
|
|
||||||
|
See for more information:
|
||||||
|
- https://docs.kernel.org/fb/modedb.html
|
||||||
|
- https://wiki.archlinux.org/title/Kernel_mode_setting#Forcing_modes
|
||||||
|
'';
|
||||||
|
example = lib.literalExpression ''
|
||||||
|
"e"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
description = ''
|
||||||
|
Hardware/kernel-level configuration of specific outputs.
|
||||||
|
'';
|
||||||
|
default = { };
|
||||||
|
|
||||||
|
example = lib.literalExpression ''
|
||||||
|
{
|
||||||
|
edid.modelines."PG278Q_60" = "241.50 2560 2608 2640 2720 1440 1443 1448 1481 -hsync +vsync";
|
||||||
|
outputs."DP-1".edid = "PG278Q_60.bin";
|
||||||
|
outputs."DP-1".mode = "e";
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkMerge [
|
||||||
|
{
|
||||||
|
hardware.display.edid.packages =
|
||||||
|
lib.optional (cfg.edid.modelines != null) cfg.edid.modelines
|
||||||
|
++ lib.optional (cfg.edid.linuxhw != null) cfg.edid.linuxhw;
|
||||||
|
|
||||||
|
boot.kernelParams =
|
||||||
|
# forcing video modes
|
||||||
|
lib.trivial.pipe cfg.outputs [
|
||||||
|
(lib.attrsets.filterAttrs (_: spec: spec.mode != null))
|
||||||
|
(lib.mapAttrsToList (output: spec: "video=${output}:${spec.mode}"))
|
||||||
|
]
|
||||||
|
++
|
||||||
|
# selecting EDID for displays
|
||||||
|
lib.trivial.pipe cfg.outputs [
|
||||||
|
(lib.attrsets.filterAttrs (_: spec: spec.edid != null))
|
||||||
|
(lib.mapAttrsToList (output: spec: "${output}:edid/${spec.edid}"))
|
||||||
|
(builtins.concatStringsSep ",")
|
||||||
|
(p: lib.optional (p != "") "drm.edid_firmware=${p}")
|
||||||
|
]
|
||||||
|
;
|
||||||
|
}
|
||||||
|
(lib.mkIf (cfg.edid.packages != null) {
|
||||||
|
# services.udev implements hardware.firmware option
|
||||||
|
services.udev.enable = true;
|
||||||
|
hardware.firmware = [ cfg.edid.packages ];
|
||||||
|
})
|
||||||
|
];
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user