1 { config, lib, pkgs, ... }:
3 cfg = config.hardware.display;
6 meta.doc = ./display.md;
7 meta.maintainers = with lib.maintainers; [
12 hardware.display.edid.enable = lib.mkOption {
13 type = with lib.types; bool;
14 default = cfg.edid.packages != null;
15 defaultText = lib.literalExpression "config.hardware.display.edid.packages != null";
17 Enables handling of EDID files
21 hardware.display.edid.packages = lib.mkOption {
22 type = with lib.types; listOf package;
25 List of packages containing EDID binary files at `$out/lib/firmware/edid`.
26 Such files will be available for use in `drm.edid_firmware` kernel
27 parameter as `edid/<filename>`.
29 You can craft one directly here or use sibling options `linuxhw` and `modelines`.
31 example = lib.literalExpression ''
33 (pkgs.runCommand "edid-custom" {} '''
34 mkdir -p "$out/lib/firmware/edid"
35 base64 -d > "$out/lib/firmware/edid/custom1.bin" <<'EOF'
36 <insert your base64 encoded EDID file here `base64 < /sys/class/drm/card0-.../edid`>
42 if list == [ ] then null else
44 name = "firmware-edid";
46 pathsToLink = [ "/lib/firmware/edid" ];
47 ignoreCollisions = true;
49 compressFirmware = false;
53 hardware.display.edid.linuxhw = lib.mkOption {
54 type = with lib.types; attrsOf (listOf str);
57 Exposes EDID files from users-sourced database at https://github.com/linuxhw/EDID
59 Attribute names will be mapped to EDID filenames `<NAME>.bin`.
61 Attribute values are lists of `awk` regexp patterns that (together) must match
62 exactly one line in either of:
63 - [AnalogDisplay.md](https://raw.githubusercontent.com/linuxhw/EDID/master/AnalogDisplay.md)
64 - [DigitalDisplay.md](https://raw.githubusercontent.com/linuxhw/EDID/master/DigitalDisplay.md)
66 There is no universal way of locating your device config, but here are some practical tips:
67 1. locate your device:
68 - find your model number (second column)
69 - locate manufacturer (first column) and go through the list manually
70 2. narrow down results using other columns until there is only one left:
72 - production date (`Made` column)
74 - screen diagonal (`Inch` column)
75 - as a last resort use `ID` from the last column
77 example = lib.literalExpression ''
79 PG278Q_2014 = [ "PG278Q" "2014" ];
83 if displays == { } then null else
84 pkgs.linuxhw-edid-fetcher.override { inherit displays; };
87 hardware.display.edid.modelines = lib.mkOption {
88 type = with lib.types; attrsOf str;
91 Attribute set of XFree86 Modelines automatically converted
92 and exposed as `edid/<name>.bin` files in initrd.
93 See for more information:
94 - https://en.wikipedia.org/wiki/XFree86_Modeline
96 example = lib.literalExpression ''
98 "PG278Q_60" = " 241.50 2560 2608 2640 2720 1440 1443 1448 1481 -hsync +vsync";
99 "PG278Q_120" = " 497.75 2560 2608 2640 2720 1440 1443 1448 1525 +hsync -vsync";
100 "U2711_60" = " 241.50 2560 2600 2632 2720 1440 1443 1448 1481 -hsync +vsync";
104 if modelines == { } then null else
105 pkgs.edid-generator.overrideAttrs {
107 passthru.config = modelines;
108 modelines = lib.trivial.pipe modelines [
109 (lib.mapAttrsToList (name: value:
110 lib.throwIfNot (builtins.stringLength name <= 12) "Modeline name must be 12 characters or less"
111 ''Modeline "${name}" ${value}''
113 (builtins.map (line: "${line}\n"))
114 (lib.strings.concatStringsSep "")
119 hardware.display.outputs = lib.mkOption {
120 type = lib.types.attrsOf (lib.types.submodule ({
122 edid = lib.mkOption {
123 type = with lib.types; nullOr str;
126 An EDID filename to be used for configured display, as in `edid/<filename>`.
127 See for more information:
128 - `hardware.display.edid.packages`
129 - https://wiki.archlinux.org/title/Kernel_mode_setting#Forcing_modes_and_EDID
132 mode = lib.mkOption {
133 type = with lib.types; nullOr str;
136 A `video` kernel parameter (framebuffer mode) configuration for the specific output:
138 <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
140 See for more information:
141 - https://docs.kernel.org/fb/modedb.html
142 - https://wiki.archlinux.org/title/Kernel_mode_setting#Forcing_modes
144 example = lib.literalExpression ''
151 Hardware/kernel-level configuration of specific outputs.
155 example = lib.literalExpression ''
157 edid.modelines."PG278Q_60" = "241.50 2560 2608 2640 2720 1440 1443 1448 1481 -hsync +vsync";
158 outputs."DP-1".edid = "PG278Q_60.bin";
159 outputs."DP-1".mode = "e";
165 config = lib.mkMerge [
167 hardware.display.edid.packages =
168 lib.optional (cfg.edid.modelines != null) cfg.edid.modelines
169 ++ lib.optional (cfg.edid.linuxhw != null) cfg.edid.linuxhw;
172 # forcing video modes
173 lib.trivial.pipe cfg.outputs [
174 (lib.attrsets.filterAttrs (_: spec: spec.mode != null))
175 (lib.mapAttrsToList (output: spec: "video=${output}:${spec.mode}"))
178 # selecting EDID for displays
179 lib.trivial.pipe cfg.outputs [
180 (lib.attrsets.filterAttrs (_: spec: spec.edid != null))
181 (lib.mapAttrsToList (output: spec: "${output}:edid/${spec.edid}"))
182 (builtins.concatStringsSep ",")
183 (p: lib.optional (p != "") "drm.edid_firmware=${p}")
187 (lib.mkIf (cfg.edid.packages != null) {
188 # services.udev implements hardware.firmware option
189 services.udev.enable = true;
190 hardware.firmware = [ cfg.edid.packages ];