Skip to content

awilliam/asus-switcheroo

Repository files navigation

These drivers are for Asus laptops with integrated Intel
graphics and discrete Nvidia graphics.  To see if this
might work on your laptop, disassemble your DSDT and
look for a _DSM method similar to this:

    Method (_DSM, 4, NotSerialized)
    {
        If (LEqual (Arg0, Buffer (0x10)
                {
                    /* 0000 */    0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D,
                    /* 0008 */    0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4
                }))
        {
        ...

Also check to see that you have both MXMX and MXDS methods:

    Method (MXMX, 1, NotSerialized)
    {
    ...

    Method (MXDS, 1, NotSerialized)
    {
    ...

You'll need two sets of these, one for each gfx device.

These drivers will not work if the system has only the "optimus"
_DSM.  Sorry, I don't have a laptop with that to hack on.

This works pretty well on my Asus UL30VT, and we've seen some
reports that it works on other Asus laptops as well.

To get started:

Build the asus-switcheroo kernel modules:

# make

To install, pick your distro from

# sudo make install-fedora
# sudo make install-ubuntu
# sudo make install-arch

If these don't do what you need, reproduce these steps on your
distribution:

 - copy the kernel modules in place (*.ko)
 - update modprobe.d to load asus-switcheroo before nouveau
 - update modprobe.d to load i915-jprobe before i915
 - add asus-switcher and i915-jprobe to the initramfs conf files
 - install the suspend/resume script
 - run depmod
 - build a new initramfs

Please post instructions for your distribution if you come up
with working steps.  Each provided install target also has
a corresponding uninstall target.  Note you will need to reboot
after install to make use of the modules as they're loaded from
the initramfs.

Using it:

For simply disabling the discrete graphics to save power, the
only other thing you need to do is:

# echo OFF > /sys/kernel/debug/vgaswitcheroo/switch

If debugfs isn't automatically mounted for you, add this to your
/etc/fstab:

debugfs		/sys/kernel/debug	debugfs	defaults	0 0

See the suspend/resume script for a description of an issue
and workaround for suspend/resume and powering off the other
device.

If you want to run with nouveau graphics, echo DIS to the
above switch file.  Note that this will not work if X is
already running.  You can run DDIS to the switch file for
a delayed switch when X restarts.

The asus-switcheroo module now includes a workaround for older
kernels where nouveau does not reprobe devices when we
switch to it.  This fixes the black screen issue when using
the discrete gfx with the nouveau driver we had previously.

The i915-jprobe module also comes into play when the Intel
gfx is turned off.  This module dynamically fixes a bug in
the Intel driver and prevents the Intel lid notifier from
being called when the Intel gfx is turned off.

It is also possible, though very, very alpha and extremely
not recommended for average users to use the asus-switcheroo
module as a dummy switcheroo client that allows you to run
the proprietary nvidia module.  To do this, blacklist
nouveau and rebuild your initramfs to get nouveau out of it.
Use the dummy-client=1 option for asus-switcheroo, if loaded
from initramfs, use asus-switcher.dummy-client=1.  At boot,
switch to DIS, modprobe nvidia, then start X using the nvidia
proprietary driver.  Note that the screen LVDS will go black
as soon as you switch via DIS and will not come back until X
starts.  There is no framebuffer driver in this mode, so you
will only have X.  This switch is one way, you'll need to
reboot to get Intel graphics back.

Theory of operation

asus-switcheroo:

The vga switcher subsystem is designed for two clients and
one handler.  The clients are the actual graphics drivers,
for this laptop, the Intel i915 and Nvidia nouveau.  The
client has device specific callbacks to set the device state
and let us know whether a switch is possible (ie. making
sure the device isn't already opened by X).  Newer kernels
also have a reprobe trigger to tell the graphics driver to
go discover what outputs it's connected to.  All of these
are a mix of driver/device specific with a bit of generic
PCI callbacks.

The handler primarly manages the multiplexing for the
displays between the devices (ex. which device is connected
to LVDS) and low level power control.  It appears that all
of the handlers we have today are ACPI based.

asus-switcher is an ACPI based handler (ignoring the
dummy client for the moment).  As noted above, the vga
switcheroo subsystem only uses one handler, first come
first served, which is why we need to load the asus-switcheroo
module before nouveau.

The handler in nouveau_acpi is nearly correct and provides
the basis for asus-switcheroo.  The difference are:

1) The last parameter to the _DSM call in nouveau_acpi is
   wrong and doesn't follow the ACPI spec.  The Asus DSDT
   does operations on this parameter that are undefined for
   the parameter type nouveau_acpi uses.

2) There's no multiplexer control.

The only difficulty to fixing the first problem is making
sure we don't break whatever the current code works on.  In
asus-switcheroo, we don't care, since it's targettd at one
vendor.

The second problem is more difficult.  I had originally
been trying to hack on Dave Airlie's drm tree, following
his example of using the WMI MXM interface, hoping there
was mux control burried somewhere in there.  Unfortunately
the call he's relying on to switch the mux on his T410s,
"MXDS", doesn't exist in the Asus WMI interface.  However
we do have MXDS methods we can call directly, one under
each graphics device in ACPI, which matches what the
T410s WMI call does.  I proceeded to poke these from
userspace with acpi_call, but didn't have much luck.  By
trial and error, I was able to find that calling the MXMX
method on the device before calling the MXDS method
produced the right switch.

So, the mux formula for this ACPI implementation is to
simply call MXMX followed by MXDS, and for this DSDT,
the parameters don't seem to matter much.  This is
where it would be nice to see and actual MXM spec to
get the right parameters and better understand the
relationship between these two calls.

The last piece is to hack back in the reprobe operation
that Dave has added to new kernels.  nouveau already
has the resulting function call that the reprobe in newer
kernels use, so it's just a matter of getting to it.

As for the dummy client facility, to enable vga switcheroo
we need two clients and one handler.  We have the i915
client and asus-switcheroo as a hander.  Much of the client
control is simply bringing the device up to PCI D0 power
state when we turn it on, and putting it in D3hot to turn
it off.  So, that's effectively all the dummy client does.
This much is sufficient that we use the handler to power
on the discrete graphics and switch the display multiplexer
to the right output, then use the dummy client to wake
up the device and restore it's state.  From there the
proprietary nvidia driver can make use of it.

i915-jprobe:

This module is mostly just a fun little jprobe hack.  When
we switch to discrete graphics and close the laptop lid,
we get a kernel oops because the i915 driver is still
getting notified of laptop events and tries to poke the
hardware.

We have to implement this hack in two stages to get all
the right entry points.  First we insert a jprobe for
the acpi_lid_notifier_register() function.  This means
we get called any time a driver calls that function and
we get passed the same parameters that the real function
gets.  When that jprobe gets triggered, we can look to
see if some of the i915 symbols are resolvable, meaning
the i915 driver is now loaded, then we can compare
whether the notifier block callback is for the i915
lid notifier function.  At that point, we have a pointer
to the notifier block and can change the callback
function pointer to call our dummy notifier function
instead of the i915 version.  Registering one more jprobe
on i915_switcheroo_set_state() allows us to set and reset
that function callback when we switch devices.  We have
to use a workqueue to register that jprobe because parts
of the registration call functions that can sleep and
the jprobe callback where we enable this is called with
interrupts disabled.

Overall, this is ~130 lines of code that do the equivalent
of the 3 line proposed upstream patch:

http://lists.freedesktop.org/archives/dri-devel/2011-April/010488.html

But, it fixes it in an existing kernel, which is
pretty cool.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published