Pages

Tuesday, April 15, 2014

Getting multi-head to work on Optimus laptops

Howdy. In recent times, my employer saw fit to provide me and my teammates with a Thinkpad W520. I have a W530 at home so there's not much difference between the two and I knew the beauties that we would be getting.

Also in recent times, thanks to an event I was part of, I found out that the VGA output connector bundled in these laptops is hard-wired to the NVIDIA card. Effectively, booting in integrated mode (thanks to Lenovo's BIOSes which allow you to choose between Intel integrated - NVIDIA discrete - Optimus mode) would render the output connectors completely useless. Initially I did not give this much thought (I have 4 monitors on my desktop PC at home, and whenever I use the W530 it is from the coffee table, while I watch a movie or a series' episode).

After receiving my W520, I quickly switched to NVIDIA mode from the BIOS, corrected the system (KMS off, nvidia-drivers merged) booted it up and tried to use the three displays (LVDS, VGA and DisplayPort). Sadly, the card in this model (1000M) does not seem to be able to handle more than two simultaneous screens (LVDS + VGA OR DP). I read that Kepler cards (like the K1000M the W530 has) should be able to run three displays, but this was not the case. The only way out for me was Optimus.

It came to my surprise that now Optimus has a nice level of support on Linux. And I'm not talking about running a specific app on a separate X server running on the NVIDIA card, drawing the app contents onto the Intel card's framebuffer. I mean real support, being able to use the external display connectors while running on the Intel card.

Here follow some guidelines as to how I got this working.

Requirements


You will need fairly recent versions of involved packages:

  • x11-drivers/nvidia-drivers-337.12 (should also work on 334.21)
  • x11-apps/xrandr-1.4.1 (needed to get the randr extensions for VIRTUAL displays)
  • x11-base/xorg-server-1.15 (should also work on 1.14)
  • x11-drivers/xf86-video-intel-9999 (sadly, even though the latest bundles should be apt, they do not include[1] needed[2] bugfixes[3]).


Caveats


On the NVIDIA side, there's a recent issue (with driver versions >304.xx) where the EDID of the connected display is not read properly, and it must be specified directly on the xorg.conf file, so as to be read later by xrandr. For reference, here's my current /etc/bumblebee/xorg.conf.nvidia, prepared for two external displays (one on VGA-0, one on DP-0):

Section "ServerLayout"
    Identifier  "Layout0"
    Option      "AutoAddDevices" "false"
    Option      "AutoAddGPU" "false"
EndSection

Section "Device"
    Identifier  "DiscreteNvidia"
    Driver      "nvidia"
    VendorName  "NVIDIA Corporation"

#   If the X server does not automatically detect your VGA device,
#   you can manually set it here.
#   To get the BusID prop, run `lspci | egrep 'VGA|3D'` and input the data
#   as you see in the commented example.
#   This Setting may be needed in some platforms with more than one
#   nvidia card, which may confuse the proprietary driver (e.g.,
#   trying to take ownership of the wrong device). Also needed on Ubuntu 13.04.
    BusID "PCI:01:00:0"

#   Setting ProbeAllGpus to false prevents the new proprietary driver
#   instance spawned to try to control the integrated graphics card,
#   which is already being managed outside bumblebee.
#   This option doesn't hurt and it is required on platforms running
#   more than one nvidia graphics card with the proprietary driver.
#   (E.g. Macbook Pro pre-2010 with nVidia 9400M + 9600M GT).
#   If this option is not set, the new Xorg may blacken the screen and
#   render it unusable (unless you have some way to run killall Xorg).
    Option "ProbeAllGpus" "false"

    Option "NoLogo" "true"
    Option "UseEDID" "true"
    Option "Monitor-DP" "Monitor0"
    Option "Monitor-VGA" "Monitor1
    #Option "UseDisplayDevice" "none"
    #Option "AllowSHMPixmaps" "true" <-- No matter what you read on the Internet, do NOT enable this. It will cause black output on the external monitors.
EndSection

Section "Monitor"
    Identifier "Monitor1"
    #Option "ModeDebug" "TRUE"  #Only needed to debug and to get the DFP-X number, is written to /var/log/Xorg.8.log
    Option "ConnectedMonitor" "VGA-0"
    HorizSync   35-81
    VertRefresh  35-76
        Option "ModeValidation" "AllowNonEdidModes"
    Option "ModeValidation" "NoDFPNativeResolutionCheck"
    Modeline "1440x900_60.00"  106.50  1440 1528 1672 1904  900 903 909 934 -hsync +vsync
EndSection


Section "Monitor"
    Identifier "Monitor0"
    #Option "ModeDebug" "TRUE"  #Only needed to debug and to get the DFP-X number, is written to /var/log/Xorg.8.log
    Option "ConnectedMonitor" "DP-0"
    HorizSync   35-81
    VertRefresh  35-76
        Option "ModeValidation" "AllowNonEdidModes"
    Option "ModeValidation" "NoDFPNativeResolutionCheck"
    Modeline "1280x1024_50.00"   88.50  1280 1352 1480 1680  1024 1027 1034 1057 -hsync +vsync 
EndSection

On the Intel side, recent builds (2.99.xxx) of the driver bundle the necessary intel-virtual-output binary to configure the virtual displays automagically. Bear in mind that this binary does not provide any debug output at all by default (even though you might have the debug USE activated). In order to have debug output in case you need to troubleshoot, you have to first edit the tools/virtual.c file:


diff --git a/tools/virtual.c b/tools/virtual.c
index 5883950..c5316a2 100644
--- a/tools/virtual.c
+++ b/tools/virtual.c
@@ -65,7 +65,7 @@
 #include <fcntl.h>
 #include <assert.h>

-#if 0
+#if 1
 #define DBG(x) printf x
 #define EXTRA_DBG 1
 #else


Also, beware some configuration needed on Bumblebee's side (/etc/bumblebee/bumblebee.conf):


[bumblebeed]
VirtualDisplay=:8
KeepUnusedXServer=true
ServerGroup=bumblebee
TurnCardOffAtExit=false
NoEcoModeOverride=false
Driver=nvidia
XorgConfDir=/etc/bumblebee/xorg.conf.d

[optirun]
Bridge=auto
VGLTransport=proxy
PrimusLibraryPath=/usr/lib/primus:/usr/lib32/primus
AllowFallbackToIGC=false

[driver-nvidia]
KernelDriver=nvidia
PMMethod=none
LibraryPath=/usr/lib64/opengl/nvidia/lib:/usr/lib32/opengl/nvidia/lib:/usr/lib/opengl/nvidia/lib
XorgModulePath=/usr/lib64/opengl/nvidia/lib,/usr/lib64/opengl/nvidia/extensions,/usr/lib64/xorg/modules/drivers,/usr/lib64/xorg/modules
XorgConfFile=/etc/bumblebee/xorg.conf.nvidia

[driver-nouveau]
KernelDriver=nouveau
PMMethod=none
XorgConfFile=/etc/bumblebee/xorg.conf.nouveau

Steps


Make sure you are booting into Optimus mode, using the Intel card to drive the laptop's main display (LVDS1).
Log on to your desktop environment of choice, open up a shell.
/etc/init.d/bumblebee start
modprobe bbswitch (also, encure /proc/acpi/bbswitch reports the NVIDIA card as being ON)
optirun true (this is to ensure the card is turned on and does not get turned off. I can omit this and it still works).
intel-virtual-output (I have to run this as root, did not spend too much time trying to see how could I run it from my user without it blowing up on my face).
By now (it takes a few seconds) you should be getting your desktop environment popup, notifying you of the new display(s) that has/have been attached.
You can now fire up the xrandr frontend of your choosing, and configure your VIRTUALX (where X is a number) displays to your delight.
Even if you do not plan to use a triple-head setup

[1]: https://bugs.freedesktop.org/show_bug.cgi?id=71345
[2]: https://bugs.freedesktop.org/show_bug.cgi?id=73816
[3]: https://bugs.freedesktop.org/show_bug.cgi?id=76721