lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1328300884-21551-4-git-send-email-seth.forshee@canonical.com>
Date:	Fri,  3 Feb 2012 14:28:04 -0600
From:	Seth Forshee <seth.forshee@...onical.com>
To:	linux-kernel@...r.kernel.org
Cc:	Richard Purdie <rpurdie@...ys.net>,
	Matthew Garrett <mjg@...hat.com>
Subject: [PATCH 3/3] apple_bl: Add support for gmux backlight control

On many Apple models the gmux device, which controls the display mux,
also supports control of the display backlight. On some models this is
the only way to adjust screen brightness.

Signed-off-by: Seth Forshee <seth.forshee@...onical.com>
---
 drivers/video/backlight/apple_bl.c |  183 +++++++++++++++++++++++++++++++-----
 1 files changed, 160 insertions(+), 23 deletions(-)

diff --git a/drivers/video/backlight/apple_bl.c b/drivers/video/backlight/apple_bl.c
index e65b459..b705dc2 100644
--- a/drivers/video/backlight/apple_bl.c
+++ b/drivers/video/backlight/apple_bl.c
@@ -27,7 +27,13 @@
 #include <linux/pci.h>
 #include <linux/acpi.h>
 
+#define APPLE_BL_ID	"APP0002"
+#define APPLE_GMUX_ID	"APP000B"
+
 struct apple_bl_data {
+	const char *name;
+	int max_brightness;
+
 	/* I/O resource to allocate. */
 	unsigned long iostart;
 	unsigned long iolen;
@@ -41,6 +47,37 @@ struct apple_bl_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.
+ */
+#define GMUX_PORT_VERSION_MAJOR		0x04
+#define GMUX_PORT_VERSION_MINOR		0x05
+#define GMUX_PORT_VERSION_RELEASE	0x06
+#define GMUX_PORT_SWITCH_DISPLAY	0x10
+#define GMUX_PORT_SWITCH_GET_DISPLAY	0x11
+#define GMUX_PORT_INTERRUPT_ENABLE	0x14
+#define GMUX_PORT_INTERRUPT_STATUS	0x16
+#define GMUX_PORT_SWITCH_DDC		0x28
+#define GMUX_PORT_SWITCH_EXTERNAL	0x40
+#define GMUX_PORT_SWITCH_GET_EXTERNAL	0x41
+#define GMUX_PORT_DISCRETE_POWER	0x50
+#define GMUX_PORT_MAX_BRIGHTNESS	0x70
+#define GMUX_PORT_BRIGHTNESS		0x74
+
+#define GMUX_MIN_IO_LEN			(GMUX_PORT_BRIGHTNESS + 4)
+
+#define GMUX_INTERRUPT_ENABLE		0xff
+#define GMUX_INTERRUPT_DISABLE		0x00
+
+#define GMUX_INTERRUPT_STATUS_ACTIVE	0
+#define GMUX_INTERRUPT_STATUS_DISPLAY	(1 << 0)
+#define GMUX_INTERRUPT_STATUS_POWER	(1 << 2)
+#define GMUX_INTERRUPT_STATUS_HOTPLUG	(1 << 3)
+
+#define GMUX_BRIGHTNESS_MASK		0x00ffffff
+#define GMUX_MAX_BRIGHTNESS		GMUX_BRIGHTNESS_MASK
+
+/*
  * Implementation for machines with Intel chipset.
  */
 static void intel_chipset_set_brightness(struct apple_bl_data *bl_data,
@@ -87,6 +124,28 @@ static int nvidia_chipset_get_brightness(struct apple_bl_data *bl_data)
 }
 
 /*
+ * Implementation for gmux backlight control
+ */
+static void gmux_set_brightness(struct apple_bl_data *bl_data, int intensity)
+{
+	/*
+	 * Older versions of gmux require writing out lower bytes first
+	 * then setting upper byte to 0 to flush values. Newer versions
+	 * accept a single u32 write, but the old method works as well
+	 * so just use it for everything.
+	 */
+	outb(intensity, bl_data->iostart + GMUX_PORT_BRIGHTNESS);
+	outb(intensity >> 8, bl_data->iostart + GMUX_PORT_BRIGHTNESS + 1);
+	outb(intensity >> 16, bl_data->iostart + GMUX_PORT_BRIGHTNESS + 2);
+	outb(0, bl_data->iostart + GMUX_PORT_BRIGHTNESS + 3);
+}
+
+static int gmux_get_brightness(struct apple_bl_data *bl_data)
+{
+	return inl(bl_data->iostart + GMUX_PORT_BRIGHTNESS) & GMUX_BRIGHTNESS_MASK;
+}
+
+/*
  * Backlight device class operations
  */
 static int apple_bl_get_brightness(struct backlight_device *bd)
@@ -108,54 +167,131 @@ static const struct backlight_ops apple_bl_ops = {
 	.update_status = apple_bl_update_status,
 };
 
-static int __devinit apple_bl_add(struct acpi_device *dev)
+static int __devinit legacy_bl_init(struct apple_bl_data *bl_data,
+				    struct acpi_device *dev)
 {
-	struct apple_bl_data *bl_data;
-	struct backlight_properties props;
-	struct backlight_device *bdev;
 	struct pci_dev *host;
 	unsigned short vendor;
-	int intensity;
-	int ret = -ENODEV;
-
-	bl_data = kzalloc(sizeof(*bl_data), GFP_KERNEL);
-	if (!bl_data)
-		return -ENOMEM;
-	dev->driver_data = dev;
 
 	host = pci_get_bus_and_slot(0, 0);
-
 	if (!host) {
 		pr_err("unable to find PCI host\n");
-		goto err_free;
+		return -ENODEV;
 	}
 
 	vendor = host->vendor;
 	pci_dev_put(host);
 
-	if (vendor == PCI_VENDOR_ID_INTEL) {
+	switch (vendor) {
+	case PCI_VENDOR_ID_INTEL:
 		bl_data->iostart = 0xb2;
 		bl_data->iolen = 2;
 		bl_data->get_brightness = intel_chipset_get_brightness;
 		bl_data->set_brightness = intel_chipset_set_brightness;
-	} else if (vendor == PCI_VENDOR_ID_NVIDIA) {
+		break;
+	case PCI_VENDOR_ID_NVIDIA:
 		bl_data->iostart = 0x52e;
 		bl_data->iolen = 2;
 		bl_data->get_brightness = nvidia_chipset_get_brightness;
 		bl_data->set_brightness = nvidia_chipset_set_brightness;
-	} else {
-		pr_err("unknown hardware\n");
-		goto err_free;
+		break;
+	default:
+		pr_err("no backlight support for PCI vendor %hu\n", vendor);
+		return -ENODEV;
+	}
+
+	bl_data->name = "apple_backlight";
+	bl_data->max_brightness = 15;
+	return 0;
+}
+
+static acpi_status __devinit gmux_get_resources(struct acpi_resource *res,
+						void *context)
+{
+	struct apple_bl_data *bl_data = context;
+	struct acpi_resource_io *io;
+
+	if (res->type == ACPI_RESOURCE_TYPE_IO) {
+		io = &res->data.io;
+		bl_data->iostart = io->minimum;
+		bl_data->iolen = io->maximum - io->minimum;
+
+		return AE_CTRL_TERMINATE;
+	}
+
+	return AE_OK;
+}
+
+static int __devinit gmux_init(struct apple_bl_data *bl_data,
+			       struct acpi_device *dev)
+{
+	acpi_status status;
+
+	status = acpi_walk_resources(dev->handle, METHOD_NAME__CRS,
+				     gmux_get_resources, bl_data);
+	if (ACPI_FAILURE(status))
+		return -ENXIO;
+
+	if (!bl_data->iostart) {
+		pr_err("Failed to find gmux I/O resources\n");
+		return -ENXIO;
+	}
+
+	if (bl_data->iolen < GMUX_MIN_IO_LEN) {
+		pr_err("gmux I/O region too small (%lu < %u)\n",
+		       bl_data->iolen, GMUX_MIN_IO_LEN);
+		return -ENXIO;
 	}
 
+	bl_data->name = "gmux_backlight";
+	bl_data->get_brightness = gmux_get_brightness;
+	bl_data->set_brightness = gmux_set_brightness;
+	bl_data->max_brightness =
+		inl(bl_data->iostart + GMUX_PORT_MAX_BRIGHTNESS);
+
+	/*
+	 * Currently it's assumed that the maximum brightness is less
+	 * than 2^24 for compatibility with old gmux versions. Cap the
+	 * max brightness at this max value, but print a warning if
+	 * the hardware reports something higher so it can be fixed.
+	 */
+	if (WARN_ON(bl_data->max_brightness > GMUX_MAX_BRIGHTNESS))
+		bl_data->max_brightness = GMUX_MAX_BRIGHTNESS;
+
+	return 0;
+}
+
+static int __devinit apple_bl_add(struct acpi_device *dev)
+{
+	struct apple_bl_data *bl_data;
+	struct backlight_properties props;
+	struct backlight_device *bdev;
+	int intensity;
+	int ret = -ENODEV;
+
+	bl_data = kzalloc(sizeof(*bl_data), GFP_KERNEL);
+	if (!bl_data)
+		return -ENOMEM;
+	dev->driver_data = bl_data;
+
+	if (!strcmp(acpi_device_hid(dev), APPLE_GMUX_ID))
+		ret = gmux_init(bl_data, dev);
+	else
+		ret = legacy_bl_init(bl_data, dev);
+
+	if (ret)
+		goto err_free;
+
 	/* Check that the hardware responds - this may not work under EFI */
 
 	intensity = bl_data->get_brightness(bl_data);
 
 	if (!intensity) {
 		bl_data->set_brightness(bl_data, 1);
-		if (!bl_data->get_brightness(bl_data))
+		if (!bl_data->get_brightness(bl_data)) {
+			ret = -ENODEV;
 			goto err_free;
+		}
 
 		bl_data->set_brightness(bl_data, 0);
 	}
@@ -168,8 +304,8 @@ static int __devinit apple_bl_add(struct acpi_device *dev)
 
 	memset(&props, 0, sizeof(struct backlight_properties));
 	props.type = BACKLIGHT_PLATFORM;
-	props.max_brightness = 15;
-	bdev = backlight_device_register("apple_backlight", NULL, bl_data,
+	props.max_brightness = bl_data->max_brightness;
+	bdev = backlight_device_register(bl_data->name, &dev->dev, bl_data,
 					 &apple_bl_ops, &props);
 
 	if (IS_ERR(bdev)) {
@@ -178,7 +314,7 @@ static int __devinit apple_bl_add(struct acpi_device *dev)
 	}
 
 	bl_data->bdev = bdev;
-	bdev->props.brightness = bl_data->get_brightness(bl_data);
+	bdev->props.brightness = intensity;
 	backlight_update_status(bdev);
 
 	return 0;
@@ -202,7 +338,8 @@ static int __devexit apple_bl_remove(struct acpi_device *dev, int type)
 }
 
 static const struct acpi_device_id apple_bl_ids[] = {
-	{"APP0002", 0},
+	{APPLE_BL_ID, 0},
+	{APPLE_GMUX_ID, 0},
 	{"", 0},
 };
 
-- 
1.7.8.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ