Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b0fbfb0c authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge branch 'for_linus' of git://cavan.codon.org.uk/platform-drivers-x86

Pull x86 platform driver update from Matthew Garrett:
 "Some small updates for a few drivers, and some hardware enablement for
  new Ideapads and the gmux hardware in the latest Macs.

  This code won't run on older devices and has been well tested on new
  ones, so low risk of regressions."

* 'for_linus' of git://cavan.codon.org.uk/platform-drivers-x86:
  ideapad: add Lenovo IdeaPad Z570 support (part 3)
  ideapad: add Lenovo IdeaPad Z570 support (part 2)
  ideapad: add Lenovo IdeaPad Z570 support (part 1)
  classmate-laptop: always call input_sync() after input_report_switch()
  thinkpad-acpi: recognize latest V-Series using DMI_BIOS_VENDOR
  dell-laptop: Fixed typo in touchpad LED quirk
  vga_switcheroo: Don't require handler init callback
  vga_switcheroo: Remove assumptions about registration/unregistration ordering
  apple-gmux: Add display mux support
  apple-gmux: Fix kconfig dependencies
  asus-wmi: record wlan status while controlled by userapp
  apple_gmux: Fix ACPI video unregister
  apple_gmux: Add support for newer hardware
  gmux: Add generic write32 function
parents 807b5169 0c7bbeb9
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -5,4 +5,15 @@ Contact: "Ike Panhc <ike.pan@canonical.com>"
Description:
		Control the power of camera module. 1 means on, 0 means off.

What:		/sys/devices/platform/ideapad/fan_mode
Date:		June 2012
KernelVersion:	3.6
Contact:	"Maxim Mikityanskiy <maxtram95@gmail.com>"
Description:
		Change fan mode
		There are four available modes:
			* 0 -> Super Silent Mode
			* 1 -> Standard Mode
			* 2 -> Dust Cleaning
			* 4 -> Efficient Thermal Dissipation Mode
+0 −6
Original line number Diff line number Diff line
@@ -211,11 +211,6 @@ static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id,
	return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state);
}

static int nouveau_dsm_init(void)
{
	return 0;
}

static int nouveau_dsm_get_client_id(struct pci_dev *pdev)
{
	/* easy option one - intel vendor ID means Integrated */
@@ -232,7 +227,6 @@ static int nouveau_dsm_get_client_id(struct pci_dev *pdev)
static struct vga_switcheroo_handler nouveau_dsm_handler = {
	.switchto = nouveau_dsm_switchto,
	.power_state = nouveau_dsm_power_state,
	.init = nouveau_dsm_init,
	.get_client_id = nouveau_dsm_get_client_id,
};

+38 −23
Original line number Diff line number Diff line
@@ -70,27 +70,12 @@ static struct vgasr_priv vgasr_priv = {
	.clients = LIST_HEAD_INIT(vgasr_priv.clients),
};

int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler)
{
	mutex_lock(&vgasr_mutex);
	if (vgasr_priv.handler) {
		mutex_unlock(&vgasr_mutex);
		return -EINVAL;
	}

	vgasr_priv.handler = handler;
	mutex_unlock(&vgasr_mutex);
	return 0;
}
EXPORT_SYMBOL(vga_switcheroo_register_handler);

void vga_switcheroo_unregister_handler(void)
static bool vga_switcheroo_ready(void)
{
	mutex_lock(&vgasr_mutex);
	vgasr_priv.handler = NULL;
	mutex_unlock(&vgasr_mutex);
	/* we're ready if we get two clients + handler */
	return !vgasr_priv.active &&
	       vgasr_priv.registered_clients == 2 && vgasr_priv.handler;
}
EXPORT_SYMBOL(vga_switcheroo_unregister_handler);

static void vga_switcheroo_enable(void)
{
@@ -98,6 +83,7 @@ static void vga_switcheroo_enable(void)
	struct vga_switcheroo_client *client;

	/* call the handler to init */
	if (vgasr_priv.handler->init)
		vgasr_priv.handler->init();

	list_for_each_entry(client, &vgasr_priv.clients, list) {
@@ -113,6 +99,37 @@ static void vga_switcheroo_enable(void)
	vgasr_priv.active = true;
}

int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler)
{
	mutex_lock(&vgasr_mutex);
	if (vgasr_priv.handler) {
		mutex_unlock(&vgasr_mutex);
		return -EINVAL;
	}

	vgasr_priv.handler = handler;
	if (vga_switcheroo_ready()) {
		printk(KERN_INFO "vga_switcheroo: enabled\n");
		vga_switcheroo_enable();
	}
	mutex_unlock(&vgasr_mutex);
	return 0;
}
EXPORT_SYMBOL(vga_switcheroo_register_handler);

void vga_switcheroo_unregister_handler(void)
{
	mutex_lock(&vgasr_mutex);
	vgasr_priv.handler = NULL;
	if (vgasr_priv.active) {
		pr_info("vga_switcheroo: disabled\n");
		vga_switcheroo_debugfs_fini(&vgasr_priv);
		vgasr_priv.active = false;
	}
	mutex_unlock(&vgasr_mutex);
}
EXPORT_SYMBOL(vga_switcheroo_unregister_handler);

static int register_client(struct pci_dev *pdev,
			   const struct vga_switcheroo_client_ops *ops,
			   int id, bool active)
@@ -134,9 +151,7 @@ static int register_client(struct pci_dev *pdev,
	if (client_is_vga(client))
		vgasr_priv.registered_clients++;

	/* if we get two clients + handler */
	if (!vgasr_priv.active &&
	    vgasr_priv.registered_clients == 2 && vgasr_priv.handler) {
	if (vga_switcheroo_ready()) {
		printk(KERN_INFO "vga_switcheroo: enabled\n");
		vga_switcheroo_enable();
	}
+5 −1
Original line number Diff line number Diff line
@@ -289,6 +289,7 @@ config IDEAPAD_LAPTOP
	tristate "Lenovo IdeaPad Laptop Extras"
	depends on ACPI
	depends on RFKILL && INPUT
	depends on SERIO_I8042
	select INPUT_SPARSEKMAP
	help
	  This is a driver for the rfkill switches on Lenovo IdeaPad netbooks.
@@ -758,8 +759,11 @@ config SAMSUNG_Q10

config APPLE_GMUX
	tristate "Apple Gmux Driver"
	depends on ACPI
	depends on PNP
	select BACKLIGHT_CLASS_DEVICE
	depends on BACKLIGHT_CLASS_DEVICE
	depends on BACKLIGHT_APPLE=n || BACKLIGHT_APPLE
	depends on ACPI_VIDEO=n || ACPI_VIDEO
	---help---
	  This driver provides support for the gmux device found on many
	  Apple laptops, which controls the display mux for the hybrid
+402 −24
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@
 *  Gmux driver for Apple laptops
 *
 *  Copyright (C) Canonical Ltd. <seth.forshee@canonical.com>
 *  Copyright (C) 2010-2012 Andreas Heider <andreas@meetr.de>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
@@ -18,16 +19,30 @@
#include <linux/pnp.h>
#include <linux/apple_bl.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/vga_switcheroo.h>
#include <acpi/video.h>
#include <asm/io.h>

struct apple_gmux_data {
	unsigned long iostart;
	unsigned long iolen;
	bool indexed;
	struct mutex index_lock;

	struct backlight_device *bdev;

	/* switcheroo data */
	acpi_handle dhandle;
	int gpe;
	enum vga_switcheroo_client_id resume_client_id;
	enum vga_switcheroo_state power_state;
	struct completion powerchange_done;
};

static struct apple_gmux_data *apple_gmux_data;

/*
 * gmux port offsets. Many of these are not yet used, but may be in the
 * future, and it's useful to have them documented here anyhow.
@@ -45,6 +60,9 @@ struct apple_gmux_data {
#define GMUX_PORT_DISCRETE_POWER	0x50
#define GMUX_PORT_MAX_BRIGHTNESS	0x70
#define GMUX_PORT_BRIGHTNESS		0x74
#define GMUX_PORT_VALUE			0xc2
#define GMUX_PORT_READ			0xd0
#define GMUX_PORT_WRITE			0xd4

#define GMUX_MIN_IO_LEN			(GMUX_PORT_BRIGHTNESS + 4)

@@ -59,22 +77,172 @@ struct apple_gmux_data {
#define GMUX_BRIGHTNESS_MASK		0x00ffffff
#define GMUX_MAX_BRIGHTNESS		GMUX_BRIGHTNESS_MASK

static inline u8 gmux_read8(struct apple_gmux_data *gmux_data, int port)
static u8 gmux_pio_read8(struct apple_gmux_data *gmux_data, int port)
{
	return inb(gmux_data->iostart + port);
}

static inline void gmux_write8(struct apple_gmux_data *gmux_data, int port,
static void gmux_pio_write8(struct apple_gmux_data *gmux_data, int port,
			       u8 val)
{
	outb(val, gmux_data->iostart + port);
}

static inline u32 gmux_read32(struct apple_gmux_data *gmux_data, int port)
static u32 gmux_pio_read32(struct apple_gmux_data *gmux_data, int port)
{
	return inl(gmux_data->iostart + port);
}

static void gmux_pio_write32(struct apple_gmux_data *gmux_data, int port,
			     u32 val)
{
	int i;
	u8 tmpval;

	for (i = 0; i < 4; i++) {
		tmpval = (val >> (i * 8)) & 0xff;
		outb(tmpval, port + i);
	}
}

static int gmux_index_wait_ready(struct apple_gmux_data *gmux_data)
{
	int i = 200;
	u8 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);

	while (i && (gwr & 0x01)) {
		inb(gmux_data->iostart + GMUX_PORT_READ);
		gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
		udelay(100);
		i--;
	}

	return !!i;
}

static int gmux_index_wait_complete(struct apple_gmux_data *gmux_data)
{
	int i = 200;
	u8 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);

	while (i && !(gwr & 0x01)) {
		gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
		udelay(100);
		i--;
	}

	if (gwr & 0x01)
		inb(gmux_data->iostart + GMUX_PORT_READ);

	return !!i;
}

static u8 gmux_index_read8(struct apple_gmux_data *gmux_data, int port)
{
	u8 val;

	mutex_lock(&gmux_data->index_lock);
	outb((port & 0xff), gmux_data->iostart + GMUX_PORT_READ);
	gmux_index_wait_ready(gmux_data);
	val = inb(gmux_data->iostart + GMUX_PORT_VALUE);
	mutex_unlock(&gmux_data->index_lock);

	return val;
}

static void gmux_index_write8(struct apple_gmux_data *gmux_data, int port,
			      u8 val)
{
	mutex_lock(&gmux_data->index_lock);
	outb(val, gmux_data->iostart + GMUX_PORT_VALUE);
	gmux_index_wait_ready(gmux_data);
	outb(port & 0xff, gmux_data->iostart + GMUX_PORT_WRITE);
	gmux_index_wait_complete(gmux_data);
	mutex_unlock(&gmux_data->index_lock);
}

static u32 gmux_index_read32(struct apple_gmux_data *gmux_data, int port)
{
	u32 val;

	mutex_lock(&gmux_data->index_lock);
	outb((port & 0xff), gmux_data->iostart + GMUX_PORT_READ);
	gmux_index_wait_ready(gmux_data);
	val = inl(gmux_data->iostart + GMUX_PORT_VALUE);
	mutex_unlock(&gmux_data->index_lock);

	return val;
}

static void gmux_index_write32(struct apple_gmux_data *gmux_data, int port,
			       u32 val)
{
	int i;
	u8 tmpval;

	mutex_lock(&gmux_data->index_lock);

	for (i = 0; i < 4; i++) {
		tmpval = (val >> (i * 8)) & 0xff;
		outb(tmpval, gmux_data->iostart + GMUX_PORT_VALUE + i);
	}

	gmux_index_wait_ready(gmux_data);
	outb(port & 0xff, gmux_data->iostart + GMUX_PORT_WRITE);
	gmux_index_wait_complete(gmux_data);
	mutex_unlock(&gmux_data->index_lock);
}

static u8 gmux_read8(struct apple_gmux_data *gmux_data, int port)
{
	if (gmux_data->indexed)
		return gmux_index_read8(gmux_data, port);
	else
		return gmux_pio_read8(gmux_data, port);
}

static void gmux_write8(struct apple_gmux_data *gmux_data, int port, u8 val)
{
	if (gmux_data->indexed)
		gmux_index_write8(gmux_data, port, val);
	else
		gmux_pio_write8(gmux_data, port, val);
}

static u32 gmux_read32(struct apple_gmux_data *gmux_data, int port)
{
	if (gmux_data->indexed)
		return gmux_index_read32(gmux_data, port);
	else
		return gmux_pio_read32(gmux_data, port);
}

static void gmux_write32(struct apple_gmux_data *gmux_data, int port,
			     u32 val)
{
	if (gmux_data->indexed)
		gmux_index_write32(gmux_data, port, val);
	else
		gmux_pio_write32(gmux_data, port, val);
}

static bool gmux_is_indexed(struct apple_gmux_data *gmux_data)
{
	u16 val;

	outb(0xaa, gmux_data->iostart + 0xcc);
	outb(0x55, gmux_data->iostart + 0xcd);
	outb(0x00, gmux_data->iostart + 0xce);

	val = inb(gmux_data->iostart + 0xcc) |
		(inb(gmux_data->iostart + 0xcd) << 8);

	if (val == 0x55aa)
		return true;

	return false;
}

static int gmux_get_brightness(struct backlight_device *bd)
{
	struct apple_gmux_data *gmux_data = bl_get_data(bd);
@@ -90,16 +258,7 @@ static int gmux_update_status(struct backlight_device *bd)
	if (bd->props.state & BL_CORE_SUSPENDED)
		return 0;

	/*
	 * Older gmux versions require writing out lower bytes first then
	 * setting the upper byte to 0 to flush the values. Newer versions
	 * accept a single u32 write, but the old method also works, so we
	 * just use the old method for all gmux versions.
	 */
	gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS, brightness);
	gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 1, brightness >> 8);
	gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 2, brightness >> 16);
	gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 3, 0);
	gmux_write32(gmux_data, GMUX_PORT_BRIGHTNESS, brightness);

	return 0;
}
@@ -110,6 +269,146 @@ static const struct backlight_ops gmux_bl_ops = {
	.update_status = gmux_update_status,
};

static int gmux_switchto(enum vga_switcheroo_client_id id)
{
	if (id == VGA_SWITCHEROO_IGD) {
		gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 1);
		gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 2);
		gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 2);
	} else {
		gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 2);
		gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 3);
		gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 3);
	}

	return 0;
}

static int gmux_set_discrete_state(struct apple_gmux_data *gmux_data,
				   enum vga_switcheroo_state state)
{
	INIT_COMPLETION(gmux_data->powerchange_done);

	if (state == VGA_SWITCHEROO_ON) {
		gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1);
		gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 3);
		pr_debug("Discrete card powered up\n");
	} else {
		gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1);
		gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 0);
		pr_debug("Discrete card powered down\n");
	}

	gmux_data->power_state = state;

	if (gmux_data->gpe >= 0 &&
	    !wait_for_completion_interruptible_timeout(&gmux_data->powerchange_done,
						       msecs_to_jiffies(200)))
		pr_warn("Timeout waiting for gmux switch to complete\n");

	return 0;
}

static int gmux_set_power_state(enum vga_switcheroo_client_id id,
				enum vga_switcheroo_state state)
{
	if (id == VGA_SWITCHEROO_IGD)
		return 0;

	return gmux_set_discrete_state(apple_gmux_data, state);
}

static int gmux_get_client_id(struct pci_dev *pdev)
{
	/*
	 * Early Macbook Pros with switchable graphics use nvidia
	 * integrated graphics. Hardcode that the 9400M is integrated.
	 */
	if (pdev->vendor == PCI_VENDOR_ID_INTEL)
		return VGA_SWITCHEROO_IGD;
	else if (pdev->vendor == PCI_VENDOR_ID_NVIDIA &&
		 pdev->device == 0x0863)
		return VGA_SWITCHEROO_IGD;
	else
		return VGA_SWITCHEROO_DIS;
}

static enum vga_switcheroo_client_id
gmux_active_client(struct apple_gmux_data *gmux_data)
{
	if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_DISPLAY) == 2)
		return VGA_SWITCHEROO_IGD;

	return VGA_SWITCHEROO_DIS;
}

static struct vga_switcheroo_handler gmux_handler = {
	.switchto = gmux_switchto,
	.power_state = gmux_set_power_state,
	.get_client_id = gmux_get_client_id,
};

static inline void gmux_disable_interrupts(struct apple_gmux_data *gmux_data)
{
	gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE,
		    GMUX_INTERRUPT_DISABLE);
}

static inline void gmux_enable_interrupts(struct apple_gmux_data *gmux_data)
{
	gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE,
		    GMUX_INTERRUPT_ENABLE);
}

static inline u8 gmux_interrupt_get_status(struct apple_gmux_data *gmux_data)
{
	return gmux_read8(gmux_data, GMUX_PORT_INTERRUPT_STATUS);
}

static void gmux_clear_interrupts(struct apple_gmux_data *gmux_data)
{
	u8 status;

	/* to clear interrupts write back current status */
	status = gmux_interrupt_get_status(gmux_data);
	gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_STATUS, status);
}

static void gmux_notify_handler(acpi_handle device, u32 value, void *context)
{
	u8 status;
	struct pnp_dev *pnp = (struct pnp_dev *)context;
	struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);

	status = gmux_interrupt_get_status(gmux_data);
	gmux_disable_interrupts(gmux_data);
	pr_debug("Notify handler called: status %d\n", status);

	gmux_clear_interrupts(gmux_data);
	gmux_enable_interrupts(gmux_data);

	if (status & GMUX_INTERRUPT_STATUS_POWER)
		complete(&gmux_data->powerchange_done);
}

static int gmux_suspend(struct pnp_dev *pnp, pm_message_t state)
{
	struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
	gmux_data->resume_client_id = gmux_active_client(gmux_data);
	gmux_disable_interrupts(gmux_data);
	return 0;
}

static int gmux_resume(struct pnp_dev *pnp)
{
	struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
	gmux_enable_interrupts(gmux_data);
	gmux_switchto(gmux_data->resume_client_id);
	if (gmux_data->power_state == VGA_SWITCHEROO_OFF)
		gmux_set_discrete_state(gmux_data, gmux_data->power_state);
	return 0;
}

static int __devinit gmux_probe(struct pnp_dev *pnp,
				const struct pnp_device_id *id)
{
@@ -119,6 +418,11 @@ static int __devinit gmux_probe(struct pnp_dev *pnp,
	struct backlight_device *bdev;
	u8 ver_major, ver_minor, ver_release;
	int ret = -ENXIO;
	acpi_status status;
	unsigned long long gpe;

	if (apple_gmux_data)
		return -EBUSY;

	gmux_data = kzalloc(sizeof(*gmux_data), GFP_KERNEL);
	if (!gmux_data)
@@ -147,21 +451,28 @@ static int __devinit gmux_probe(struct pnp_dev *pnp,
	}

	/*
	 * On some machines the gmux is in ACPI even thought the machine
	 * doesn't really have a gmux. Check for invalid version information
	 * to detect this.
	 * Invalid version information may indicate either that the gmux
	 * device isn't present or that it's a new one that uses indexed
	 * io
	 */

	ver_major = gmux_read8(gmux_data, GMUX_PORT_VERSION_MAJOR);
	ver_minor = gmux_read8(gmux_data, GMUX_PORT_VERSION_MINOR);
	ver_release = gmux_read8(gmux_data, GMUX_PORT_VERSION_RELEASE);
	if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) {
		if (gmux_is_indexed(gmux_data)) {
			mutex_init(&gmux_data->index_lock);
			gmux_data->indexed = true;
		} else {
			pr_info("gmux device not present\n");
			ret = -ENODEV;
			goto err_release;
		}

		pr_info("Found indexed gmux\n");
	} else {
		pr_info("Found gmux version %d.%d.%d\n", ver_major, ver_minor,
			ver_release);
	}

	memset(&props, 0, sizeof(props));
	props.type = BACKLIGHT_PLATFORM;
@@ -194,13 +505,67 @@ static int __devinit gmux_probe(struct pnp_dev *pnp,
	 * Disable the other backlight choices.
	 */
	acpi_video_dmi_promote_vendor();
#ifdef CONFIG_ACPI_VIDEO
#if defined (CONFIG_ACPI_VIDEO) || defined (CONFIG_ACPI_VIDEO_MODULE)
	acpi_video_unregister();
#endif
	apple_bl_unregister();

	gmux_data->power_state = VGA_SWITCHEROO_ON;

	gmux_data->dhandle = DEVICE_ACPI_HANDLE(&pnp->dev);
	if (!gmux_data->dhandle) {
		pr_err("Cannot find acpi handle for pnp device %s\n",
		       dev_name(&pnp->dev));
		ret = -ENODEV;
		goto err_notify;
	}

	status = acpi_evaluate_integer(gmux_data->dhandle, "GMGP", NULL, &gpe);
	if (ACPI_SUCCESS(status)) {
		gmux_data->gpe = (int)gpe;

		status = acpi_install_notify_handler(gmux_data->dhandle,
						     ACPI_DEVICE_NOTIFY,
						     &gmux_notify_handler, pnp);
		if (ACPI_FAILURE(status)) {
			pr_err("Install notify handler failed: %s\n",
			       acpi_format_exception(status));
			ret = -ENODEV;
			goto err_notify;
		}

		status = acpi_enable_gpe(NULL, gmux_data->gpe);
		if (ACPI_FAILURE(status)) {
			pr_err("Cannot enable gpe: %s\n",
			       acpi_format_exception(status));
			goto err_enable_gpe;
		}
	} else {
		pr_warn("No GPE found for gmux\n");
		gmux_data->gpe = -1;
	}

	if (vga_switcheroo_register_handler(&gmux_handler)) {
		ret = -ENODEV;
		goto err_register_handler;
	}

	init_completion(&gmux_data->powerchange_done);
	apple_gmux_data = gmux_data;
	gmux_enable_interrupts(gmux_data);

	return 0;

err_register_handler:
	if (gmux_data->gpe >= 0)
		acpi_disable_gpe(NULL, gmux_data->gpe);
err_enable_gpe:
	if (gmux_data->gpe >= 0)
		acpi_remove_notify_handler(gmux_data->dhandle,
					   ACPI_DEVICE_NOTIFY,
					   &gmux_notify_handler);
err_notify:
	backlight_device_unregister(bdev);
err_release:
	release_region(gmux_data->iostart, gmux_data->iolen);
err_free:
@@ -212,12 +577,23 @@ static void __devexit gmux_remove(struct pnp_dev *pnp)
{
	struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);

	vga_switcheroo_unregister_handler();
	gmux_disable_interrupts(gmux_data);
	if (gmux_data->gpe >= 0) {
		acpi_disable_gpe(NULL, gmux_data->gpe);
		acpi_remove_notify_handler(gmux_data->dhandle,
					   ACPI_DEVICE_NOTIFY,
					   &gmux_notify_handler);
	}

	backlight_device_unregister(gmux_data->bdev);

	release_region(gmux_data->iostart, gmux_data->iolen);
	apple_gmux_data = NULL;
	kfree(gmux_data);

	acpi_video_dmi_demote_vendor();
#ifdef CONFIG_ACPI_VIDEO
#if defined (CONFIG_ACPI_VIDEO) || defined (CONFIG_ACPI_VIDEO_MODULE)
	acpi_video_register();
#endif
	apple_bl_register();
@@ -233,6 +609,8 @@ static struct pnp_driver gmux_pnp_driver = {
	.probe		= gmux_probe,
	.remove		= __devexit_p(gmux_remove),
	.id_table	= gmux_device_ids,
	.suspend	= gmux_suspend,
	.resume		= gmux_resume
};

static int __init apple_gmux_init(void)
Loading