diff --git a/drivers/video/backlight/mbp_nvidia_bl.c b/drivers/video/backlight/mbp_nvidia_bl.c index 3bb4c0a..1754b51 100644 --- a/drivers/video/backlight/mbp_nvidia_bl.c +++ b/drivers/video/backlight/mbp_nvidia_bl.c @@ -1,5 +1,5 @@ /* - * Backlight Driver for Nvidia 8600 in Macbook Pro + * Backlight Driver for Nvidia 8600/9400M/9600 in Macbook/Macbook Pro * * Copyright (c) Red Hat * Based on code from Pommed: @@ -7,6 +7,10 @@ * Copyright (C) 2006 Felipe Alfaro Solana * Copyright (C) 2007 Julien BLACHE * + * Copyright (c) 2009 Brian Tarricone + * Based on code from nvclock: + * Copyright (C) 2007 Roderick Colenbrander + * * 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 * published by the Free Software Foundation. @@ -24,6 +28,7 @@ #include #include #include +#include static struct backlight_device *mbp_backlight_device; @@ -32,6 +37,10 @@ struct dmi_match_data { /* I/O resource to allocate. */ unsigned long iostart; unsigned long iolen; + /* ... or MMIO region to allocate. */ + unsigned long memstart; + unsigned long memlen; + void *membase; /* Backlight operations structure. */ struct backlight_ops backlight_ops; }; @@ -41,6 +50,8 @@ static int debug; module_param_named(debug, debug, int, 0644); MODULE_PARM_DESC(debug, "Set to one to enable debugging messages."); +static /* const */ struct dmi_match_data *driver_data; + /* * Implementation for MacBooks with Intel chipset. */ @@ -124,10 +135,69 @@ static const struct dmi_match_data nvidia_chipset_data = { }; /* - * DMI matching. + * Implementation for MacBooks with Nvidia chipset. This implementation + * hits the PDISPLAY_SOR0_REGS_BRIGHTNESS register which works while X + * is running, unlike the other method (this is at least true for + * MacBookPro5,5). + * + * I think this method should work for many Nvidia NV50 chipsets (not just + * on MacBookPro5,5, and probably even on non-Apple hardware), but I really + * have no idea, so we'll just enable for the Pro5,5 only for now. + * + * Implementation taken from nvclock's src/backend/nv50.c, which is: + * Copyright(C) 2007 Roderick Colenbrander + * and released under the terms of the GNU General Public License v2 or later. */ -static /* const */ struct dmi_match_data *driver_data; +#define NV_PDISPLAY_OFFSET 0x610000 +#define NV_PDISPLAY_SOR0_REGS_BRIGHTNESS 0xc084 +#define NV_PDISPLAY_SOR0_REGS_BRIGHTNESS_CONTROL_ENABLED 0x80000000 + +/* leave the driver's max_brightness value at 15 to avoid reworking how + * the driver works entirely. we can just scale to the 'real' max of 1024 + */ +#define NV_PDISPLAY_BACKLIGHT_MAX 1024 + +static int nvidia_chipset_send_intensity_mmio(struct backlight_device *bd) +{ + int intensity = bd->props.brightness; + + if (debug) + printk(KERN_DEBUG "mbp_nvidia_bl: setting brightness to %d\n", + intensity); + + writel((intensity * NV_PDISPLAY_BACKLIGHT_MAX / 15) | NV_PDISPLAY_SOR0_REGS_BRIGHTNESS_CONTROL_ENABLED, + driver_data->membase + NV_PDISPLAY_OFFSET + NV_PDISPLAY_SOR0_REGS_BRIGHTNESS); + + return 0; +} + +static int nvidia_chipset_get_intensity_mmio(struct backlight_device *bd) +{ + int intensity; + unsigned int val = readl(driver_data->membase + NV_PDISPLAY_OFFSET + NV_PDISPLAY_SOR0_REGS_BRIGHTNESS); + + if (debug) + printk(KERN_DEBUG "mbp_nvidia_bl: read brightness of %u\n", + val); + + /* convert to level between 0 and 15 */ + intensity = (((double)val / NV_PDISPLAY_BACKLIGHT_MAX * 15) + 0.5); + return intensity; +} + +static const struct dmi_match_data nvidia_chipset_data_mmio = { + .iolen = 0, + .backlight_ops = { + .options = BL_CORE_SUSPENDRESUME, + .get_brightness = nvidia_chipset_get_intensity_mmio, + .update_status = nvidia_chipset_send_intensity_mmio + } +}; + +/* + * DMI matching. + */ static int mbp_dmi_match(const struct dmi_system_id *id) { driver_data = id->driver_data; @@ -136,6 +206,32 @@ static int mbp_dmi_match(const struct dmi_system_id *id) return 1; } +static int mbp_dmi_match_mmio(const struct dmi_system_id *id) +{ + struct pci_dev *pdev = NULL; + + driver_data = id->driver_data; + + printk(KERN_INFO "mbp_nvidia_bl: %s detected\n", id->ident); + + while((pdev = pci_get_device(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, pdev))) { + if((pdev->class >> 16) != PCI_BASE_CLASS_DISPLAY) /* XXX: should we use PCI_CLASS_DISPLAY_VGA? */ + continue; + + driver_data->memstart = pdev->resource[0].start; + driver_data->memlen = pdev->resource[0].end - pdev->resource[0].start + 1; + if (debug) + printk(KERN_DEBUG "Found video device, memstart=0x%lx, memlen=0x%lx\n", + driver_data->memstart, driver_data->memlen); + return 1; + } + + driver_data = NULL; + printk(KERN_ERR "mbp_nvidia_bl: Couldn't find PCI device\n"); + + return 0; +} + static const struct dmi_system_id __initdata mbp_device_table[] = { { .callback = mbp_dmi_match, @@ -191,22 +287,41 @@ static const struct dmi_system_id __initdata mbp_device_table[] = { }, .driver_data = (void *)&nvidia_chipset_data, }, + { + .callback = mbp_dmi_match_mmio, + .ident = "MacBookPro 5,5", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,5"), + }, + .driver_data = (void *)&nvidia_chipset_data_mmio, + }, { } }; static int __init mbp_init(void) { - if (!dmi_check_system(mbp_device_table)) + if (!dmi_check_system(mbp_device_table) || !driver_data) return -ENODEV; - if (!request_region(driver_data->iostart, driver_data->iolen, - "Macbook Pro backlight")) - return -ENXIO; + if (driver_data->iolen != 0) { + if (!request_region(driver_data->iostart, driver_data->iolen, + "Macbook Pro backlight")) + return -ENXIO; + } else if (driver_data->memlen != 0) { + driver_data->membase = ioremap(driver_data->memstart, + driver_data->memlen); + if (!driver_data->membase) + return -ENXIO; + } mbp_backlight_device = backlight_device_register("mbp_backlight", NULL, NULL, &driver_data->backlight_ops); if (IS_ERR(mbp_backlight_device)) { - release_region(driver_data->iostart, driver_data->iolen); + if (driver_data->iolen != 0) + release_region(driver_data->iostart, driver_data->iolen); + else if (driver_data->memlen != 0) + iounmap(driver_data->membase); return PTR_ERR(mbp_backlight_device); } @@ -222,7 +337,10 @@ static void __exit mbp_exit(void) { backlight_device_unregister(mbp_backlight_device); - release_region(driver_data->iostart, driver_data->iolen); + if (driver_data->iolen != 0) + release_region(driver_data->iostart, driver_data->iolen); + else if (driver_data->membase) + iounmap(driver_data->membase); } module_init(mbp_init);