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