Matthew Garrett f1d2f5d
commit 9964e2a108292685fbbf0980493c5e9776eac762
Matthew Garrett f1d2f5d
Author: Seth Forshee <seth.forshee@canonical.com>
Matthew Garrett f1d2f5d
Date:   Fri Mar 16 14:41:21 2012 -0500
Matthew Garrett f1d2f5d
Matthew Garrett f1d2f5d
    apple_bl: Add register/unregister functions
Matthew Garrett f1d2f5d
    
Matthew Garrett f1d2f5d
    Add functions to allow other modules to enable or disable apple_bl. This
Matthew Garrett f1d2f5d
    will be used by the gmux driver to disable apple_bl when the gmux is
Matthew Garrett f1d2f5d
    present, as it is a better and more reliable option for brightness
Matthew Garrett f1d2f5d
    control.
Matthew Garrett f1d2f5d
    
Matthew Garrett f1d2f5d
    Signed-off-by: Seth Forshee <seth.forshee@canonical.com>
Matthew Garrett f1d2f5d
    Signed-off-by: Matthew Garrett <mjg@redhat.com>
Matthew Garrett f1d2f5d
Matthew Garrett f1d2f5d
diff --git a/drivers/video/backlight/apple_bl.c b/drivers/video/backlight/apple_bl.c
Matthew Garrett f1d2f5d
index be98d15..a523b25 100644
Matthew Garrett f1d2f5d
--- a/drivers/video/backlight/apple_bl.c
Matthew Garrett f1d2f5d
+++ b/drivers/video/backlight/apple_bl.c
Matthew Garrett f1d2f5d
@@ -24,6 +24,7 @@
Matthew Garrett f1d2f5d
 #include <linux/io.h>
Matthew Garrett f1d2f5d
 #include <linux/pci.h>
Matthew Garrett f1d2f5d
 #include <linux/acpi.h>
Matthew Garrett f1d2f5d
+#include <linux/atomic.h>
Matthew Garrett f1d2f5d
 
Matthew Garrett f1d2f5d
 static struct backlight_device *apple_backlight_device;
Matthew Garrett f1d2f5d
 
Matthew Garrett f1d2f5d
@@ -221,14 +222,32 @@ static struct acpi_driver apple_bl_driver = {
Matthew Garrett f1d2f5d
 	},
Matthew Garrett f1d2f5d
 };
Matthew Garrett f1d2f5d
 
Matthew Garrett f1d2f5d
+static atomic_t apple_bl_registered = ATOMIC_INIT(0);
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+int apple_bl_register(void)
Matthew Garrett f1d2f5d
+{
Matthew Garrett f1d2f5d
+	if (atomic_xchg(&apple_bl_registered, 1) == 0)
Matthew Garrett f1d2f5d
+		return acpi_bus_register_driver(&apple_bl_driver);
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+	return 0;
Matthew Garrett f1d2f5d
+}
Matthew Garrett f1d2f5d
+EXPORT_SYMBOL_GPL(apple_bl_register);
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+void apple_bl_unregister(void)
Matthew Garrett f1d2f5d
+{
Matthew Garrett f1d2f5d
+	if (atomic_xchg(&apple_bl_registered, 0) == 1)
Matthew Garrett f1d2f5d
+		acpi_bus_unregister_driver(&apple_bl_driver);
Matthew Garrett f1d2f5d
+}
Matthew Garrett f1d2f5d
+EXPORT_SYMBOL_GPL(apple_bl_unregister);
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
 static int __init apple_bl_init(void)
Matthew Garrett f1d2f5d
 {
Matthew Garrett f1d2f5d
-	return acpi_bus_register_driver(&apple_bl_driver);
Matthew Garrett f1d2f5d
+	return apple_bl_register();
Matthew Garrett f1d2f5d
 }
Matthew Garrett f1d2f5d
 
Matthew Garrett f1d2f5d
 static void __exit apple_bl_exit(void)
Matthew Garrett f1d2f5d
 {
Matthew Garrett f1d2f5d
-	acpi_bus_unregister_driver(&apple_bl_driver);
Matthew Garrett f1d2f5d
+	apple_bl_unregister();
Matthew Garrett f1d2f5d
 }
Matthew Garrett f1d2f5d
 
Matthew Garrett f1d2f5d
 module_init(apple_bl_init);
Matthew Garrett f1d2f5d
diff --git a/include/linux/apple_bl.h b/include/linux/apple_bl.h
Matthew Garrett f1d2f5d
new file mode 100644
Matthew Garrett f1d2f5d
index 0000000..47bedc0
Matthew Garrett f1d2f5d
--- /dev/null
Matthew Garrett f1d2f5d
+++ b/include/linux/apple_bl.h
Matthew Garrett f1d2f5d
@@ -0,0 +1,26 @@
Matthew Garrett f1d2f5d
+/*
Matthew Garrett f1d2f5d
+ * apple_bl exported symbols
Matthew Garrett f1d2f5d
+ */
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+#ifndef _LINUX_APPLE_BL_H
Matthew Garrett f1d2f5d
+#define _LINUX_APPLE_BL_H
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+#ifdef CONFIG_BACKLIGHT_APPLE
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+extern int apple_bl_register(void);
Matthew Garrett f1d2f5d
+extern void apple_bl_unregister(void);
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+#else /* !CONFIG_BACKLIGHT_APPLE */
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+static inline int apple_bl_register(void)
Matthew Garrett f1d2f5d
+{
Matthew Garrett f1d2f5d
+	return 0;
Matthew Garrett f1d2f5d
+}
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+static inline void apple_bl_unregister(void)
Matthew Garrett f1d2f5d
+{
Matthew Garrett f1d2f5d
+}
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+#endif /* !CONFIG_BACKLIGHT_APPLE */
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+#endif /* _LINUX_APPLE_BL_H */
Matthew Garrett f1d2f5d
commit 89c093937dc8b07db4652251c83ec3710e5f15a2
Matthew Garrett f1d2f5d
Author: Seth Forshee <seth.forshee@canonical.com>
Matthew Garrett f1d2f5d
Date:   Fri Mar 16 14:41:22 2012 -0500
Matthew Garrett f1d2f5d
Matthew Garrett f1d2f5d
    platform/x86: Add driver for Apple gmux device
Matthew Garrett f1d2f5d
    
Matthew Garrett f1d2f5d
    Apple laptops with hybrid graphics have a device named gmux that
Matthew Garrett f1d2f5d
    controls the muxing of the LVDS panel between the GPUs as well as screen
Matthew Garrett f1d2f5d
    brightness. This driver adds support for the gmux device. Only backlight
Matthew Garrett f1d2f5d
    control is supported initially.
Matthew Garrett f1d2f5d
    
Matthew Garrett f1d2f5d
    Signed-off-by: Seth Forshee <seth.forshee@canonical.com>
Matthew Garrett f1d2f5d
    Signed-off-by: Matthew Garrett <mjg@redhat.com>
Matthew Garrett f1d2f5d
    Tested-by: Grant Likely <grant.likely@secretlab.ca>
Matthew Garrett f1d2f5d
Matthew Garrett f1d2f5d
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
Matthew Garrett f1d2f5d
index ce10f03..c5b4bfe 100644
Matthew Garrett f1d2f5d
--- a/drivers/platform/x86/Kconfig
Matthew Garrett f1d2f5d
+++ b/drivers/platform/x86/Kconfig
Matthew Garrett f1d2f5d
@@ -752,4 +752,14 @@ config SAMSUNG_Q10
Matthew Garrett f1d2f5d
 	  This driver provides support for backlight control on Samsung Q10
Matthew Garrett f1d2f5d
 	  and related laptops, including Dell Latitude X200.
Matthew Garrett f1d2f5d
 
Matthew Garrett f1d2f5d
+config APPLE_GMUX
Matthew Garrett f1d2f5d
+	tristate "Apple Gmux Driver"
Matthew Garrett f1d2f5d
+	depends on PNP
Matthew Garrett f1d2f5d
+	select BACKLIGHT_CLASS_DEVICE
Matthew Garrett f1d2f5d
+	---help---
Matthew Garrett f1d2f5d
+	  This driver provides support for the gmux device found on many
Matthew Garrett f1d2f5d
+	  Apple laptops, which controls the display mux for the hybrid
Matthew Garrett f1d2f5d
+	  graphics as well as the backlight. Currently only backlight
Matthew Garrett f1d2f5d
+	  control is supported by the driver.
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
 endif # X86_PLATFORM_DEVICES
Matthew Garrett f1d2f5d
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
Matthew Garrett f1d2f5d
index dcfee6b..bf7e4f9 100644
Matthew Garrett f1d2f5d
--- a/drivers/platform/x86/Makefile
Matthew Garrett f1d2f5d
+++ b/drivers/platform/x86/Makefile
Matthew Garrett f1d2f5d
@@ -49,3 +49,4 @@ obj-$(CONFIG_MXM_WMI)		+= mxm-wmi.o
Matthew Garrett f1d2f5d
 obj-$(CONFIG_INTEL_MID_POWER_BUTTON)	+= intel_mid_powerbtn.o
Matthew Garrett f1d2f5d
 obj-$(CONFIG_INTEL_OAKTRAIL)	+= intel_oaktrail.o
Matthew Garrett f1d2f5d
 obj-$(CONFIG_SAMSUNG_Q10)	+= samsung-q10.o
Matthew Garrett f1d2f5d
+obj-$(CONFIG_APPLE_GMUX)	+= apple-gmux.o
Matthew Garrett f1d2f5d
diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c
Matthew Garrett f1d2f5d
new file mode 100644
Matthew Garrett f1d2f5d
index 0000000..c81e31f
Matthew Garrett f1d2f5d
--- /dev/null
Matthew Garrett f1d2f5d
+++ b/drivers/platform/x86/apple-gmux.c
Matthew Garrett f1d2f5d
@@ -0,0 +1,242 @@
Matthew Garrett f1d2f5d
+/*
Matthew Garrett f1d2f5d
+ *  Gmux driver for Apple laptops
Matthew Garrett f1d2f5d
+ *
Matthew Garrett f1d2f5d
+ *  Copyright (C) Canonical Ltd. <seth.forshee@canonical.com>
Matthew Garrett f1d2f5d
+ *
Matthew Garrett f1d2f5d
+ *  This program is free software; you can redistribute it and/or modify
Matthew Garrett f1d2f5d
+ *  it under the terms of the GNU General Public License version 2 as
Matthew Garrett f1d2f5d
+ *  published by the Free Software Foundation.
Matthew Garrett f1d2f5d
+ */
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+#include <linux/module.h>
Matthew Garrett f1d2f5d
+#include <linux/kernel.h>
Matthew Garrett f1d2f5d
+#include <linux/init.h>
Matthew Garrett f1d2f5d
+#include <linux/backlight.h>
Matthew Garrett f1d2f5d
+#include <linux/acpi.h>
Matthew Garrett f1d2f5d
+#include <linux/pnp.h>
Matthew Garrett f1d2f5d
+#include <linux/apple_bl.h>
Matthew Garrett f1d2f5d
+#include <acpi/video.h>
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+struct apple_gmux_data {
Matthew Garrett f1d2f5d
+	unsigned long iostart;
Matthew Garrett f1d2f5d
+	unsigned long iolen;
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+	struct backlight_device *bdev;
Matthew Garrett f1d2f5d
+};
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+/*
Matthew Garrett f1d2f5d
+ * gmux port offsets. Many of these are not yet used, but may be in the
Matthew Garrett f1d2f5d
+ * future, and it's useful to have them documented here anyhow.
Matthew Garrett f1d2f5d
+ */
Matthew Garrett f1d2f5d
+#define GMUX_PORT_VERSION_MAJOR		0x04
Matthew Garrett f1d2f5d
+#define GMUX_PORT_VERSION_MINOR		0x05
Matthew Garrett f1d2f5d
+#define GMUX_PORT_VERSION_RELEASE	0x06
Matthew Garrett f1d2f5d
+#define GMUX_PORT_SWITCH_DISPLAY	0x10
Matthew Garrett f1d2f5d
+#define GMUX_PORT_SWITCH_GET_DISPLAY	0x11
Matthew Garrett f1d2f5d
+#define GMUX_PORT_INTERRUPT_ENABLE	0x14
Matthew Garrett f1d2f5d
+#define GMUX_PORT_INTERRUPT_STATUS	0x16
Matthew Garrett f1d2f5d
+#define GMUX_PORT_SWITCH_DDC		0x28
Matthew Garrett f1d2f5d
+#define GMUX_PORT_SWITCH_EXTERNAL	0x40
Matthew Garrett f1d2f5d
+#define GMUX_PORT_SWITCH_GET_EXTERNAL	0x41
Matthew Garrett f1d2f5d
+#define GMUX_PORT_DISCRETE_POWER	0x50
Matthew Garrett f1d2f5d
+#define GMUX_PORT_MAX_BRIGHTNESS	0x70
Matthew Garrett f1d2f5d
+#define GMUX_PORT_BRIGHTNESS		0x74
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+#define GMUX_MIN_IO_LEN			(GMUX_PORT_BRIGHTNESS + 4)
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+#define GMUX_INTERRUPT_ENABLE		0xff
Matthew Garrett f1d2f5d
+#define GMUX_INTERRUPT_DISABLE		0x00
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+#define GMUX_INTERRUPT_STATUS_ACTIVE	0
Matthew Garrett f1d2f5d
+#define GMUX_INTERRUPT_STATUS_DISPLAY	(1 << 0)
Matthew Garrett f1d2f5d
+#define GMUX_INTERRUPT_STATUS_POWER	(1 << 2)
Matthew Garrett f1d2f5d
+#define GMUX_INTERRUPT_STATUS_HOTPLUG	(1 << 3)
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+#define GMUX_BRIGHTNESS_MASK		0x00ffffff
Matthew Garrett f1d2f5d
+#define GMUX_MAX_BRIGHTNESS		GMUX_BRIGHTNESS_MASK
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+static inline u8 gmux_read8(struct apple_gmux_data *gmux_data, int port)
Matthew Garrett f1d2f5d
+{
Matthew Garrett f1d2f5d
+	return inb(gmux_data->iostart + port);
Matthew Garrett f1d2f5d
+}
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+static inline void gmux_write8(struct apple_gmux_data *gmux_data, int port,
Matthew Garrett f1d2f5d
+			       u8 val)
Matthew Garrett f1d2f5d
+{
Matthew Garrett f1d2f5d
+	outb(val, gmux_data->iostart + port);
Matthew Garrett f1d2f5d
+}
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+static inline u32 gmux_read32(struct apple_gmux_data *gmux_data, int port)
Matthew Garrett f1d2f5d
+{
Matthew Garrett f1d2f5d
+	return inl(gmux_data->iostart + port);
Matthew Garrett f1d2f5d
+}
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+static int gmux_get_brightness(struct backlight_device *bd)
Matthew Garrett f1d2f5d
+{
Matthew Garrett f1d2f5d
+	struct apple_gmux_data *gmux_data = bl_get_data(bd);
Matthew Garrett f1d2f5d
+	return gmux_read32(gmux_data, GMUX_PORT_BRIGHTNESS) &
Matthew Garrett f1d2f5d
+	       GMUX_BRIGHTNESS_MASK;
Matthew Garrett f1d2f5d
+}
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+static int gmux_update_status(struct backlight_device *bd)
Matthew Garrett f1d2f5d
+{
Matthew Garrett f1d2f5d
+	struct apple_gmux_data *gmux_data = bl_get_data(bd);
Matthew Garrett f1d2f5d
+	u32 brightness = bd->props.brightness;
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+	/*
Matthew Garrett f1d2f5d
+	 * Older gmux versions require writing out lower bytes first then
Matthew Garrett f1d2f5d
+	 * setting the upper byte to 0 to flush the values. Newer versions
Matthew Garrett f1d2f5d
+	 * accept a single u32 write, but the old method also works, so we
Matthew Garrett f1d2f5d
+	 * just use the old method for all gmux versions.
Matthew Garrett f1d2f5d
+	 */
Matthew Garrett f1d2f5d
+	gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS, brightness);
Matthew Garrett f1d2f5d
+	gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 1, brightness >> 8);
Matthew Garrett f1d2f5d
+	gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 2, brightness >> 16);
Matthew Garrett f1d2f5d
+	gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 3, 0);
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+	return 0;
Matthew Garrett f1d2f5d
+}
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+static const struct backlight_ops gmux_bl_ops = {
Matthew Garrett f1d2f5d
+	.get_brightness = gmux_get_brightness,
Matthew Garrett f1d2f5d
+	.update_status = gmux_update_status,
Matthew Garrett f1d2f5d
+};
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+static int __devinit gmux_probe(struct pnp_dev *pnp,
Matthew Garrett f1d2f5d
+				const struct pnp_device_id *id)
Matthew Garrett f1d2f5d
+{
Matthew Garrett f1d2f5d
+	struct apple_gmux_data *gmux_data;
Matthew Garrett f1d2f5d
+	struct resource *res;
Matthew Garrett f1d2f5d
+	struct backlight_properties props;
Matthew Garrett f1d2f5d
+	struct backlight_device *bdev;
Matthew Garrett f1d2f5d
+	u8 ver_major, ver_minor, ver_release;
Matthew Garrett f1d2f5d
+	int ret = -ENXIO;
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+	gmux_data = kzalloc(sizeof(*gmux_data), GFP_KERNEL);
Matthew Garrett f1d2f5d
+	if (!gmux_data)
Matthew Garrett f1d2f5d
+		return -ENOMEM;
Matthew Garrett f1d2f5d
+	pnp_set_drvdata(pnp, gmux_data);
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+	res = pnp_get_resource(pnp, IORESOURCE_IO, 0);
Matthew Garrett f1d2f5d
+	if (!res) {
Matthew Garrett f1d2f5d
+		pr_err("Failed to find gmux I/O resource\n");
Matthew Garrett f1d2f5d
+		goto err_free;
Matthew Garrett f1d2f5d
+	}
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+	gmux_data->iostart = res->start;
Matthew Garrett f1d2f5d
+	gmux_data->iolen = res->end - res->start;
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+	if (gmux_data->iolen < GMUX_MIN_IO_LEN) {
Matthew Garrett f1d2f5d
+		pr_err("gmux I/O region too small (%lu < %u)\n",
Matthew Garrett f1d2f5d
+		       gmux_data->iolen, GMUX_MIN_IO_LEN);
Matthew Garrett f1d2f5d
+		goto err_free;
Matthew Garrett f1d2f5d
+	}
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+	if (!request_region(gmux_data->iostart, gmux_data->iolen,
Matthew Garrett f1d2f5d
+			    "Apple gmux")) {
Matthew Garrett f1d2f5d
+		pr_err("gmux I/O already in use\n");
Matthew Garrett f1d2f5d
+		goto err_free;
Matthew Garrett f1d2f5d
+	}
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+	/*
Matthew Garrett f1d2f5d
+	 * On some machines the gmux is in ACPI even thought the machine
Matthew Garrett f1d2f5d
+	 * doesn't really have a gmux. Check for invalid version information
Matthew Garrett f1d2f5d
+	 * to detect this.
Matthew Garrett f1d2f5d
+	 */
Matthew Garrett f1d2f5d
+	ver_major = gmux_read8(gmux_data, GMUX_PORT_VERSION_MAJOR);
Matthew Garrett f1d2f5d
+	ver_minor = gmux_read8(gmux_data, GMUX_PORT_VERSION_MINOR);
Matthew Garrett f1d2f5d
+	ver_release = gmux_read8(gmux_data, GMUX_PORT_VERSION_RELEASE);
Matthew Garrett f1d2f5d
+	if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) {
Matthew Garrett f1d2f5d
+		pr_info("gmux device not present\n");
Matthew Garrett f1d2f5d
+		ret = -ENODEV;
Matthew Garrett f1d2f5d
+		goto err_release;
Matthew Garrett f1d2f5d
+	}
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+	pr_info("Found gmux version %d.%d.%d\n", ver_major, ver_minor,
Matthew Garrett f1d2f5d
+		ver_release);
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+	memset(&props, 0, sizeof(props));
Matthew Garrett f1d2f5d
+	props.type = BACKLIGHT_PLATFORM;
Matthew Garrett f1d2f5d
+	props.max_brightness = gmux_read32(gmux_data, GMUX_PORT_MAX_BRIGHTNESS);
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+	/*
Matthew Garrett f1d2f5d
+	 * Currently it's assumed that the maximum brightness is less than
Matthew Garrett f1d2f5d
+	 * 2^24 for compatibility with old gmux versions. Cap the max
Matthew Garrett f1d2f5d
+	 * brightness at this value, but print a warning if the hardware
Matthew Garrett f1d2f5d
+	 * reports something higher so that it can be fixed.
Matthew Garrett f1d2f5d
+	 */
Matthew Garrett f1d2f5d
+	if (WARN_ON(props.max_brightness > GMUX_MAX_BRIGHTNESS))
Matthew Garrett f1d2f5d
+		props.max_brightness = GMUX_MAX_BRIGHTNESS;
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+	bdev = backlight_device_register("gmux_backlight", &pnp->dev,
Matthew Garrett f1d2f5d
+					 gmux_data, &gmux_bl_ops, &props;;
Matthew Garrett f1d2f5d
+	if (IS_ERR(bdev)) {
Matthew Garrett f1d2f5d
+		ret = PTR_ERR(bdev);
Matthew Garrett f1d2f5d
+		goto err_release;
Matthew Garrett f1d2f5d
+	}
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+	gmux_data->bdev = bdev;
Matthew Garrett f1d2f5d
+	bdev->props.brightness = gmux_get_brightness(bdev);
Matthew Garrett f1d2f5d
+	backlight_update_status(bdev);
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+	/*
Matthew Garrett f1d2f5d
+	 * The backlight situation on Macs is complicated. If the gmux is
Matthew Garrett f1d2f5d
+	 * present it's the best choice, because it always works for
Matthew Garrett f1d2f5d
+	 * backlight control and supports more levels than other options.
Matthew Garrett f1d2f5d
+	 * Disable the other backlight choices.
Matthew Garrett f1d2f5d
+	 */
Matthew Garrett f1d2f5d
+	acpi_video_unregister();
Matthew Garrett f1d2f5d
+	apple_bl_unregister();
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+	return 0;
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+err_release:
Matthew Garrett f1d2f5d
+	release_region(gmux_data->iostart, gmux_data->iolen);
Matthew Garrett f1d2f5d
+err_free:
Matthew Garrett f1d2f5d
+	kfree(gmux_data);
Matthew Garrett f1d2f5d
+	return ret;
Matthew Garrett f1d2f5d
+}
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+static void __devexit gmux_remove(struct pnp_dev *pnp)
Matthew Garrett f1d2f5d
+{
Matthew Garrett f1d2f5d
+	struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+	backlight_device_unregister(gmux_data->bdev);
Matthew Garrett f1d2f5d
+	release_region(gmux_data->iostart, gmux_data->iolen);
Matthew Garrett f1d2f5d
+	kfree(gmux_data);
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+	acpi_video_register();
Matthew Garrett f1d2f5d
+	apple_bl_register();
Matthew Garrett f1d2f5d
+}
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+static const struct pnp_device_id gmux_device_ids[] = {
Matthew Garrett f1d2f5d
+	{"APP000B", 0},
Matthew Garrett f1d2f5d
+	{"", 0}
Matthew Garrett f1d2f5d
+};
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+static struct pnp_driver gmux_pnp_driver = {
Matthew Garrett f1d2f5d
+	.name		= "apple-gmux",
Matthew Garrett f1d2f5d
+	.probe		= gmux_probe,
Matthew Garrett f1d2f5d
+	.remove		= __devexit_p(gmux_remove),
Matthew Garrett f1d2f5d
+	.id_table	= gmux_device_ids,
Matthew Garrett f1d2f5d
+};
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+static int __init apple_gmux_init(void)
Matthew Garrett f1d2f5d
+{
Matthew Garrett f1d2f5d
+	return pnp_register_driver(&gmux_pnp_driver);
Matthew Garrett f1d2f5d
+}
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+static void __exit apple_gmux_exit(void)
Matthew Garrett f1d2f5d
+{
Matthew Garrett f1d2f5d
+	pnp_unregister_driver(&gmux_pnp_driver);
Matthew Garrett f1d2f5d
+}
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+module_init(apple_gmux_init);
Matthew Garrett f1d2f5d
+module_exit(apple_gmux_exit);
Matthew Garrett f1d2f5d
+
Matthew Garrett f1d2f5d
+MODULE_AUTHOR("Seth Forshee <seth.forshee@canonical.com>");
Matthew Garrett f1d2f5d
+MODULE_DESCRIPTION("Apple Gmux Driver");
Matthew Garrett f1d2f5d
+MODULE_LICENSE("GPL");
Matthew Garrett f1d2f5d
+MODULE_DEVICE_TABLE(pnp, gmux_device_ids);