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