1# This module provides the proprietary NVIDIA X11 / OpenGL drivers.
2
3{ config, lib, pkgs, ... }:
4
5with lib;
6
7let
8 nvidia_x11 = let
9 drivers = config.services.xserver.videoDrivers;
10 isDeprecated = str: (hasPrefix "nvidia" str) && (str != "nvidia");
11 hasDeprecated = drivers: any isDeprecated drivers;
12 in if (hasDeprecated drivers) then
13 throw ''
14 Selecting an nvidia driver has been modified for NixOS 19.03. The version is now set using `hardware.nvidia.package`.
15 ''
16 else if (elem "nvidia" drivers) then cfg.package else null;
17
18 enabled = nvidia_x11 != null;
19 cfg = config.hardware.nvidia;
20
21 pCfg = cfg.prime;
22 syncCfg = pCfg.sync;
23 offloadCfg = pCfg.offload;
24 primeEnabled = syncCfg.enable || offloadCfg.enable;
25 nvidiaPersistencedEnabled = cfg.nvidiaPersistenced;
26in
27
28{
29 imports =
30 [
31 (mkRenamedOptionModule [ "hardware" "nvidia" "optimus_prime" "enable" ] [ "hardware" "nvidia" "prime" "sync" "enable" ])
32 (mkRenamedOptionModule [ "hardware" "nvidia" "optimus_prime" "allowExternalGpu" ] [ "hardware" "nvidia" "prime" "sync" "allowExternalGpu" ])
33 (mkRenamedOptionModule [ "hardware" "nvidia" "optimus_prime" "nvidiaBusId" ] [ "hardware" "nvidia" "prime" "nvidiaBusId" ])
34 (mkRenamedOptionModule [ "hardware" "nvidia" "optimus_prime" "intelBusId" ] [ "hardware" "nvidia" "prime" "intelBusId" ])
35 ];
36
37 options = {
38 hardware.nvidia.powerManagement.enable = mkOption {
39 type = types.bool;
40 default = false;
41 description = ''
42 Experimental power management through systemd. For more information, see
43 the NVIDIA docs, on Chapter 21. Configuring Power Management Support.
44 '';
45 };
46
47 hardware.nvidia.powerManagement.finegrained = mkOption {
48 type = types.bool;
49 default = false;
50 description = ''
51 Experimental power management of PRIME offload. For more information, see
52 the NVIDIA docs, chapter 22. PCI-Express runtime power management.
53 '';
54 };
55
56 hardware.nvidia.modesetting.enable = mkOption {
57 type = types.bool;
58 default = false;
59 description = ''
60 Enable kernel modesetting when using the NVIDIA proprietary driver.
61
62 Enabling this fixes screen tearing when using Optimus via PRIME (see
63 <option>hardware.nvidia.prime.sync.enable</option>. This is not enabled
64 by default because it is not officially supported by NVIDIA and would not
65 work with SLI.
66 '';
67 };
68
69 hardware.nvidia.prime.nvidiaBusId = mkOption {
70 type = types.str;
71 default = "";
72 example = "PCI:1:0:0";
73 description = ''
74 Bus ID of the NVIDIA GPU. You can find it using lspci; for example if lspci
75 shows the NVIDIA GPU at "01:00.0", set this option to "PCI:1:0:0".
76 '';
77 };
78
79 hardware.nvidia.prime.intelBusId = mkOption {
80 type = types.str;
81 default = "";
82 example = "PCI:0:2:0";
83 description = ''
84 Bus ID of the Intel GPU. You can find it using lspci; for example if lspci
85 shows the Intel GPU at "00:02.0", set this option to "PCI:0:2:0".
86 '';
87 };
88
89 hardware.nvidia.prime.amdgpuBusId = mkOption {
90 type = types.str;
91 default = "";
92 example = "PCI:4:0:0";
93 description = ''
94 Bus ID of the AMD APU. You can find it using lspci; for example if lspci
95 shows the AMD APU at "04:00.0", set this option to "PCI:4:0:0".
96 '';
97 };
98
99 hardware.nvidia.prime.sync.enable = mkOption {
100 type = types.bool;
101 default = false;
102 description = ''
103 Enable NVIDIA Optimus support using the NVIDIA proprietary driver via PRIME.
104 If enabled, the NVIDIA GPU will be always on and used for all rendering,
105 while enabling output to displays attached only to the integrated Intel GPU
106 without a multiplexer.
107
108 Note that this option only has any effect if the "nvidia" driver is specified
109 in <option>services.xserver.videoDrivers</option>, and it should preferably
110 be the only driver there.
111
112 If this is enabled, then the bus IDs of the NVIDIA and Intel GPUs have to be
113 specified (<option>hardware.nvidia.prime.nvidiaBusId</option> and
114 <option>hardware.nvidia.prime.intelBusId</option>).
115
116 If you enable this, you may want to also enable kernel modesetting for the
117 NVIDIA driver (<option>hardware.nvidia.modesetting.enable</option>) in order
118 to prevent tearing.
119
120 Note that this configuration will only be successful when a display manager
121 for which the <option>services.xserver.displayManager.setupCommands</option>
122 option is supported is used.
123 '';
124 };
125
126 hardware.nvidia.prime.sync.allowExternalGpu = mkOption {
127 type = types.bool;
128 default = false;
129 description = ''
130 Configure X to allow external NVIDIA GPUs when using optimus.
131 '';
132 };
133
134 hardware.nvidia.prime.offload.enable = mkOption {
135 type = types.bool;
136 default = false;
137 description = ''
138 Enable render offload support using the NVIDIA proprietary driver via PRIME.
139
140 If this is enabled, then the bus IDs of the NVIDIA and Intel GPUs have to be
141 specified (<option>hardware.nvidia.prime.nvidiaBusId</option> and
142 <option>hardware.nvidia.prime.intelBusId</option>).
143 '';
144 };
145
146 hardware.nvidia.nvidiaPersistenced = mkOption {
147 default = false;
148 type = types.bool;
149 description = ''
150 Update for NVIDA GPU headless mode, i.e. nvidia-persistenced. It ensures all
151 GPUs stay awake even during headless mode.
152 '';
153 };
154
155 hardware.nvidia.package = lib.mkOption {
156 type = lib.types.package;
157 default = config.boot.kernelPackages.nvidiaPackages.stable;
158 defaultText = "config.boot.kernelPackages.nvidiaPackages.stable";
159 description = ''
160 The NVIDIA X11 derivation to use.
161 '';
162 example = "config.boot.kernelPackages.nvidiaPackages.legacy340";
163 };
164 };
165
166 config = let
167 igpuDriver = if pCfg.intelBusId != "" then "modesetting" else "amdgpu";
168 igpuBusId = if pCfg.intelBusId != "" then pCfg.intelBusId else pCfg.amdgpuBusId;
169 in mkIf enabled {
170 assertions = [
171 {
172 assertion = with config.services.xserver.displayManager; gdm.nvidiaWayland -> cfg.modesetting.enable;
173 message = "You cannot use wayland with GDM without modesetting enabled for NVIDIA drivers, set `hardware.nvidia.modesetting.enable = true`";
174 }
175
176 {
177 assertion = primeEnabled -> pCfg.intelBusId == "" || pCfg.amdgpuBusId == "";
178 message = ''
179 You cannot configure both an Intel iGPU and an AMD APU. Pick the one corresponding to your processor.
180 '';
181 }
182 {
183 assertion = primeEnabled -> pCfg.nvidiaBusId != "" && (pCfg.intelBusId != "" || pCfg.amdgpuBusId != "");
184 message = ''
185 When NVIDIA PRIME is enabled, the GPU bus IDs must configured.
186 '';
187 }
188 {
189 assertion = offloadCfg.enable -> versionAtLeast nvidia_x11.version "435.21";
190 message = "NVIDIA PRIME render offload is currently only supported on versions >= 435.21.";
191 }
192 {
193 assertion = !(syncCfg.enable && offloadCfg.enable);
194 message = "Only one NVIDIA PRIME solution may be used at a time.";
195 }
196 {
197 assertion = !(syncCfg.enable && cfg.powerManagement.finegrained);
198 message = "Sync precludes powering down the NVIDIA GPU.";
199 }
200 {
201 assertion = cfg.powerManagement.enable -> offloadCfg.enable;
202 message = "Fine-grained power management requires offload to be enabled.";
203 }
204 ];
205
206 # If Optimus/PRIME is enabled, we:
207 # - Specify the configured NVIDIA GPU bus ID in the Device section for the
208 # "nvidia" driver.
209 # - Add the AllowEmptyInitialConfiguration option to the Screen section for the
210 # "nvidia" driver, in order to allow the X server to start without any outputs.
211 # - Add a separate Device section for the Intel GPU, using the "modesetting"
212 # driver and with the configured BusID.
213 # - OR add a separate Device section for the AMD APU, using the "amdgpu"
214 # driver and with the configures BusID.
215 # - Reference that Device section from the ServerLayout section as an inactive
216 # device.
217 # - Configure the display manager to run specific `xrandr` commands which will
218 # configure/enable displays connected to the Intel iGPU / AMD APU.
219
220 services.xserver.useGlamor = mkDefault offloadCfg.enable;
221
222 services.xserver.drivers = let
223 in optional primeEnabled {
224 name = igpuDriver;
225 display = offloadCfg.enable;
226 modules = optional (igpuDriver == "amdgpu") [ pkgs.xorg.xf86videoamdgpu ];
227 deviceSection = ''
228 BusID "${igpuBusId}"
229 ${optionalString syncCfg.enable ''Option "AccelMethod" "none"''}
230 '';
231 } ++ singleton {
232 name = "nvidia";
233 modules = [ nvidia_x11.bin ];
234 display = !offloadCfg.enable;
235 deviceSection = optionalString primeEnabled
236 ''
237 BusID "${pCfg.nvidiaBusId}"
238 ${optionalString syncCfg.allowExternalGpu "Option \"AllowExternalGpus\""}
239 ${optionalString cfg.powerManagement.finegrained "Option \"NVreg_DynamicPowerManagement=0x02\""}
240 '';
241 screenSection =
242 ''
243 Option "RandRRotation" "on"
244 ${optionalString syncCfg.enable "Option \"AllowEmptyInitialConfiguration\""}
245 '';
246 };
247
248 services.xserver.serverLayoutSection = optionalString syncCfg.enable ''
249 Inactive "Device-${igpuDriver}[0]"
250 '' + optionalString offloadCfg.enable ''
251 Option "AllowNVIDIAGPUScreens"
252 '';
253
254 services.xserver.displayManager.setupCommands = optionalString syncCfg.enable ''
255 # Added by nvidia configuration module for Optimus/PRIME.
256 ${pkgs.xorg.xrandr}/bin/xrandr --setprovideroutputsource ${igpuDriver} NVIDIA-0
257 ${pkgs.xorg.xrandr}/bin/xrandr --auto
258 '';
259
260 environment.etc."nvidia/nvidia-application-profiles-rc" = mkIf nvidia_x11.useProfiles {
261 source = "${nvidia_x11.bin}/share/nvidia/nvidia-application-profiles-rc";
262 };
263
264 hardware.opengl.package = mkIf (!offloadCfg.enable) nvidia_x11.out;
265 hardware.opengl.package32 = mkIf (!offloadCfg.enable) nvidia_x11.lib32;
266 hardware.opengl.extraPackages = optional offloadCfg.enable nvidia_x11.out;
267 hardware.opengl.extraPackages32 = optional offloadCfg.enable nvidia_x11.lib32;
268
269 environment.systemPackages = [ nvidia_x11.bin nvidia_x11.settings ]
270 ++ optionals nvidiaPersistencedEnabled [ nvidia_x11.persistenced ];
271
272 systemd.packages = optional cfg.powerManagement.enable nvidia_x11.out;
273
274 systemd.services = let
275 baseNvidiaService = state: {
276 description = "NVIDIA system ${state} actions";
277
278 path = with pkgs; [ kbd ];
279 serviceConfig = {
280 Type = "oneshot";
281 ExecStart = "${nvidia_x11.out}/bin/nvidia-sleep.sh '${state}'";
282 };
283 };
284
285 nvidiaService = sleepState: (baseNvidiaService sleepState) // {
286 before = [ "systemd-${sleepState}.service" ];
287 requiredBy = [ "systemd-${sleepState}.service" ];
288 };
289
290 services = (builtins.listToAttrs (map (t: nameValuePair "nvidia-${t}" (nvidiaService t)) ["hibernate" "suspend"]))
291 // {
292 nvidia-resume = (baseNvidiaService "resume") // {
293 after = [ "systemd-suspend.service" "systemd-hibernate.service" ];
294 requiredBy = [ "systemd-suspend.service" "systemd-hibernate.service" ];
295 };
296 };
297 in optionalAttrs cfg.powerManagement.enable services
298 // optionalAttrs nvidiaPersistencedEnabled {
299 "nvidia-persistenced" = mkIf nvidiaPersistencedEnabled {
300 description = "NVIDIA Persistence Daemon";
301 wantedBy = [ "multi-user.target" ];
302 serviceConfig = {
303 Type = "forking";
304 Restart = "always";
305 PIDFile = "/var/run/nvidia-persistenced/nvidia-persistenced.pid";
306 ExecStart = "${nvidia_x11.persistenced}/bin/nvidia-persistenced --verbose";
307 ExecStopPost = "${pkgs.coreutils}/bin/rm -rf /var/run/nvidia-persistenced";
308 };
309 };
310 };
311
312 systemd.tmpfiles.rules = optional config.virtualisation.docker.enableNvidia
313 "L+ /run/nvidia-docker/bin - - - - ${nvidia_x11.bin}/origBin"
314 ++ optional (nvidia_x11.persistenced != null && config.virtualisation.docker.enableNvidia)
315 "L+ /run/nvidia-docker/extras/bin/nvidia-persistenced - - - - ${nvidia_x11.persistenced}/origBin/nvidia-persistenced";
316
317 boot.extraModulePackages = [ nvidia_x11.bin ];
318
319 # nvidia-uvm is required by CUDA applications.
320 boot.kernelModules = [ "nvidia-uvm" ] ++
321 optionals config.services.xserver.enable [ "nvidia" "nvidia_modeset" "nvidia_drm" ];
322
323 # If requested enable modesetting via kernel parameter.
324 boot.kernelParams = optional (offloadCfg.enable || cfg.modesetting.enable) "nvidia-drm.modeset=1"
325 ++ optional cfg.powerManagement.enable "nvidia.NVreg_PreserveVideoMemoryAllocations=1";
326
327 services.udev.extraRules =
328 ''
329 # Create /dev/nvidia-uvm when the nvidia-uvm module is loaded.
330 KERNEL=="nvidia", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidiactl c $$(grep nvidia-frontend /proc/devices | cut -d \ -f 1) 255'"
331 KERNEL=="nvidia_modeset", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-modeset c $$(grep nvidia-frontend /proc/devices | cut -d \ -f 1) 254'"
332 KERNEL=="card*", SUBSYSTEM=="drm", DRIVERS=="nvidia", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia%n c $$(grep nvidia-frontend /proc/devices | cut -d \ -f 1) %n'"
333 KERNEL=="nvidia_uvm", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-uvm c $$(grep nvidia-uvm /proc/devices | cut -d \ -f 1) 0'"
334 KERNEL=="nvidia_uvm", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-uvm-tools c $$(grep nvidia-uvm /proc/devices | cut -d \ -f 1) 0'"
335 '' + optionalString cfg.powerManagement.finegrained ''
336 # Remove NVIDIA USB xHCI Host Controller devices, if present
337 ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c0330", ATTR{remove}="1"
338
339 # Remove NVIDIA USB Type-C UCSI devices, if present
340 ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c8000", ATTR{remove}="1"
341
342 # Remove NVIDIA Audio devices, if present
343 ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x040300", ATTR{remove}="1"
344
345 # Enable runtime PM for NVIDIA VGA/3D controller devices on driver bind
346 ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="auto"
347 ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="auto"
348
349 # Disable runtime PM for NVIDIA VGA/3D controller devices on driver unbind
350 ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="on"
351 ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="on"
352 '';
353
354 boot.extraModprobeConfig = mkIf cfg.powerManagement.finegrained ''
355 options nvidia "NVreg_DynamicPowerManagement=0x02"
356 '';
357
358 boot.blacklistedKernelModules = [ "nouveau" "nvidiafb" ];
359
360 services.acpid.enable = true;
361
362 };
363
364}