Triple Head with nVidia Optimus on Fedora 21 (includes Docking udev Rules for Systemd)
In this post, we’re going to look at how to setup a laptop (Lenovo T530, in this case) to utilize both the nVidia and Intel GPUs to drive a triple head or quadruple head system and to automatically configure those displays at boot, on docking, and undocking.
If you don’t have a Lenovo T530, aren’t using the ThinkPad Mini Dock Plus Series 3 dock, or aren’t running Fedora 21, you’ll need to alter the instructions to fit your needs. As well, you’ll definitely need to alter the scripts for your display configurations.
Let’s begin!
First, let’s install the KMS and DDX drivers for nVidia. You can go to nVidia’s website and get the installer, or you can just grab them from RPMFusion. Personally, I prefer the latter.
To enable the RPMFusion Free and Non-Free yum repository, run the following command:
su -c 'yum localinstall --nogpgcheck \ http://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm \ http://download1.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm'
Next, install the nVidia drivers:
su -c 'yum install kmod-nvidia xorg-x11-drv-nvidia xorg-x11-drv-nvidia-libs'
Now, edit your xorg.conf file so it looks like so:
Section "ServerLayout" Identifier "layout" Screen 0 "nvidia" Inactive "intel" EndSection Section "Device" Identifier "nvidia" Driver "nvidia" BusID "PCI:1:0:0" EndSection Section "Screen" Identifier "nvidia" Device "nvidia" Option "AllowEmptyInitialConfiguration" Option "ConstrainCursor" "no" EndSection Section "Device" Identifier "intel" Driver "modesetting" EndSection Section "Screen" Identifier "intel" Device "intel" EndSection
Now for the more complicated part! Scripting the startup.
First, we need to create a script that will run after X initializes. Do note that this initializes after you’ve logged in, so your Display Manager login will only show up on the nVidia outputs. It’s a bit of a caveat that causes issues with logging in while undocked. We’ll discuss those in a minute though.
Create the following file:
#!/bin/bash # # configure monitors using xrandr rather than xorg.conf # AT_HOME=$(xrandr | grep -q 3840x2160)$? AT_WORK=$(xrandr | grep -q 1920x1080)$? AT_REMOTE=$(( $AT_HOME ^ $AT_WORK )) XRANDR="/usr/bin/xrandr -d :0" $XRANDR --setprovideroutputsource modesetting NVIDIA-0 sleep 1 $XRANDR --auto sleep 3 if [ $AT_HOME -eq 0 ]; then $XRANDR --output LVDS-1-0 --off --output DP-2 --rotate left --right-of DP-4 elif [ $AT_WORK -eq 0 ]; then $XRANDR --output LVDS-1-0 --off --output DP-2 --left-of DP-1 --output VGA-1-0 --left-of DP-2 elif [ $AT_REMOTE -eq 0 ]; then $XRANDR --output LVDS-1-0 --auto --output VGA-1-0 --off --output DP-1 --off --output DP-2 --off fi
I think the script is pretty self explanatory. The AT_HOME/WORK variables are set to 0 or 1 if they find the resolution specified. AT_REMOTE is set based on the previous two. The –setprovideroutputsource tells xrandr that the nVidia GPU is going to do the rendering for the displays connected to the Intel GPU. We sleep to make sure that happens. Now turn all the displays on with `xrandr –auto` and sleep while that updates. Next we configure the setup we want based on where we are.
Set the executable permissions bit on the file:
su -c 'chmod +x /etc/X11/xinit/xinitrc.d/40-xrandr.conf'
Now, if you restart your laptop while docked and login, it should work! If you’re not docked, however, you’ll be staring at a black screen. This is because the xorg configuration is outputting to the outputs connected to the nVidia GPU only (DisplayPort/DVI), so no output is going to the panel or VGA. So, how do we fix this? Well, you can memorize the keystrokes required to login when you’re facing a blank screen… or you can enable auto login. Let’s do the latter!
Assuming you’re using gdm, you’ll want to add the AutomaticLogin and AutomaticLoginEnable options to /etc/gdm/custom.conf so it looks like the following, replacing YOUR_USERNAME_HERE with your username:
# GDM configuration storage [daemon] AutomaticLogin=YOUR_USERNAME_HERE AutomaticLoginEnable=True [security] [xdmcp] [greeter] [chooser] [debug]
Now we need to tell gdm that it’s A-OK to let you login without a password! You’re going to add a new line at the very top of /etc/pam.d/gdm-password so that it looks like so:
auth sufficient pam_succeed_if.so user ingroup nopasswdlogin auth [success=done ignore=ignore default=bad] pam_selinux_permit.so auth substack password-auth auth optional pam_gnome_keyring.so account required pam_nologin.so account include password-auth password substack password-auth -password optional pam_gnome_keyring.so use_authtok session required pam_selinux.so close session required pam_loginuid.so session optional pam_console.so -session optional pam_ck_connector.so session required pam_selinux.so open session optional pam_keyinit.so force revoke session required pam_namespace.so session include password-auth session optional pam_gnome_keyring.so auto_start
This tells PAM that authentication is sufficient if the user belongs to the ‘nopasswdlogin’ group. But, that group doesn’t exist! Let’s create it, replacing YOUR_USERNAME_HERE with your username once again:
su -c 'groupadd nopasswdlogin' su -c 'usermod -a -G nopasswdlogin YOUR_USERNAME_HERE'
Now when gdm loads, it will automatically login with your user! Now that we’re done throwing security out the window, let’s tackle the issue of docking and undocking events.
Systemd is the default in Fedora. If you’re opting to use acpid, you can check out this solution here. For this post, I’m going to assume you’re sticking with the defaults in Fedora. So, Systemd uses udev as its device manager and it has a really nice feature that lets you create rules for various devices you plug in. Let’s create a new rule for our dock:
SUBSYSTEM=="usb", DEVPATH=="/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.8", RUN+="/etc/X11/xinit/xinitrc.d/40-xrandr.conf"
Update 2015-03-09: After an update somewhere in the last month or two, my udev rule above quit working properly due to a change in the dev path. As such, I’ve updated it to the following:
SUBSYSTEM=="drm", DEVPATH=="/devices/pci0000:00/0000:00:02.0/drm/card0", RUN+="/usr/local/bin/set_resolution.sh"
The rest of the article is based on the original rule and has not been updated.
// Update 2015-03-09
Now, you’re probably wondering how I came up with this rule. Let’s have a look at that!
Run the following command:
udevadm monitor
Now, undock your laptop and you should see:
KERNEL[8277.917714] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.8/2-1.8:1.0 (usb) KERNEL[8277.918837] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.8 (usb) UDEV [8277.950128] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.8/2-1.8:1.0 (usb) KERNEL[8278.281632] change /devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:08/PNP0C09:00/ACPI0003:00/power_supply/AC (power_supply) UDEV [8278.283788] change /devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:08/PNP0C09:00/ACPI0003:00/power_supply/AC (power_supply) KERNEL[8278.484895] change /devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:08/PNP0C09:00/PNP0C0A:00/power_supply/BAT0 (power_supply) UDEV [8278.486339] change /devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:08/PNP0C09:00/PNP0C0A:00/power_supply/BAT0 (power_supply) UDEV [8282.027590] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.8 (usb)
Now, dock your laptop and you should see:
KERNEL[8291.748427] add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.8 (usb) KERNEL[8291.748496] add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.8/2-1.8:1.0 (usb) KERNEL[8292.945982] change /devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:08/PNP0C09:00/ACPI0003:00/power_supply/AC (power_supply) UDEV [8292.947872] change /devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:08/PNP0C09:00/ACPI0003:00/power_supply/AC (power_supply) KERNEL[8293.047476] change /devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:08/PNP0C09:00/PNP0C0A:00/power_supply/BAT0 (power_supply) UDEV [8293.047953] change /devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:08/PNP0C09:00/PNP0C0A:00/power_supply/BAT0 (power_supply) UDEV [8296.184518] add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.8 (usb) UDEV [8296.185398] add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.8/2-1.8:1.0 (usb)
They may be a little out of order depending on when udev gets the signal. So, we see where the usb device path is… so, let’s check that out: (Control+C to quit the last command, if you weren’t aware)
[root@localhost ~]# udevadm info /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.8/2-1.8:1.0 P: /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.8/2-1.8:1.0 E: DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.8/2-1.8:1.0 E: DEVTYPE=usb_interface E: DRIVER=hub E: ID_MODEL_FROM_DATABASE=ThinkPad Mini Dock Plus Series 3 E: ID_USB_CLASS_FROM_DATABASE=Hub E: ID_USB_PROTOCOL_FROM_DATABASE=TT per port E: ID_VENDOR_FROM_DATABASE=Lenovo E: INTERFACE=9/0/2 E: MODALIAS=usb:v17EFp100Ad0000dc09dsc00dp02ic09isc00ip02in00 E: PRODUCT=17ef/100a/0 E: SUBSYSTEM=usb E: TYPE=9/0/2 E: USEC_INITIALIZED=91748338
Here we see the SUBSYSTEM and DEVPATH values we used in the udev rule! Keep in mind, not all of these attributes will work as expected. You can also add ‘ACTION=add’ or ‘ACTION=remove’ to the udev rule we created earlier if you want to have one rule for docking and one rule for undocking.
And that’s all there is to it! Hopefully this worked for you. If it didn’t, feel free to comment and I’ll do my best to give you a little guidance.
Sources:
http://rpmfusion.org/Configuration
http://www.thinkwiki.org/wiki/Docking_Solutions
https://wiki.archlinux.org/index.php/GDM
https://wiki.archlinux.org/index.php/udev
http://www.reactivated.net/writing_udev_rules.html
‹ Resolving the infamous rdesktop error: failed to open keymap en-us AnyConnect VPN on a Chromebook ›