1 /* 2 * Backlight Driver for Intel-based Apples 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/backlight.h> 23 #include <linux/err.h> 24 #include <linux/io.h> 25 #include <linux/pci.h> 26 #include <linux/acpi.h> 27 #include <linux/atomic.h> 28 #include <linux/apple_bl.h> 29 30 static struct backlight_device *apple_backlight_device; 31 32 struct hw_data { 33 /* I/O resource to allocate. */ 34 unsigned long iostart; 35 unsigned long iolen; 36 /* Backlight operations structure. */ 37 const struct backlight_ops backlight_ops; 38 void (*set_brightness)(int); 39 }; 40 41 static const struct hw_data *hw_data; 42 43 #define DRIVER "apple_backlight: " 44 45 /* Module parameters. */ 46 static int debug; 47 module_param_named(debug, debug, int, 0644); 48 MODULE_PARM_DESC(debug, "Set to one to enable debugging messages."); 49 50 /* 51 * Implementation for machines with Intel chipset. 52 */ 53 static void intel_chipset_set_brightness(int intensity) 54 { 55 outb(0x04 | (intensity << 4), 0xb3); 56 outb(0xbf, 0xb2); 57 } 58 59 static int intel_chipset_send_intensity(struct backlight_device *bd) 60 { 61 int intensity = bd->props.brightness; 62 63 if (debug) 64 printk(KERN_DEBUG DRIVER "setting brightness to %d\n", 65 intensity); 66 67 intel_chipset_set_brightness(intensity); 68 return 0; 69 } 70 71 static int intel_chipset_get_intensity(struct backlight_device *bd) 72 { 73 int intensity; 74 75 outb(0x03, 0xb3); 76 outb(0xbf, 0xb2); 77 intensity = inb(0xb3) >> 4; 78 79 if (debug) 80 printk(KERN_DEBUG DRIVER "read brightness of %d\n", 81 intensity); 82 83 return intensity; 84 } 85 86 static const struct hw_data intel_chipset_data = { 87 .iostart = 0xb2, 88 .iolen = 2, 89 .backlight_ops = { 90 .options = BL_CORE_SUSPENDRESUME, 91 .get_brightness = intel_chipset_get_intensity, 92 .update_status = intel_chipset_send_intensity, 93 }, 94 .set_brightness = intel_chipset_set_brightness, 95 }; 96 97 /* 98 * Implementation for machines with Nvidia chipset. 99 */ 100 static void nvidia_chipset_set_brightness(int intensity) 101 { 102 outb(0x04 | (intensity << 4), 0x52f); 103 outb(0xbf, 0x52e); 104 } 105 106 static int nvidia_chipset_send_intensity(struct backlight_device *bd) 107 { 108 int intensity = bd->props.brightness; 109 110 if (debug) 111 printk(KERN_DEBUG DRIVER "setting brightness to %d\n", 112 intensity); 113 114 nvidia_chipset_set_brightness(intensity); 115 return 0; 116 } 117 118 static int nvidia_chipset_get_intensity(struct backlight_device *bd) 119 { 120 int intensity; 121 122 outb(0x03, 0x52f); 123 outb(0xbf, 0x52e); 124 intensity = inb(0x52f) >> 4; 125 126 if (debug) 127 printk(KERN_DEBUG DRIVER "read brightness of %d\n", 128 intensity); 129 130 return intensity; 131 } 132 133 static const struct hw_data nvidia_chipset_data = { 134 .iostart = 0x52e, 135 .iolen = 2, 136 .backlight_ops = { 137 .options = BL_CORE_SUSPENDRESUME, 138 .get_brightness = nvidia_chipset_get_intensity, 139 .update_status = nvidia_chipset_send_intensity 140 }, 141 .set_brightness = nvidia_chipset_set_brightness, 142 }; 143 144 static int __devinit apple_bl_add(struct acpi_device *dev) 145 { 146 struct backlight_properties props; 147 struct pci_dev *host; 148 int intensity; 149 150 host = pci_get_bus_and_slot(0, 0); 151 152 if (!host) { 153 printk(KERN_ERR DRIVER "unable to find PCI host\n"); 154 return -ENODEV; 155 } 156 157 if (host->vendor == PCI_VENDOR_ID_INTEL) 158 hw_data = &intel_chipset_data; 159 else if (host->vendor == PCI_VENDOR_ID_NVIDIA) 160 hw_data = &nvidia_chipset_data; 161 162 pci_dev_put(host); 163 164 if (!hw_data) { 165 printk(KERN_ERR DRIVER "unknown hardware\n"); 166 return -ENODEV; 167 } 168 169 /* Check that the hardware responds - this may not work under EFI */ 170 171 intensity = hw_data->backlight_ops.get_brightness(NULL); 172 173 if (!intensity) { 174 hw_data->set_brightness(1); 175 if (!hw_data->backlight_ops.get_brightness(NULL)) 176 return -ENODEV; 177 178 hw_data->set_brightness(0); 179 } 180 181 if (!request_region(hw_data->iostart, hw_data->iolen, 182 "Apple backlight")) 183 return -ENXIO; 184 185 memset(&props, 0, sizeof(struct backlight_properties)); 186 props.type = BACKLIGHT_PLATFORM; 187 props.max_brightness = 15; 188 apple_backlight_device = backlight_device_register("apple_backlight", 189 NULL, NULL, &hw_data->backlight_ops, &props); 190 191 if (IS_ERR(apple_backlight_device)) { 192 release_region(hw_data->iostart, hw_data->iolen); 193 return PTR_ERR(apple_backlight_device); 194 } 195 196 apple_backlight_device->props.brightness = 197 hw_data->backlight_ops.get_brightness(apple_backlight_device); 198 backlight_update_status(apple_backlight_device); 199 200 return 0; 201 } 202 203 static int __devexit apple_bl_remove(struct acpi_device *dev, int type) 204 { 205 backlight_device_unregister(apple_backlight_device); 206 207 release_region(hw_data->iostart, hw_data->iolen); 208 hw_data = NULL; 209 return 0; 210 } 211 212 static const struct acpi_device_id apple_bl_ids[] = { 213 {"APP0002", 0}, 214 {"", 0}, 215 }; 216 217 static struct acpi_driver apple_bl_driver = { 218 .name = "Apple backlight", 219 .ids = apple_bl_ids, 220 .ops = { 221 .add = apple_bl_add, 222 .remove = apple_bl_remove, 223 }, 224 }; 225 226 static atomic_t apple_bl_registered = ATOMIC_INIT(0); 227 228 int apple_bl_register(void) 229 { 230 if (atomic_xchg(&apple_bl_registered, 1) == 0) 231 return acpi_bus_register_driver(&apple_bl_driver); 232 233 return 0; 234 } 235 EXPORT_SYMBOL_GPL(apple_bl_register); 236 237 void apple_bl_unregister(void) 238 { 239 if (atomic_xchg(&apple_bl_registered, 0) == 1) 240 acpi_bus_unregister_driver(&apple_bl_driver); 241 } 242 EXPORT_SYMBOL_GPL(apple_bl_unregister); 243 244 static int __init apple_bl_init(void) 245 { 246 return apple_bl_register(); 247 } 248 249 static void __exit apple_bl_exit(void) 250 { 251 apple_bl_unregister(); 252 } 253 254 module_init(apple_bl_init); 255 module_exit(apple_bl_exit); 256 257 MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); 258 MODULE_DESCRIPTION("Apple Backlight Driver"); 259 MODULE_LICENSE("GPL"); 260 MODULE_DEVICE_TABLE(acpi, apple_bl_ids); 261 MODULE_ALIAS("mbp_nvidia_bl"); 262