From c78a628849675580c7c5e1f07193c632e4b6827f Mon Sep 17 00:00:00 2001 From: Mario Schwalbe Date: Sun, 11 Jan 2009 00:11:34 +0000 Subject: [PATCH] backlight: Add support for MacBook 5, MacBook Air 2, and MacBook Pro 5 This patch adds support for the new Apple models incorporating an Nvidia chipset. Apple still uses the same protocol as on older models, but the registers moved to a different address. To do this, two sets of functions are added for the Intel/Nvidia chipset models and passed by the DMI_MATCH function. The initial code has been contributed by Hu Gang . The driver is known to work on MacBook Pro 3, MacBook Pro 4 and MacBook Pro 5. Its known to work with limitations on MacBook 5 / MacBook Air 2. Changing brightness within X doesn't work, if using Nvidia's proprietary graphics driver with no known fix at present. Changing brightness on a text console or using the open-source driver does work. MacBook Pro 5 has a known bug where the initial brightness after bootup is the last recently used brightness (in Mac OSX), while the firmware reports maximum. Impossible to fix. [akpm@linux-foundation.org: build fix] [rpurdie@linux.intel.com: Rebased the patch against latest git] Signed-off-by: Mario Schwalbe Signed-off-by: Andrew Morton Signed-off-by: Richard Purdie --- drivers/video/backlight/mbp_nvidia_bl.c | 162 ++++++++++++++++++------ 1 file changed, 126 insertions(+), 36 deletions(-) diff --git a/drivers/video/backlight/mbp_nvidia_bl.c b/drivers/video/backlight/mbp_nvidia_bl.c index 65864c500455..ce09b83244ac 100644 --- a/drivers/video/backlight/mbp_nvidia_bl.c +++ b/drivers/video/backlight/mbp_nvidia_bl.c @@ -27,52 +27,142 @@ static struct backlight_device *mbp_backlight_device; -static struct dmi_system_id __initdata mbp_device_table[] = { - { - .ident = "3,1", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3,1"), - }, - }, - { - .ident = "3,2", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3,2"), - }, - }, - { - .ident = "4,1", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro4,1"), - }, - }, - { } +/* Structure to be passed to the DMI_MATCH function. */ +struct dmi_match_data { + /* I/O resource to allocate. */ + unsigned long iostart; + unsigned long iolen; + /* Backlight operations structure. */ + struct backlight_ops backlight_ops; }; -static int mbp_send_intensity(struct backlight_device *bd) +/* + * Implementation for MacBooks with Intel chipset. + */ +static int intel_chipset_send_intensity(struct backlight_device *bd) { int intensity = bd->props.brightness; outb(0x04 | (intensity << 4), 0xb3); outb(0xbf, 0xb2); - return 0; } -static int mbp_get_intensity(struct backlight_device *bd) +static int intel_chipset_get_intensity(struct backlight_device *bd) { outb(0x03, 0xb3); outb(0xbf, 0xb2); return inb(0xb3) >> 4; } -static struct backlight_ops mbp_ops = { - .options = BL_CORE_SUSPENDRESUME, - .get_brightness = mbp_get_intensity, - .update_status = mbp_send_intensity, +static const struct dmi_match_data intel_chipset_data = { + .iostart = 0xb2, + .iolen = 2, + .backlight_ops = { + .options = BL_CORE_SUSPENDRESUME, + .get_brightness = intel_chipset_get_intensity, + .update_status = intel_chipset_send_intensity, + } +}; + +/* + * Implementation for MacBooks with Nvidia chipset. + */ +static int nvidia_chipset_send_intensity(struct backlight_device *bd) +{ + int intensity = bd->props.brightness; + + outb(0x04 | (intensity << 4), 0x52f); + outb(0xbf, 0x52e); + return 0; +} + +static int nvidia_chipset_get_intensity(struct backlight_device *bd) +{ + outb(0x03, 0x52f); + outb(0xbf, 0x52e); + return inb(0x52f) >> 4; +} + +static const struct dmi_match_data nvidia_chipset_data = { + .iostart = 0x52e, + .iolen = 2, + .backlight_ops = { + .options = BL_CORE_SUSPENDRESUME, + .get_brightness = nvidia_chipset_get_intensity, + .update_status = nvidia_chipset_send_intensity + } +}; + +/* + * DMI matching. + */ +static /* const */ struct dmi_match_data *driver_data; + +static int mbp_dmi_match(const struct dmi_system_id *id) +{ + driver_data = id->driver_data; + + printk(KERN_INFO "mbp_nvidia_bl: %s detected\n", id->ident); + return 1; +} + +static const struct dmi_system_id __initdata mbp_device_table[] = { + { + .callback = mbp_dmi_match, + .ident = "MacBookPro 3,1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3,1"), + }, + .driver_data = (void *)&intel_chipset_data, + }, + { + .callback = mbp_dmi_match, + .ident = "MacBookPro 3,2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3,2"), + }, + .driver_data = (void *)&intel_chipset_data, + }, + { + .callback = mbp_dmi_match, + .ident = "MacBookPro 4,1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro4,1"), + }, + .driver_data = (void *)&intel_chipset_data, + }, + { + .callback = mbp_dmi_match, + .ident = "MacBook 5,1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5,1"), + }, + .driver_data = (void *)&nvidia_chipset_data, + }, + { + .callback = mbp_dmi_match, + .ident = "MacBookAir 2,1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir2,1"), + }, + .driver_data = (void *)&nvidia_chipset_data, + }, + { + .callback = mbp_dmi_match, + .ident = "MacBookPro 5,1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,1"), + }, + .driver_data = (void *)&nvidia_chipset_data, + }, + { } }; static int __init mbp_init(void) @@ -80,20 +170,20 @@ static int __init mbp_init(void) if (!dmi_check_system(mbp_device_table)) return -ENODEV; - if (!request_region(0xb2, 2, "Macbook Pro backlight")) + if (!request_region(driver_data->iostart, driver_data->iolen, + "Macbook Pro backlight")) return -ENXIO; mbp_backlight_device = backlight_device_register("mbp_backlight", - NULL, NULL, - &mbp_ops); + NULL, NULL, &driver_data->backlight_ops); if (IS_ERR(mbp_backlight_device)) { - release_region(0xb2, 2); + release_region(driver_data->iostart, driver_data->iolen); return PTR_ERR(mbp_backlight_device); } mbp_backlight_device->props.max_brightness = 15; mbp_backlight_device->props.brightness = - mbp_get_intensity(mbp_backlight_device); + driver_data->backlight_ops.get_brightness(mbp_backlight_device); backlight_update_status(mbp_backlight_device); return 0; @@ -103,7 +193,7 @@ static void __exit mbp_exit(void) { backlight_device_unregister(mbp_backlight_device); - release_region(0xb2, 2); + release_region(driver_data->iostart, driver_data->iolen); } module_init(mbp_init);