diff options
Diffstat (limited to 'drivers/video/backlight/apple_bl.c')
-rw-r--r-- | drivers/video/backlight/apple_bl.c | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/drivers/video/backlight/apple_bl.c b/drivers/video/backlight/apple_bl.c new file mode 100644 index 000000000000..be98d152b7fd --- /dev/null +++ b/drivers/video/backlight/apple_bl.c | |||
@@ -0,0 +1,241 @@ | |||
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 | |||
28 | static struct backlight_device *apple_backlight_device; | ||
29 | |||
30 | struct hw_data { | ||
31 | /* I/O resource to allocate. */ | ||
32 | unsigned long iostart; | ||
33 | unsigned long iolen; | ||
34 | /* Backlight operations structure. */ | ||
35 | const struct backlight_ops backlight_ops; | ||
36 | void (*set_brightness)(int); | ||
37 | }; | ||
38 | |||
39 | static const struct hw_data *hw_data; | ||
40 | |||
41 | #define DRIVER "apple_backlight: " | ||
42 | |||
43 | /* Module parameters. */ | ||
44 | static int debug; | ||
45 | module_param_named(debug, debug, int, 0644); | ||
46 | MODULE_PARM_DESC(debug, "Set to one to enable debugging messages."); | ||
47 | |||
48 | /* | ||
49 | * Implementation for machines with Intel chipset. | ||
50 | */ | ||
51 | static void intel_chipset_set_brightness(int intensity) | ||
52 | { | ||
53 | outb(0x04 | (intensity << 4), 0xb3); | ||
54 | outb(0xbf, 0xb2); | ||
55 | } | ||
56 | |||
57 | static int intel_chipset_send_intensity(struct backlight_device *bd) | ||
58 | { | ||
59 | int intensity = bd->props.brightness; | ||
60 | |||
61 | if (debug) | ||
62 | printk(KERN_DEBUG DRIVER "setting brightness to %d\n", | ||
63 | intensity); | ||
64 | |||
65 | intel_chipset_set_brightness(intensity); | ||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | static int intel_chipset_get_intensity(struct backlight_device *bd) | ||
70 | { | ||
71 | int intensity; | ||
72 | |||
73 | outb(0x03, 0xb3); | ||
74 | outb(0xbf, 0xb2); | ||
75 | intensity = inb(0xb3) >> 4; | ||
76 | |||
77 | if (debug) | ||
78 | printk(KERN_DEBUG DRIVER "read brightness of %d\n", | ||
79 | 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 | printk(KERN_DEBUG DRIVER "setting brightness to %d\n", | ||
110 | intensity); | ||
111 | |||
112 | nvidia_chipset_set_brightness(intensity); | ||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | static int nvidia_chipset_get_intensity(struct backlight_device *bd) | ||
117 | { | ||
118 | int intensity; | ||
119 | |||
120 | outb(0x03, 0x52f); | ||
121 | outb(0xbf, 0x52e); | ||
122 | intensity = inb(0x52f) >> 4; | ||
123 | |||
124 | if (debug) | ||
125 | printk(KERN_DEBUG DRIVER "read brightness of %d\n", | ||
126 | intensity); | ||
127 | |||
128 | return intensity; | ||
129 | } | ||
130 | |||
131 | static const struct hw_data nvidia_chipset_data = { | ||
132 | .iostart = 0x52e, | ||
133 | .iolen = 2, | ||
134 | .backlight_ops = { | ||
135 | .options = BL_CORE_SUSPENDRESUME, | ||
136 | .get_brightness = nvidia_chipset_get_intensity, | ||
137 | .update_status = nvidia_chipset_send_intensity | ||
138 | }, | ||
139 | .set_brightness = nvidia_chipset_set_brightness, | ||
140 | }; | ||
141 | |||
142 | static int __devinit apple_bl_add(struct acpi_device *dev) | ||
143 | { | ||
144 | struct backlight_properties props; | ||
145 | struct pci_dev *host; | ||
146 | int intensity; | ||
147 | |||
148 | host = pci_get_bus_and_slot(0, 0); | ||
149 | |||
150 | if (!host) { | ||
151 | printk(KERN_ERR DRIVER "unable to find PCI host\n"); | ||
152 | return -ENODEV; | ||
153 | } | ||
154 | |||
155 | if (host->vendor == PCI_VENDOR_ID_INTEL) | ||
156 | hw_data = &intel_chipset_data; | ||
157 | else if (host->vendor == PCI_VENDOR_ID_NVIDIA) | ||
158 | hw_data = &nvidia_chipset_data; | ||
159 | |||
160 | pci_dev_put(host); | ||
161 | |||
162 | if (!hw_data) { | ||
163 | printk(KERN_ERR DRIVER "unknown hardware\n"); | ||
164 | return -ENODEV; | ||
165 | } | ||
166 | |||
167 | /* Check that the hardware responds - this may not work under EFI */ | ||
168 | |||
169 | intensity = hw_data->backlight_ops.get_brightness(NULL); | ||
170 | |||
171 | if (!intensity) { | ||
172 | hw_data->set_brightness(1); | ||
173 | if (!hw_data->backlight_ops.get_brightness(NULL)) | ||
174 | return -ENODEV; | ||
175 | |||
176 | hw_data->set_brightness(0); | ||
177 | } | ||
178 | |||
179 | if (!request_region(hw_data->iostart, hw_data->iolen, | ||
180 | "Apple backlight")) | ||
181 | return -ENXIO; | ||
182 | |||
183 | memset(&props, 0, sizeof(struct backlight_properties)); | ||
184 | props.type = BACKLIGHT_PLATFORM; | ||
185 | props.max_brightness = 15; | ||
186 | apple_backlight_device = backlight_device_register("apple_backlight", | ||
187 | NULL, NULL, &hw_data->backlight_ops, &props); | ||
188 | |||
189 | if (IS_ERR(apple_backlight_device)) { | ||
190 | release_region(hw_data->iostart, hw_data->iolen); | ||
191 | return PTR_ERR(apple_backlight_device); | ||
192 | } | ||
193 | |||
194 | apple_backlight_device->props.brightness = | ||
195 | hw_data->backlight_ops.get_brightness(apple_backlight_device); | ||
196 | backlight_update_status(apple_backlight_device); | ||
197 | |||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | static int __devexit apple_bl_remove(struct acpi_device *dev, int type) | ||
202 | { | ||
203 | backlight_device_unregister(apple_backlight_device); | ||
204 | |||
205 | release_region(hw_data->iostart, hw_data->iolen); | ||
206 | hw_data = NULL; | ||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | static const struct acpi_device_id apple_bl_ids[] = { | ||
211 | {"APP0002", 0}, | ||
212 | {"", 0}, | ||
213 | }; | ||
214 | |||
215 | static struct acpi_driver apple_bl_driver = { | ||
216 | .name = "Apple backlight", | ||
217 | .ids = apple_bl_ids, | ||
218 | .ops = { | ||
219 | .add = apple_bl_add, | ||
220 | .remove = apple_bl_remove, | ||
221 | }, | ||
222 | }; | ||
223 | |||
224 | static int __init apple_bl_init(void) | ||
225 | { | ||
226 | return acpi_bus_register_driver(&apple_bl_driver); | ||
227 | } | ||
228 | |||
229 | static void __exit apple_bl_exit(void) | ||
230 | { | ||
231 | acpi_bus_unregister_driver(&apple_bl_driver); | ||
232 | } | ||
233 | |||
234 | module_init(apple_bl_init); | ||
235 | module_exit(apple_bl_exit); | ||
236 | |||
237 | MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); | ||
238 | MODULE_DESCRIPTION("Apple Backlight Driver"); | ||
239 | MODULE_LICENSE("GPL"); | ||
240 | MODULE_DEVICE_TABLE(acpi, apple_bl_ids); | ||
241 | MODULE_ALIAS("mbp_nvidia_bl"); | ||