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