Friday 8 June 2012

vgaswitcheroo conquered!

For quite a while switchable graphics on my Lenovo T400 laptop has been the only thing that wasn't working properly (or at all) in Linux. Inability to keep the both cards enabled for when I wanted to use one of them in Windows led to me using the weak integrated Intel card for the sake of power consumption, as it was disastrous to leave the discrete ATI Radeon 3450 on for lengthy periods of time - system power consumption jumped up to 25-30 Watts.

A while ago David Airlie has come up with a solution - vga_switcheroo which enabled the support for switchable graphics in Linux. While it was soon merged in the kernel, the technology remained rather unstable. The biggest problem was that once a discrete card was disabled with vga_switcheroo and the system suspended or hibernated, on wake up the discrete card was in semi-enabled state: the vga_switcheroo showed it as disabled, yet it still consumed about 60% of its normal consumption, bringing the system wattage up to 19-20 Watts. This led me to use the integrated card for pretty much everything, as enabling the discrete one in BIOS involved a lengthy process with a reboot, multiple passwords, etc.

Just yesterday I thought I would have a look at the progress with this issue. It appears that a solution to the suspend problem has been found, though it is far from elegant: it involves enabling both cards before suspending the system and disabling one of them back on wake up. After a day of testing I can say that it seems to be working without any quirks.

So, what needs to be done:

  1. Make sure that the kernel used supports vga_switcheroo. Grep the kernel config for CONFIG_VGA_SWITCHEROO=y.
  2. Enable switchable graphics in BIOS and check that vga_switcheroo is seeing both of the cards, run cat /sys/kernel/debug/vgaswitcheroo/switch.
  3. Check that the drivers for both cards are installed (i.e. xserver-xorg-video-intel and xserver-xorg-video-radeon)
  4. Do the following to select an active graphics card at boot time (according to this article). It should also be noted that quite the same effect can be reached by simply putting echo "OFF" | tee /sys/kernel/debug/vgaswitcheroo/switch into /etc/rc.local.
    1. Create the file /etc/initramfs-tools/scripts/local-top/hybrid_boot_options with the following content:
      #
      # Standard initramfs preamble
      #
      prereqs()
      {
      :
      }
      
      case $1 in
      prereqs)
              prereqs
              exit 0
              ;;
      esac
      
      # source for log_*_msg() functions, see LP: #272301
      #. /scripts/functions
      
      #
      # Helper functions
      #
      message()
      {
              if [ -x /bin/plymouth ] && plymouth --ping; then
                      plymouth message --text="$@"
              elif [ -p /dev/.initramfs/usplash_outfifo ] && [ -x /sbin/usplash_write ]; then
                      usplash_write "TEXT-URGENT $@"
              else
                      echo "$@" >&2
              fi
              return 0
      }
      
      run_switcheroo()
      {
              local hybridopts
              hybridopts="$1"
      
              if [ -z "$hybridopts" ]; then
                      return 1
              fi
      
              local IFS=" ,"
              for x in $hybridopts; do
                      message "Switching hybrid graphics to $x"
                      echo $x | tee /sys/kernel/debug/vgaswitcheroo/switch
              done
              return 0
      }
      
      #
      # Begin real processing
      #
      
      # Do we have any kernel boot arguments?
      for opt in $(cat /proc/cmdline); do
              case $opt in
              hybridopts=*)
                      run_switcheroo "${opt#hybridopts=}"
                      ;;
              esac
      done
      
      exit 0
      
    2. Make the file executable
    3. Run sudo update-initramfs -c -k all to create an updated init image
    4. Add hybridopts=OFF to kernel boot options in GRUB config. To make it permanent when using GRUB2, add it to /etc/default/grub and run sudo update-grub.
  5. To make sure that the discrete card remains off after suspend (according to this article by Stefan Huber which also contains further useful advice and scripts) create the following file: /etc/pm/sleep.d/91vga_switch, make it executable and add write the following:
    #!/bin/sh
    case "$1" in
            hibernate|suspend)
                    echo "ON" > /sys/kernel/debug/vgaswitcheroo/switch
                    ;;
            thaw|resume)
                    echo "OFF" > /sys/kernel/debug/vgaswitcheroo/switch
                    ;;                                                                      
            *) exit $NA                                                                     
                    ;;
    esac
    

That's it!
A rather simple but effective solution. With these settings the system will start with the discrete card disabled (which however can be changed when needed by altering kernel boot options in GRUB) and will maintain this state after suspend or hibernation.
Furthermore, it is possible to switch to a discrete graphics card when necessary without rebooting the system. To do that run echo DDIS | sudo tee /sys/kernel/debug/vgaswitcheroo/switch and restart the X session. The next X session should be using the discrete card with the integrated one disabled.


UPD: It turns out that the init config mentioned in step 4 is not in fact working, at least in Kubuntu 12.04. It is thus easier to achieve the same effect by using the rc.local file as mentioned above.

UPD2: As it turns out, vga_switcheroo is compatible only with the free radeon driver, not the fglrx one. As the performance of the free driver in 3D is quite poor, it hardly makes any sense to switch to the discrete card to play games, etc. - the performance boost in 3D will most likely be negligible when compared to the integrated Intel card. Thus, the best approach would be to use the switchable graphics with the discrete card always disabled, yet use it when booting in Windows.

2 comments: