This post is the third of a series of posts on tweaking Ubuntu 12.10 to exploit Optimus technology on my Lenovo W530 to the extent I need. Make sure you are familiar with the context, especially the objectives and constraints as described in Part 1 and Part 2.
My previous post on the topic describes how to control the power state of the DIS with vgaswitheroo that is part of the stock Ubuntu 12.10 kernel. It explains key terms related to X also shows alternative ways to use both the DIS and IGD within the same X server.
As the "single X server with 2 screens" approach is not an option until the related bugfix is available in the official repositories, I investigated how a second X server could be used to reach my goals. As a first step I analysed existing solutions in the area. Starting a second X server is core concept of Bumblebee, that enables rendering on the DIS, and then uses VisualGL to copy the content of individual windows back to the screen handled by the primary X server that uses the IGD only. Unfortunately, this project does not support external monitors in the case when ports are wired to DIS only. Also, it currently does not seem to be mature and stable enough for my production thinkpad. Nevertheless, it gave me a starting point...
Two X servers with one screen each
Starting a separate X instance to handle the DIS enables better isolation/sandboxing but also introduces additional issues.
- First of all, the primary X instance has to be configured in a way that it will not grab any resources of the DIS, else the secondary instance fails to starts up with the message "No screens found".
- Similarly, the second instance has to be configured explicitly to only use the DIS and related monitors.
- I used configuration to override the actual connection status of the external VGA port and always enable VGA internally. Without an enabled monitor X would not start up.
- The desktop cannot be extended to a monitor of the second X instance.
- One could use a separate window manager on the second X instance - twm is very lightweight one. I stayed with a naked X for reasons described below.
- With two X instances, I get a cursor on both displays. Without a pointing device, X would not start either. There are solution to use a mock input device, but anyway, having a two cursors that move in tandem on the two monitors is not critical. With my current configuration, the touchpad only controls the cursor on my primary X, while the trackpoint controls both.
- Finally, the second X server will run as root, essentially with access control disabled so mortals can open windows on the second X as well.
$ cd ~
$ mkdir optimus && cd optimus
$ cat >>xorg.conf.nouveau<<EOF
Section "Modes"
Identifier "FallbackModes" # Mode to use if External-VGA is diconnected
Modeline "1024x768" 65.00 1024 1048 1184 1344 768 771 777 806 -hsync -vsync
EndSection
Section "ServerLayout"
Identifier "Layout0"
Screen "Screen0"
Option "AutoAddDevices" "false"
Option "AutoEnableDevices" "false"
Option "AutoAddGPU" "false"
EndSection
Section "Monitor"
Identifier "External-VGA"
UseModes "FallbackModes"
Option "Enable" "true" # always enabled
Option "PreferredMode" "1024x768"
EndSection
Section "Monitor"
Identifier "LCD"
Option "Enable" "false" # always disabled
EndSection
Section "Device"
Identifier "DIS"
Driver "nouveau"
BusID "PCI:1:0:0"
Option "HWCursor" "true"
# The numbers in output names change based on whether IGD or DIS is
# initialized first by the kernel. This tweak takes care of both cases.
Option "Monitor-VGA-1" "External-VGA"
Option "Monitor-VGA-2" "External-VGA"
Option "Monitor-LVDS-1" "LCD"
Option "Monitor-LVDS-2" "LCD"
EndSection
Section "Screen"
Identifier "Screen0"
Device "DIS"
Monitor "External-VGA"
DefaultDepth 24
SubSection "Display"
Depth 24
EndSubSection
EndSection
EOF
$ cat >>xorg.conf.intel<<EOF
Section "ServerLayout"
Identifier "Layout0"
Screen "Screen0"
Option "AutoAddDevices" "true"
Option "AutoEnableDevices" "true"
Option "AutoAddGPU" "false"
EndSection
Section "Device"
Identifier "IGD"
Driver "intel"
BusID "PCI:0:2:0"
EndSection
Section "Screen"
Identifier "Screen0"
Device "IGD"
DefaultDepth 24
SubSection "Display"
Depth 24
EndSubSection
EndSection
EOF
$ sudo cp xorg.conf.intel /etc/X11/
$ sudo rm /etc/X11/xorg.conf # this is the link to the 2 screen config
$ sudo ln -s /etc/X11/xorg.conf.intel /etc/X11/xorg.conf
$ sudo lightdm restart
If there is no explicit default xorg configuration, then the X server will hold both /dev/dri/card[01]
in which case the second X instance could not start up.
Starting, disabling and restoring external VGA output
I used the following small script to ensure DIS is powered on, spawn the X server, decorate it with a random background. After pressing Enter, X is terminated and DIS powered off.
#!/bin/bash
msg() {
echo "******* $1"
}
msg "Ensuring DIS is powered on."
echo ON | sudo tee /sys/kernel/debug/vgaswitcheroo/switch
msg "Launching X server on display :1."
sudo /usr/bin/X -ac -audit 0 -config /home/tibi/optimus/xorg.conf.nouveau -sharevts -verbose 1 -logverbose 9 -logfile /tmp/Xorg.1.log -nolisten tcp -noreset :1 &
PID=$!
sleep 2
msg "PID is $PID, log goes to /tmp/Xorg.1.log."
# bonus: get a random background
BACKGROUND=$(find /home/tibi/Pictures -maxdepth 1 -name '*.jpg' | sort --random-sort | head -1)
msg "Setting background: $BACKGROUND"
gm display -window root -display :1.0 $BACKGROUND
msg "DONE."
msg "Press Enter to terminate and clean up."
read
msg "Terminating X server..."
sudo kill $PID
sleep 2
echo OFF | sudo tee /sys/kernel/debug/vgaswitcheroo/switch
msg "Discrete graphics device powered off."
I can safely disable and restore the external display without stopping the X server with the following commands:
### DISABLE
# numbers change across reboot, one of the two will work, other will print an error.
xrandr -d :1 --output VGA-1 --off
xrandr -d :1 --output VGA-2 --off
echo OFF | sudo tee /sys/kernel/debug/vgaswitcheroo/switch
### RESTORE
echo ON | sudo tee /sys/kernel/debug/vgaswitcheroo/switch
# numbers change across reboot, one of the two will work, other will print an error.
xrandr -d :1 --output VGA-1 --auto
xrandr -d :1 --output VGA-2 --auto
Usplash and plymouth issues
As it has been stated earlier, the issue around missing usplash/plymouth turned out to be connected to the random order in which the kernel initialized the graphics devices.
$ lspci | grep VGA
00:02.0 VGA compatible controller: Intel Corporation 3rd Gen Core processor Graphics Controller (rev 09)
01:00.0 VGA compatible controller: NVIDIA Corporation GK107 [Quadro K1000M] (rev a1)
$ # framebuffers - the order (0 and 1) changes randomly across reboots
$ cat /proc/fb
0 inteldrmfb
1 nouveaufb
Usplash is hardcoded to render to /dev/fb0
which is fine if the IGD is initialized first by the kernel, but not good if /dev/fb0
is associated with the DIS framebuffer. Kernel boot fbcon=map:1
is of no help in solving this issue. One workaround to mediate this problem is blacklisting nouveau, and loading it later, after usplash is started on the IGD framebuffer.
$ cat <<EOF | sudo tee /etc/modprobe.d/blacklist-nouveau.conf
# blacklist nouveau to force IGD framebuffer to take precedence
blacklist nouveau
EOF
$ sudo update-initramfs -c -k all
### and later during the boot process...
modprobe nouveau # we need modprobe as it was blacklisted during boot
udevadm settle # wait until all udev events are handled
echo OFF > /sys/kernel/debug/vgaswitcheroo/switch
This approach is rather a quick and dirty workaround. Technically, it would be enough - and much more elegant - to merely ensure the proper order of module loading by tinkering around with initram scripts; this is subject to further investigation.
About Part 4
Part 4 shows how this second naked X server can be used to achieve the objectives outlined in the previous post.