Nouveau & nvidia-drivers switching

From Gentoo Wiki
(Redirected from Nouveau & Nvidia Switching)
Jump to: navigation, search

Switching between nVidia's binary driver and the open source nouveau driver can be tricky, but definitely doable. This article will detail how to go about switching between them at different stages (i.e. at boot, in a runlevel, etc.)

Switching using two kernels

On Boot

This method assumes the following:

  • Two kernels, one with the nouveau driver enabled and one with the nouveau driver disabled (or built as a module).
  • The nouveau-less kernel needs to have a -nvidia suffix.
  • The GRUB bootloader.

It is assumed you already have a nouveau kernel and want to build the one that will use the nvidia driver. Begin with appending the -nvidia suffix to the kernel name:

Kernel configuration

General setup  --->
    (-mykernel-nvidia) Local version - append to kernel release

Now make sure the nouveau driver is disabled or built as a module:

Kernel configuration

Device Drivers  --->
    Graphics support  --->
        <*> Direct Rendering Manager (XFree86 4.1.0 and higher DRI support) --->
            <M> Nouveau (nVidia) cards

Now recompile and install the kernel. Make sure you update GRUB to take the new kernel into account. Note that kernels with the same version but with different names are considered unique. So if you want to emerge an out-of-tree kernel module to be used with the nouveau kernel you'll first need to copy over the nouveau kernels config file to /usr/src/linux/.config.

Portage uses the variable VIDEO_CARDS for enabling support for various graphics cards in packages. Setting VIDEO_CARDS to nvidia nouveau will pull in the correct driver:

File/etc/portage/make.conf

 VIDEO_CARDS="nvidia nouveau"

After setting this you want to update your system so the changes take effect:

root # emerge --ask --changed-use --deep world

Blacklist the nvidia and nouveau modules as you don't want udev to try and autoload them.

root # echo -e "blacklist nouveau\nblacklist nvidia" >> /etc/modprobe.d/nvidia-n-nouveau.conf

And finally use the local.d script below to switch graphics driver depending on what kernel you booted with:

File/etc/local.d/nvidia.start

#!/bin/bash

depend() {
	need localmount
	before xdm
}

if [[ $(uname -r) == *-nvidia ]] ; then
	modprobe -q nvidia
	if [[ $(eselect opengl show) != nvidia ]] ; then
		eselect opengl set nvidia &>/dev/null
	fi
cat > /etc/X11/xorg.conf.d/01-nvidia.conf << EOF
Section "Device"
  Identifier   "Device0"
  Driver       "nvidia"
  Option       "NoLogo" "True"
EndSection
EOF
else
	modprobe -q nouveau
	if [ -f /etc/X11/xorg.conf.d/01-nvidia.conf ] ; then
		rm /etc/X11/xorg.conf.d/01-nvidia.conf
	fi
	if [[ $(eselect opengl show) != xorg-x11 ]] ; then
		eselect opengl set xorg-x11 &>/dev/null
	fi
fi

Copy it to /etc/local.d/nvidia.start and set the executable bit:

root # chmod +x /etc/local.d/nvidia.start

Switching using a single kernel and hprofile

Another method is switching between two profiles with hprofile, using a single kernel: /etc/init.d/hprofile has to be modified adding a few lines for vga switching. Warning: hprofile shall be added in boot runlevel.

Switching using a single kernel and systemd

If you have your system configured to use systemd, you can control setting nvidia and nouveau by using the functionality provided by the kernel command line.

Note
The grub2 menu items shown are for demonstrating the changes to be made. Hopefully you have the common sense not to just copy and paste them verbatim into your grub.cfg.

Within the grub.cfg file you need to duplicate the desired menu item and then edit the two versions to specify which modules with be blacklisted, and to pass an environment variable to systemd. If you previously blacklisted nvidia or nouveau modules in /etc/modprobe.d then you need to remove them since that will now be controlled directly from the kernel command line.

File/boot/grub2/grub.cfgBefore:

menuentry 'Gentoo Linux' {
        root=hd0,1
        linux /boot/bzImage root=PARTUUID=11111111-2222-3333-4444-555555555555 init=/usr/lib/systemd/systemd console=tty1
}
File/boot/grub2/grub.cfgAfter:

menuentry 'Gentoo Linux - Nouveau' {
        root=hd0,1
        linux /boot/bzImage root=PARTUUID=11111111-2222-3333-4444-555555555555 init=/usr/lib/systemd/systemd console=tty1 modprobe.blacklist=nvidia systemd.setenv=GPUMOD=nouveau
}

menuentry 'Gentoo Linux - Nvidia' {
        root=hd0,1
        linux /boot/bzImage root=PARTUUID=11111111-2222-3333-4444-555555555555 init=/usr/lib/systemd/systemd console=tty1 modprobe.blacklist=nouveau systemd.setenv=GPUMOD=nvidia
}

Next we are going to use a small script similar to the one shown in the "Switching using two kernels" section. It is very similar, but slightly modified. For the sake on continuity we will place it in the same location. The main difference is that is does not use the kernel's name to determine which profile to use, but rather it uses a variable set in the environment by systemd from the kernel command line.

File/etc/local.d/nvidia.startOpenGL

#!/bin/bash

if [[ "$GPUMOD" == "nvidia" ]] ; then
        if [[ $(eselect opengl show) != nvidia ]] ; then
                eselect opengl set nvidia &>/dev/null
        fi
cat > /etc/X11/xorg.conf.d/01-nv.conf << EOF
Section "Device"
  Identifier   "Device0"
  Driver       "nvidia"
  Option       "NoLogo" "True"
EndSection
EOF
else
        if [[ $(eselect opengl show) != xorg-x11 ]] ; then
                eselect opengl set xorg-x11 &>/dev/null
        fi
cat > /etc/X11/xorg.conf.d/01-nv.conf << EOF
Section "Device"
  Identifier   "Device0"
  Driver       "nouveau"
EndSection
EOF
fi

If you have any software that uses opencl, then it is recommend you use this alternate version of the script that also switches the opencl implementation between mesa and nvidia. If software tries to use opencl under the nouveau driver, but the opencl is still set to nvidia, then bad things happen. You can have the system change to Mesa's Clover Gallium OpenCL state tracker, but you must first ensure that mesa has been built with the opencl USE flag enabled. If that dependency has been taken care of then use the following version.

File/etc/local.d/nvidia.startOpenGL + OpenCL

#!/bin/bash

if [[ "$GPUMOD" == "nvidia" ]] ; then
        if [[ $(eselect opengl show) != nvidia ]] ; then
                eselect opengl set nvidia &>/dev/null
                eselect opencl set nvidia &>/dev/null
        fi
cat > /etc/X11/xorg.conf.d/01-nv.conf << EOF
Section "Device"
  Identifier   "Device0"
  Driver       "nvidia"
  Option       "NoLogo" "True"
EndSection
EOF
else
        if [[ $(eselect opengl show) != xorg-x11 ]] ; then
                eselect opengl set xorg-x11 &>/dev/null
                eselect opencl set mesa &>/dev/null
        fi
cat > /etc/X11/xorg.conf.d/01-nv.conf << EOF
Section "Device"
  Identifier   "Device0"
  Driver       "nouveau"
EndSection
EOF
fi

As mentioned in the previous section, don't forget to set the executable bit.

root # chmod +x /etc/local.d/nvidia.start

To have this script get called during the systemd init process, you can create a service file for it, but it's just as effective to integrate it into the currently existing xdm | gdm | kdm | other(?) display manager service file. Edit the desired service file located in /usr/lib/systemd/system and under the "[Service]" section, add an entry for ExecStartPre. This is used to specify any command to be run before the main command of the service file is executed. The example shown below for demonstration is a modified version of the default kdm service file, but the same modification can be made to any display manager service file.

File/usr/lib/systemd/system/kdm.serviceActual contents may vary

[Unit]
Description=KDM Display Manager
Conflicts=getty@tty1.service
After=systemd-user-sessions.service getty@tty1.service plymouth-quit.service

[Service]
ExecStartPre=/etc/local.d/nvidia.start
ExecStart=/usr/bin/kdm -nodaemon
Restart=always
IgnoreSIGPIPE=no

[Install]
Alias=display-manager.service