grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / services / hardware / display.nix
blob3b3118f132e92a33b136a332c15a75949e088400
1 { config, lib, pkgs, ... }:
2 let
3   cfg = config.hardware.display;
4 in
6   meta.doc = ./display.md;
7   meta.maintainers = with lib.maintainers; [
8     nazarewk
9   ];
11   options = {
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";
16       description = ''
17         Enables handling of EDID files
18       '';
19     };
21     hardware.display.edid.packages = lib.mkOption {
22       type = with lib.types; listOf package;
23       default = [ ];
24       description = ''
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`.
30       '';
31       example = lib.literalExpression ''
32         [
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`>
37             EOF
38           ''')
39         ]
40       '';
41       apply = list:
42         if list == [ ] then null else
43         (pkgs.buildEnv {
44           name = "firmware-edid";
45           paths = list;
46           pathsToLink = [ "/lib/firmware/edid" ];
47           ignoreCollisions = true;
48         }) // {
49           compressFirmware = false;
50         };
51     };
53     hardware.display.edid.linuxhw = lib.mkOption {
54       type = with lib.types; attrsOf (listOf str);
55       default = { };
56       description = ''
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:
71           - `Name` column
72           - production date (`Made` column)
73           - resolution `Res`
74           - screen diagonal (`Inch` column)
75           - as a last resort use `ID` from the last column
76       '';
77       example = lib.literalExpression ''
78         {
79           PG278Q_2014 = [ "PG278Q" "2014" ];
80         }
81       '';
82       apply = displays:
83         if displays == { } then null else
84         pkgs.linuxhw-edid-fetcher.override { inherit displays; };
85     };
87     hardware.display.edid.modelines = lib.mkOption {
88       type = with lib.types; attrsOf str;
89       default = { };
90       description = ''
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
95       '';
96       example = lib.literalExpression ''
97         {
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";
101         }
102       '';
103       apply = modelines:
104         if modelines == { } then null else
105         pkgs.edid-generator.overrideAttrs {
106           clean = true;
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}''
112             ))
113             (builtins.map (line: "${line}\n"))
114             (lib.strings.concatStringsSep "")
115           ];
116         };
117     };
119     hardware.display.outputs = lib.mkOption {
120       type = lib.types.attrsOf (lib.types.submodule ({
121         options = {
122           edid = lib.mkOption {
123             type = with lib.types; nullOr str;
124             default = null;
125             description = ''
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
130             '';
131           };
132           mode = lib.mkOption {
133             type = with lib.types; nullOr str;
134             default = null;
135             description = ''
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
143             '';
144             example = lib.literalExpression ''
145               "e"
146             '';
147           };
148         };
149       }));
150       description = ''
151         Hardware/kernel-level configuration of specific outputs.
152       '';
153       default = { };
155       example = lib.literalExpression ''
156         {
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";
160         }
161       '';
162     };
163   };
165   config = lib.mkMerge [
166     {
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;
171       boot.kernelParams =
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}"))
176         ]
177         ++
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}")
184         ]
185       ;
186     }
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 ];
191     })
192   ];