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 reverseSyncCfg = pCfg.reverseSync;
25 primeEnabled = syncCfg.enable || reverseSyncCfg.enable || offloadCfg.enable;
26 nvidiaPersistencedEnabled = cfg.nvidiaPersistenced;
27 nvidiaSettings = cfg.nvidiaSettings;
28 busIDType = types.strMatching "([[:print:]]+[\:\@][0-9]{1,3}\:[0-9]{1,2}\:[0-9])?";
29
30 ibtSupport = cfg.open || (nvidia_x11.ibtSupport or false);
31in
32
33{
34 imports =
35 [
36 (mkRenamedOptionModule [ "hardware" "nvidia" "optimus_prime" "enable" ] [ "hardware" "nvidia" "prime" "sync" "enable" ])
37 (mkRenamedOptionModule [ "hardware" "nvidia" "optimus_prime" "allowExternalGpu" ] [ "hardware" "nvidia" "prime" "allowExternalGpu" ])
38 (mkRenamedOptionModule [ "hardware" "nvidia" "prime" "sync" "allowExternalGpu" ] [ "hardware" "nvidia" "prime" "allowExternalGpu" ])
39 (mkRenamedOptionModule [ "hardware" "nvidia" "optimus_prime" "nvidiaBusId" ] [ "hardware" "nvidia" "prime" "nvidiaBusId" ])
40 (mkRenamedOptionModule [ "hardware" "nvidia" "optimus_prime" "intelBusId" ] [ "hardware" "nvidia" "prime" "intelBusId" ])
41 ];
42
43 options = {
44 hardware.nvidia.powerManagement.enable = mkOption {
45 type = types.bool;
46 default = false;
47 description = lib.mdDoc ''
48 Experimental power management through systemd. For more information, see
49 the NVIDIA docs, on Chapter 21. Configuring Power Management Support.
50 '';
51 };
52
53 hardware.nvidia.powerManagement.finegrained = mkOption {
54 type = types.bool;
55 default = false;
56 description = lib.mdDoc ''
57 Experimental power management of PRIME offload. For more information, see
58 the NVIDIA docs, chapter 22. PCI-Express runtime power management.
59 '';
60 };
61
62 hardware.nvidia.modesetting.enable = mkOption {
63 type = types.bool;
64 default = false;
65 description = lib.mdDoc ''
66 Enable kernel modesetting when using the NVIDIA proprietary driver.
67
68 Enabling this fixes screen tearing when using Optimus via PRIME (see
69 {option}`hardware.nvidia.prime.sync.enable`. This is not enabled
70 by default because it is not officially supported by NVIDIA and would not
71 work with SLI.
72 '';
73 };
74
75 hardware.nvidia.prime.nvidiaBusId = mkOption {
76 type = busIDType;
77 default = "";
78 example = "PCI:1:0:0";
79 description = lib.mdDoc ''
80 Bus ID of the NVIDIA GPU. You can find it using lspci; for example if lspci
81 shows the NVIDIA GPU at "01:00.0", set this option to "PCI:1:0:0".
82 '';
83 };
84
85 hardware.nvidia.prime.intelBusId = mkOption {
86 type = busIDType;
87 default = "";
88 example = "PCI:0:2:0";
89 description = lib.mdDoc ''
90 Bus ID of the Intel GPU. You can find it using lspci; for example if lspci
91 shows the Intel GPU at "00:02.0", set this option to "PCI:0:2:0".
92 '';
93 };
94
95 hardware.nvidia.prime.amdgpuBusId = mkOption {
96 type = busIDType;
97 default = "";
98 example = "PCI:4:0:0";
99 description = lib.mdDoc ''
100 Bus ID of the AMD APU. You can find it using lspci; for example if lspci
101 shows the AMD APU at "04:00.0", set this option to "PCI:4:0:0".
102 '';
103 };
104
105 hardware.nvidia.prime.sync.enable = mkOption {
106 type = types.bool;
107 default = false;
108 description = lib.mdDoc ''
109 Enable NVIDIA Optimus support using the NVIDIA proprietary driver via PRIME.
110 If enabled, the NVIDIA GPU will be always on and used for all rendering,
111 while enabling output to displays attached only to the integrated Intel/AMD
112 GPU without a multiplexer.
113
114 Note that this option only has any effect if the "nvidia" driver is specified
115 in {option}`services.xserver.videoDrivers`, and it should preferably
116 be the only driver there.
117
118 If this is enabled, then the bus IDs of the NVIDIA and Intel/AMD GPUs have to
119 be specified ({option}`hardware.nvidia.prime.nvidiaBusId` and
120 {option}`hardware.nvidia.prime.intelBusId` or
121 {option}`hardware.nvidia.prime.amdgpuBusId`).
122
123 If you enable this, you may want to also enable kernel modesetting for the
124 NVIDIA driver ({option}`hardware.nvidia.modesetting.enable`) in order
125 to prevent tearing.
126
127 Note that this configuration will only be successful when a display manager
128 for which the {option}`services.xserver.displayManager.setupCommands`
129 option is supported is used.
130 '';
131 };
132
133 hardware.nvidia.prime.allowExternalGpu = mkOption {
134 type = types.bool;
135 default = false;
136 description = lib.mdDoc ''
137 Configure X to allow external NVIDIA GPUs when using Prime [Reverse] sync optimus.
138 '';
139 };
140
141 hardware.nvidia.prime.offload.enable = mkOption {
142 type = types.bool;
143 default = false;
144 description = lib.mdDoc ''
145 Enable render offload support using the NVIDIA proprietary driver via PRIME.
146
147 If this is enabled, then the bus IDs of the NVIDIA and Intel/AMD GPUs have to
148 be specified ({option}`hardware.nvidia.prime.nvidiaBusId` and
149 {option}`hardware.nvidia.prime.intelBusId` or
150 {option}`hardware.nvidia.prime.amdgpuBusId`).
151 '';
152 };
153
154 hardware.nvidia.prime.offload.enableOffloadCmd = mkOption {
155 type = types.bool;
156 default = false;
157 description = lib.mdDoc ''
158 Adds a `nvidia-offload` convenience script to {option}`environment.systemPackages`
159 for offloading programs to an nvidia device. To work, should have also enabled
160 {option}`hardware.nvidia.prime.offload.enable` or {option}`hardware.nvidia.prime.reverseSync.enable`.
161
162 Example usage `nvidia-offload sauerbraten_client`.
163 '';
164 };
165
166 hardware.nvidia.prime.reverseSync.enable = mkOption {
167 type = types.bool;
168 default = false;
169 description = lib.mdDoc ''
170 Warning: This feature is relatively new, depending on your system this might
171 work poorly. AMD support, especially so.
172 See: https://forums.developer.nvidia.com/t/the-all-new-outputsink-feature-aka-reverse-prime/129828
173
174 Enable NVIDIA Optimus support using the NVIDIA proprietary driver via reverse
175 PRIME. If enabled, the Intel/AMD GPU will be used for all rendering, while
176 enabling output to displays attached only to the NVIDIA GPU without a
177 multiplexer.
178
179 Note that this option only has any effect if the "nvidia" driver is specified
180 in {option}`services.xserver.videoDrivers`, and it should preferably
181 be the only driver there.
182
183 If this is enabled, then the bus IDs of the NVIDIA and Intel/AMD GPUs have to
184 be specified ({option}`hardware.nvidia.prime.nvidiaBusId` and
185 {option}`hardware.nvidia.prime.intelBusId` or
186 {option}`hardware.nvidia.prime.amdgpuBusId`).
187
188 If you enable this, you may want to also enable kernel modesetting for the
189 NVIDIA driver ({option}`hardware.nvidia.modesetting.enable`) in order
190 to prevent tearing.
191
192 Note that this configuration will only be successful when a display manager
193 for which the {option}`services.xserver.displayManager.setupCommands`
194 option is supported is used.
195 '';
196 };
197
198 hardware.nvidia.nvidiaSettings = mkOption {
199 default = true;
200 type = types.bool;
201 description = lib.mdDoc ''
202 Whether to add nvidia-settings, NVIDIA's GUI configuration tool, to
203 systemPackages.
204 '';
205 };
206
207 hardware.nvidia.nvidiaPersistenced = mkOption {
208 default = false;
209 type = types.bool;
210 description = lib.mdDoc ''
211 Update for NVIDA GPU headless mode, i.e. nvidia-persistenced. It ensures all
212 GPUs stay awake even during headless mode.
213 '';
214 };
215
216 hardware.nvidia.forceFullCompositionPipeline = lib.mkOption {
217 default = false;
218 type = types.bool;
219 description = lib.mdDoc ''
220 Whether to force-enable the full composition pipeline.
221 This sometimes fixes screen tearing issues.
222 This has been reported to reduce the performance of some OpenGL applications and may produce issues in WebGL.
223 It also drastically increases the time the driver needs to clock down after load.
224 '';
225 };
226
227 hardware.nvidia.package = lib.mkOption {
228 type = types.package;
229 default = config.boot.kernelPackages.nvidiaPackages.stable;
230 defaultText = literalExpression "config.boot.kernelPackages.nvidiaPackages.stable";
231 description = lib.mdDoc ''
232 The NVIDIA X11 derivation to use.
233 '';
234 example = literalExpression "config.boot.kernelPackages.nvidiaPackages.legacy_340";
235 };
236
237 hardware.nvidia.open = lib.mkOption {
238 type = lib.types.bool;
239 default = false;
240 description = lib.mdDoc ''
241 Whether to use the open source kernel module
242 '';
243 };
244 };
245
246 config = let
247 igpuDriver = if pCfg.intelBusId != "" then "modesetting" else "amdgpu";
248 igpuBusId = if pCfg.intelBusId != "" then pCfg.intelBusId else pCfg.amdgpuBusId;
249 in mkIf enabled {
250 assertions = [
251 {
252 assertion = primeEnabled -> pCfg.intelBusId == "" || pCfg.amdgpuBusId == "";
253 message = ''
254 You cannot configure both an Intel iGPU and an AMD APU. Pick the one corresponding to your processor.
255 '';
256 }
257
258 {
259 assertion = offloadCfg.enableOffloadCmd -> offloadCfg.enable || reverseSyncCfg.enable;
260 message = ''
261 Offload command requires offloading or reverse prime sync to be enabled.
262 '';
263 }
264
265 {
266 assertion = primeEnabled -> pCfg.nvidiaBusId != "" && (pCfg.intelBusId != "" || pCfg.amdgpuBusId != "");
267 message = ''
268 When NVIDIA PRIME is enabled, the GPU bus IDs must configured.
269 '';
270 }
271
272 {
273 assertion = offloadCfg.enable -> versionAtLeast nvidia_x11.version "435.21";
274 message = "NVIDIA PRIME render offload is currently only supported on versions >= 435.21.";
275 }
276
277 {
278 assertion = (reverseSyncCfg.enable && pCfg.amdgpuBusId != "") -> versionAtLeast nvidia_x11.version "470.0";
279 message = "NVIDIA PRIME render offload for AMD APUs is currently only supported on versions >= 470 beta.";
280 }
281
282 {
283 assertion = !(syncCfg.enable && offloadCfg.enable);
284 message = "PRIME Sync and Offload cannot be both enabled";
285 }
286
287 {
288 assertion = !(syncCfg.enable && reverseSyncCfg.enable);
289 message = "PRIME Sync and PRIME Reverse Sync cannot be both enabled";
290 }
291
292 {
293 assertion = !(syncCfg.enable && cfg.powerManagement.finegrained);
294 message = "Sync precludes powering down the NVIDIA GPU.";
295 }
296
297 {
298 assertion = cfg.powerManagement.finegrained -> offloadCfg.enable;
299 message = "Fine-grained power management requires offload to be enabled.";
300 }
301
302 {
303 assertion = cfg.powerManagement.enable -> versionAtLeast nvidia_x11.version "430.09";
304 message = "Required files for driver based power management only exist on versions >= 430.09.";
305 }
306
307 {
308 assertion = cfg.open -> (cfg.package ? open && cfg.package ? firmware);
309 message = "This version of NVIDIA driver does not provide a corresponding opensource kernel driver";
310 }
311 ];
312
313 # If Optimus/PRIME is enabled, we:
314 # - Specify the configured NVIDIA GPU bus ID in the Device section for the
315 # "nvidia" driver.
316 # - Add the AllowEmptyInitialConfiguration option to the Screen section for the
317 # "nvidia" driver, in order to allow the X server to start without any outputs.
318 # - Add a separate Device section for the Intel GPU, using the "modesetting"
319 # driver and with the configured BusID.
320 # - OR add a separate Device section for the AMD APU, using the "amdgpu"
321 # driver and with the configures BusID.
322 # - Reference that Device section from the ServerLayout section as an inactive
323 # device.
324 # - Configure the display manager to run specific `xrandr` commands which will
325 # configure/enable displays connected to the Intel iGPU / AMD APU.
326
327 # reverse sync implies offloading
328 hardware.nvidia.prime.offload.enable = mkDefault reverseSyncCfg.enable;
329
330 services.xserver.drivers = optional primeEnabled {
331 name = igpuDriver;
332 display = offloadCfg.enable;
333 modules = optionals (igpuDriver == "amdgpu") [ pkgs.xorg.xf86videoamdgpu ];
334 deviceSection = ''
335 BusID "${igpuBusId}"
336 ${optionalString (syncCfg.enable && igpuDriver != "amdgpu") ''Option "AccelMethod" "none"''}
337 '';
338 } ++ singleton {
339 name = "nvidia";
340 modules = [ nvidia_x11.bin ];
341 display = !offloadCfg.enable;
342 deviceSection = optionalString primeEnabled
343 ''
344 BusID "${pCfg.nvidiaBusId}"
345 ${optionalString pCfg.allowExternalGpu "Option \"AllowExternalGpus\""}
346 '';
347 screenSection =
348 ''
349 Option "RandRRotation" "on"
350 '' + optionalString syncCfg.enable ''
351 Option "AllowEmptyInitialConfiguration"
352 '' + optionalString cfg.forceFullCompositionPipeline ''
353 Option "metamodes" "nvidia-auto-select +0+0 {ForceFullCompositionPipeline=On}"
354 Option "AllowIndirectGLXProtocol" "off"
355 Option "TripleBuffer" "on"
356 ''
357 ;
358 };
359
360 services.xserver.serverLayoutSection = optionalString syncCfg.enable ''
361 Inactive "Device-${igpuDriver}[0]"
362 '' + optionalString reverseSyncCfg.enable ''
363 Inactive "Device-nvidia[0]"
364 '' + optionalString offloadCfg.enable ''
365 Option "AllowNVIDIAGPUScreens"
366 '';
367
368 services.xserver.displayManager.setupCommands = let
369 gpuProviderName = if igpuDriver == "amdgpu" then
370 # find the name of the provider if amdgpu
371 "`${pkgs.xorg.xrandr}/bin/xrandr --listproviders | ${pkgs.gnugrep}/bin/grep -i AMD | ${pkgs.gnused}/bin/sed -n 's/^.*name://p'`"
372 else
373 igpuDriver;
374 providerCmdParams = if syncCfg.enable then "\"${gpuProviderName}\" NVIDIA-0" else "NVIDIA-G0 \"${gpuProviderName}\"";
375 in optionalString (syncCfg.enable || reverseSyncCfg.enable) ''
376 # Added by nvidia configuration module for Optimus/PRIME.
377 ${pkgs.xorg.xrandr}/bin/xrandr --setprovideroutputsource ${providerCmdParams}
378 ${pkgs.xorg.xrandr}/bin/xrandr --auto
379 '';
380
381 environment.etc."nvidia/nvidia-application-profiles-rc" = mkIf nvidia_x11.useProfiles {
382 source = "${nvidia_x11.bin}/share/nvidia/nvidia-application-profiles-rc";
383 };
384
385 # 'nvidia_x11' installs it's files to /run/opengl-driver/...
386 environment.etc."egl/egl_external_platform.d".source =
387 "/run/opengl-driver/share/egl/egl_external_platform.d/";
388
389 hardware.opengl.extraPackages = [
390 nvidia_x11.out
391 pkgs.nvidia-vaapi-driver
392 ];
393 hardware.opengl.extraPackages32 = [
394 nvidia_x11.lib32
395 pkgs.pkgsi686Linux.nvidia-vaapi-driver
396 ];
397
398 environment.systemPackages = [ nvidia_x11.bin ]
399 ++ optionals cfg.nvidiaSettings [ nvidia_x11.settings ]
400 ++ optionals nvidiaPersistencedEnabled [ nvidia_x11.persistenced ]
401 ++ optionals offloadCfg.enableOffloadCmd [
402 (pkgs.writeShellScriptBin "nvidia-offload" ''
403 export __NV_PRIME_RENDER_OFFLOAD=1
404 export __NV_PRIME_RENDER_OFFLOAD_PROVIDER=NVIDIA-G0
405 export __GLX_VENDOR_LIBRARY_NAME=nvidia
406 export __VK_LAYER_NV_optimus=NVIDIA_only
407 exec "$@"
408 '')
409 ];
410
411 systemd.packages = optional cfg.powerManagement.enable nvidia_x11.out;
412
413 systemd.services = let
414 baseNvidiaService = state: {
415 description = "NVIDIA system ${state} actions";
416
417 path = with pkgs; [ kbd ];
418 serviceConfig = {
419 Type = "oneshot";
420 ExecStart = "${nvidia_x11.out}/bin/nvidia-sleep.sh '${state}'";
421 };
422 };
423
424 nvidiaService = sleepState: (baseNvidiaService sleepState) // {
425 before = [ "systemd-${sleepState}.service" ];
426 requiredBy = [ "systemd-${sleepState}.service" ];
427 };
428
429 services = (builtins.listToAttrs (map (t: nameValuePair "nvidia-${t}" (nvidiaService t)) ["hibernate" "suspend"]))
430 // {
431 nvidia-resume = (baseNvidiaService "resume") // {
432 after = [ "systemd-suspend.service" "systemd-hibernate.service" ];
433 requiredBy = [ "systemd-suspend.service" "systemd-hibernate.service" ];
434 };
435 };
436 in optionalAttrs cfg.powerManagement.enable services
437 // optionalAttrs nvidiaPersistencedEnabled {
438 "nvidia-persistenced" = mkIf nvidiaPersistencedEnabled {
439 description = "NVIDIA Persistence Daemon";
440 wantedBy = [ "multi-user.target" ];
441 serviceConfig = {
442 Type = "forking";
443 Restart = "always";
444 PIDFile = "/var/run/nvidia-persistenced/nvidia-persistenced.pid";
445 ExecStart = "${nvidia_x11.persistenced}/bin/nvidia-persistenced --verbose";
446 ExecStopPost = "${pkgs.coreutils}/bin/rm -rf /var/run/nvidia-persistenced";
447 };
448 };
449 };
450
451 systemd.tmpfiles.rules = optional config.virtualisation.docker.enableNvidia
452 "L+ /run/nvidia-docker/bin - - - - ${nvidia_x11.bin}/origBin"
453 ++ optional (nvidia_x11.persistenced != null && config.virtualisation.docker.enableNvidia)
454 "L+ /run/nvidia-docker/extras/bin/nvidia-persistenced - - - - ${nvidia_x11.persistenced}/origBin/nvidia-persistenced";
455
456 boot.extraModulePackages = if cfg.open then [ nvidia_x11.open ] else [ nvidia_x11.bin ];
457 hardware.firmware = lib.optional cfg.open nvidia_x11.firmware;
458
459 # nvidia-uvm is required by CUDA applications.
460 boot.kernelModules = [ "nvidia-uvm" ] ++
461 optionals config.services.xserver.enable [ "nvidia" "nvidia_modeset" "nvidia_drm" ];
462
463 # If requested enable modesetting via kernel parameter.
464 boot.kernelParams = optional (offloadCfg.enable || cfg.modesetting.enable) "nvidia-drm.modeset=1"
465 ++ optional cfg.powerManagement.enable "nvidia.NVreg_PreserveVideoMemoryAllocations=1"
466 ++ optional cfg.open "nvidia.NVreg_OpenRmEnableUnsupportedGpus=1"
467 ++ optional (config.boot.kernelPackages.kernel.kernelAtLeast "6.2" && !ibtSupport) "ibt=off";
468
469 services.udev.extraRules =
470 ''
471 # Create /dev/nvidia-uvm when the nvidia-uvm module is loaded.
472 KERNEL=="nvidia", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidiactl c $$(grep nvidia-frontend /proc/devices | cut -d \ -f 1) 255'"
473 KERNEL=="nvidia", RUN+="${pkgs.runtimeShell} -c 'for i in $$(cat /proc/driver/nvidia/gpus/*/information | grep Minor | cut -d \ -f 4); do mknod -m 666 /dev/nvidia$${i} c $$(grep nvidia-frontend /proc/devices | cut -d \ -f 1) $${i}; done'"
474 KERNEL=="nvidia_modeset", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-modeset c $$(grep nvidia-frontend /proc/devices | cut -d \ -f 1) 254'"
475 KERNEL=="nvidia_uvm", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-uvm c $$(grep nvidia-uvm /proc/devices | cut -d \ -f 1) 0'"
476 KERNEL=="nvidia_uvm", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-uvm-tools c $$(grep nvidia-uvm /proc/devices | cut -d \ -f 1) 1'"
477 '' + optionalString cfg.powerManagement.finegrained (
478 optionalString (versionOlder config.boot.kernelPackages.kernel.version "5.5") ''
479 # Remove NVIDIA USB xHCI Host Controller devices, if present
480 ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c0330", ATTR{remove}="1"
481
482 # Remove NVIDIA USB Type-C UCSI devices, if present
483 ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c8000", ATTR{remove}="1"
484
485 # Remove NVIDIA Audio devices, if present
486 ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x040300", ATTR{remove}="1"
487 '' + ''
488 # Enable runtime PM for NVIDIA VGA/3D controller devices on driver bind
489 ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="auto"
490 ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="auto"
491
492 # Disable runtime PM for NVIDIA VGA/3D controller devices on driver unbind
493 ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="on"
494 ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="on"
495 '');
496
497 boot.extraModprobeConfig = mkIf cfg.powerManagement.finegrained ''
498 options nvidia "NVreg_DynamicPowerManagement=0x02"
499 '';
500
501 boot.blacklistedKernelModules = [ "nouveau" "nvidiafb" ];
502
503 services.acpid.enable = true;
504
505 };
506
507}