linux/drivers/video/backlight/mbp_nvidia_bl.c
<<
>>
Prefs
   1/*
   2 *  Backlight Driver for Nvidia 8600 in Macbook Pro
   3 *
   4 *  Copyright (c) Red Hat <mjg@redhat.com>
   5 *  Based on code from Pommed:
   6 *  Copyright (C) 2006 Nicolas Boichat <nicolas @boichat.ch>
   7 *  Copyright (C) 2006 Felipe Alfaro Solana <felipe_alfaro @linuxmail.org>
   8 *  Copyright (C) 2007 Julien BLACHE <jb@jblache.org>
   9 *
  10 *  This program is free software; you can redistribute it and/or modify
  11 *  it under the terms of the GNU General Public License version 2 as
  12 *  published by the Free Software Foundation.
  13 *
  14 *  This driver triggers SMIs which cause the firmware to change the
  15 *  backlight brightness. This is icky in many ways, but it's impractical to
  16 *  get at the firmware code in order to figure out what it's actually doing.
  17 */
  18
  19#include <linux/module.h>
  20#include <linux/kernel.h>
  21#include <linux/init.h>
  22#include <linux/platform_device.h>
  23#include <linux/backlight.h>
  24#include <linux/err.h>
  25#include <linux/dmi.h>
  26#include <linux/io.h>
  27
  28static struct backlight_device *mbp_backlight_device;
  29
  30/* Structure to be passed to the DMI_MATCH function. */
  31struct dmi_match_data {
  32        /* I/O resource to allocate. */
  33        unsigned long iostart;
  34        unsigned long iolen;
  35        /* Backlight operations structure. */
  36        struct backlight_ops backlight_ops;
  37};
  38
  39/* Module parameters. */
  40static int debug;
  41module_param_named(debug, debug, int, 0644);
  42MODULE_PARM_DESC(debug, "Set to one to enable debugging messages.");
  43
  44/*
  45 * Implementation for MacBooks with Intel chipset.
  46 */
  47static int intel_chipset_send_intensity(struct backlight_device *bd)
  48{
  49        int intensity = bd->props.brightness;
  50
  51        if (debug)
  52                printk(KERN_DEBUG "mbp_nvidia_bl: setting brightness to %d\n",
  53                       intensity);
  54
  55        outb(0x04 | (intensity << 4), 0xb3);
  56        outb(0xbf, 0xb2);
  57        return 0;
  58}
  59
  60static int intel_chipset_get_intensity(struct backlight_device *bd)
  61{
  62        int intensity;
  63
  64        outb(0x03, 0xb3);
  65        outb(0xbf, 0xb2);
  66        intensity = inb(0xb3) >> 4;
  67
  68        if (debug)
  69                printk(KERN_DEBUG "mbp_nvidia_bl: read brightness of %d\n",
  70                       intensity);
  71
  72        return intensity;
  73}
  74
  75static const struct dmi_match_data intel_chipset_data = {
  76        .iostart = 0xb2,
  77        .iolen = 2,
  78        .backlight_ops  = {
  79                .options        = BL_CORE_SUSPENDRESUME,
  80                .get_brightness = intel_chipset_get_intensity,
  81                .update_status  = intel_chipset_send_intensity,
  82        }
  83};
  84
  85/*
  86 * Implementation for MacBooks with Nvidia chipset.
  87 */
  88static int nvidia_chipset_send_intensity(struct backlight_device *bd)
  89{
  90        int intensity = bd->props.brightness;
  91
  92        if (debug)
  93                printk(KERN_DEBUG "mbp_nvidia_bl: setting brightness to %d\n",
  94                       intensity);
  95
  96        outb(0x04 | (intensity << 4), 0x52f);
  97        outb(0xbf, 0x52e);
  98        return 0;
  99}
 100
 101static int nvidia_chipset_get_intensity(struct backlight_device *bd)
 102{
 103        int intensity;
 104
 105        outb(0x03, 0x52f);
 106        outb(0xbf, 0x52e);
 107        intensity = inb(0x52f) >> 4;
 108
 109        if (debug)
 110                printk(KERN_DEBUG "mbp_nvidia_bl: read brightness of %d\n",
 111                       intensity);
 112
 113        return intensity;
 114}
 115
 116static const struct dmi_match_data nvidia_chipset_data = {
 117        .iostart = 0x52e,
 118        .iolen = 2,
 119        .backlight_ops          = {
 120                .options        = BL_CORE_SUSPENDRESUME,
 121                .get_brightness = nvidia_chipset_get_intensity,
 122                .update_status  = nvidia_chipset_send_intensity
 123        }
 124};
 125
 126/*
 127 * DMI matching.
 128 */
 129static /* const */ struct dmi_match_data *driver_data;
 130
 131static int mbp_dmi_match(const struct dmi_system_id *id)
 132{
 133        driver_data = id->driver_data;
 134
 135        printk(KERN_INFO "mbp_nvidia_bl: %s detected\n", id->ident);
 136        return 1;
 137}
 138
 139static const struct dmi_system_id __initdata mbp_device_table[] = {
 140        {
 141                .callback       = mbp_dmi_match,
 142                .ident          = "MacBookPro 3,1",
 143                .matches        = {
 144                        DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
 145                        DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3,1"),
 146                },
 147                .driver_data    = (void *)&intel_chipset_data,
 148        },
 149        {
 150                .callback       = mbp_dmi_match,
 151                .ident          = "MacBookPro 3,2",
 152                .matches        = {
 153                        DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
 154                        DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3,2"),
 155                },
 156                .driver_data    = (void *)&intel_chipset_data,
 157        },
 158        {
 159                .callback       = mbp_dmi_match,
 160                .ident          = "MacBookPro 4,1",
 161                .matches        = {
 162                        DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
 163                        DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro4,1"),
 164                },
 165                .driver_data    = (void *)&intel_chipset_data,
 166        },
 167        {
 168                .callback       = mbp_dmi_match,
 169                .ident          = "MacBookAir 1,1",
 170                .matches        = {
 171                        DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
 172                        DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir1,1"),
 173                },
 174                .driver_data    = (void *)&intel_chipset_data,
 175        },
 176        {
 177                .callback       = mbp_dmi_match,
 178                .ident          = "MacBook 5,1",
 179                .matches        = {
 180                        DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
 181                        DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5,1"),
 182                },
 183                .driver_data    = (void *)&nvidia_chipset_data,
 184        },
 185        {
 186                .callback       = mbp_dmi_match,
 187                .ident          = "MacBook 5,2",
 188                .matches        = {
 189                        DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
 190                        DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5,2"),
 191                },
 192                .driver_data    = (void *)&nvidia_chipset_data,
 193        },
 194        {
 195                .callback       = mbp_dmi_match,
 196                .ident          = "MacBookAir 2,1",
 197                .matches        = {
 198                        DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
 199                        DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir2,1"),
 200                },
 201                .driver_data    = (void *)&nvidia_chipset_data,
 202        },
 203        {
 204                .callback       = mbp_dmi_match,
 205                .ident          = "MacBookPro 5,1",
 206                .matches        = {
 207                        DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
 208                        DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,1"),
 209                },
 210                .driver_data    = (void *)&nvidia_chipset_data,
 211        },
 212        {
 213                .callback       = mbp_dmi_match,
 214                .ident          = "MacBookPro 5,2",
 215                .matches        = {
 216                        DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
 217                        DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,2"),
 218                },
 219                .driver_data    = (void *)&nvidia_chipset_data,
 220        },
 221        {
 222                .callback       = mbp_dmi_match,
 223                .ident          = "MacBookPro 5,5",
 224                .matches        = {
 225                        DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
 226                        DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,5"),
 227                },
 228                .driver_data    = (void *)&nvidia_chipset_data,
 229        },
 230        { }
 231};
 232
 233static int __init mbp_init(void)
 234{
 235        if (!dmi_check_system(mbp_device_table))
 236                return -ENODEV;
 237
 238        if (!request_region(driver_data->iostart, driver_data->iolen, 
 239                                                "Macbook Pro backlight"))
 240                return -ENXIO;
 241
 242        mbp_backlight_device = backlight_device_register("mbp_backlight",
 243                                        NULL, NULL, &driver_data->backlight_ops);
 244        if (IS_ERR(mbp_backlight_device)) {
 245                release_region(driver_data->iostart, driver_data->iolen);
 246                return PTR_ERR(mbp_backlight_device);
 247        }
 248
 249        mbp_backlight_device->props.max_brightness = 15;
 250        mbp_backlight_device->props.brightness =
 251                driver_data->backlight_ops.get_brightness(mbp_backlight_device);
 252        backlight_update_status(mbp_backlight_device);
 253
 254        return 0;
 255}
 256
 257static void __exit mbp_exit(void)
 258{
 259        backlight_device_unregister(mbp_backlight_device);
 260
 261        release_region(driver_data->iostart, driver_data->iolen);
 262}
 263
 264module_init(mbp_init);
 265module_exit(mbp_exit);
 266
 267MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
 268MODULE_DESCRIPTION("Nvidia-based Macbook Pro Backlight Driver");
 269MODULE_LICENSE("GPL");
 270MODULE_DEVICE_TABLE(dmi, mbp_device_table);
 271