← Back to team overview

hybrid-graphics-linux team mailing list archive

Macbook Pro graphics switching

 

Hello everybody,

I'm developing a vga_switcheroo driver for the Apple Macbook Pro models
with switchable graphics and have a few questions about how to integrate
it with the current linux hybrid graphics infrastructure.


Apple implemented the switching using a "chip" dubbed the
"gmux" (probably short for graphics multiplexer or something similar).
The gmux can power down the external graphics card, switch the DDC,
internal and external display connections (individually) between both
cards and set the backlight brightness. Unlike most newer laptops with
hybrid graphics, Apple's Macbook Pros use a physical mux.

Luckily the gmux has remained mostly the same between all models with
switchable graphics, so it should be possible to develop one driver that
supports all of them. 


This driver has actually been lying around in unfinished state for some
time now, until Seth Forshee from Canonical recently rewrote it into a
clean, backlight-only driver that will hopefully find it's way into
mainline soon.

My goal is to use Seth's work as a base and add the other features back
again and make it work well. A first version is already working and up
at https://github.com/ah-/gmux

This driver supports all vga_switcheroo operations (discrete power
on/off, display switching) and they all work on my MBP 6,2 using the 
intel and nouveau drivers. 

But unfortunately, it doesn't work with bumbleebee and/or the proprietary 
nvidia driver yet.


Because I'm using CUDA in other projects, getting the proprietary NVIDIA 
driver running well is very interesting to me. The setup bumblebee plus
intel + nvidia_current without power management works well on my laptop
if I switch to the intel card in grub. (It's just three outbs). But that
breaks suspend and uses 10W more power than necessary.

Does/should bumblebee work with a vga_switcheroo driver just for ON/OFF?

One thing I stumbled upon is a patch for the proprietary NVIDIA 
driver originally written by "Kayo <kayo@xxxxxxxxxxxx>" which adds 
some kind of vga_switcheroo support to it. I modified it to just register
with switcheroo (patch is attached), and that part works well. Turning
off the card works, but turning it on afterwards doesn't. Any Ideas?


Ultimately I'd like to get two scenarios/workflows to work well:

1. Real switching, by disabling one card completely and only using 
the other one, requiring a logout/login. 
Preferably using the NVIDIA driver.

2. Bumblebee switching with working power management when the NVIDIA
GPU is not in usw.


I'd appreciate any comments and thoughts on how to implement this and 
how to integrate it with the current hybrid graphics environment.

Cheers,
Andreas
--- /usr/src/nvidia-current-295.20/nv.c	2012-02-28 11:51:23.420161736 +0100
+++ nvidia-current-295.20/nv.c	2012-02-28 12:04:04.120192966 +0100
@@ -15,6 +15,7 @@
 #include "os-agp.h"
 #include "nv-reg.h"
 #include "rmil.h"
+#include <linux/vga_switcheroo.h>
 
 #if defined(MODULE_LICENSE)
 MODULE_LICENSE("NVIDIA");
@@ -307,6 +308,40 @@
 static int    nv_kern_smu_resume(void);
 #endif
 
+#define NV_SWITCHEROO_LOG "VGA switcheroo NV client: "
+
+static void nv_switcheroo_set_state(struct pci_dev *pdev,
+        enum vga_switcheroo_state state)
+{
+    pm_message_t pmm = { .event = PM_EVENT_SUSPEND };
+    if (state == VGA_SWITCHEROO_ON) {
+        printk(KERN_INFO NV_SWITCHEROO_LOG "Switched nvidia to: On\n");
+        nv_kern_resume(pdev);
+    } else {
+        printk(KERN_INFO NV_SWITCHEROO_LOG "Switched nvidia to: Off\n");
+        nv_kern_suspend(pdev, pmm);
+    }
+}
+
+static void nv_switcheroo_reprobe(struct pci_dev *dev)
+{
+    printk(KERN_INFO NV_SWITCHEROO_LOG "Reprobe.\n");
+}
+
+static bool nv_switcheroo_can_switch(struct pci_dev *dev)
+{
+    nv_linux_state_t *lnv = pci_get_drvdata(dev);
+    bool can_switch;
+
+    //spin_lock(&lnv->ldata_lock);
+    can_switch = (NV_ATOMIC_READ(lnv->usage_count) == 0);
+    //spin_unlock(&lnv->ldata_lock);
+
+    printk(KERN_INFO NV_SWITCHEROO_LOG "Can switch? %s\n", can_switch ? "Yes" : "No");
+
+    return can_switch;
+}
+
 /***
  *** see nv.h for functions exported to other parts of resman
  ***/
@@ -1100,6 +1135,9 @@
 #else
         nvl->nv_state.flags &= ~NV_FLAG_S4_AVAILABLE;
 #endif
+
+        printk(KERN_INFO NV_SWITCHEROO_LOG "Register client.\n");
+        vga_switcheroo_register_client(nvl->dev, nv_switcheroo_set_state, nv_switcheroo_reprobe, nv_switcheroo_can_switch);
     }
 
     return 0;
@@ -1197,6 +1235,9 @@
 
         if ((dev = nvl->dev) != NULL)
         {
+            printk(KERN_INFO NV_SWITCHEROO_LOG "Unregister client.\n");
+            vga_switcheroo_unregister_client(dev);
+            
             if (NV_IS_GVI_DEVICE(nv))
             {
                 NV_TASKQUEUE_FLUSH();

Follow ups