diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-30 14:52:52 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-30 14:52:52 -0400 |
commit | 08acd4f8af42affd8cbed81cc1b69fa12ddb213f (patch) | |
tree | 988d15db6233b20db6a500cd5f590c6d2041462d /drivers/misc | |
parent | ccf2779544eecfcc5447e2028d1029b6d4ff7bb6 (diff) | |
parent | 008238b54ac2350babf195084ecedbcf7851a202 (diff) |
Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6
* 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6: (179 commits)
ACPI: Fix acpi_processor_idle and idle= boot parameters interaction
acpi: fix section mismatch warning in pnpacpi
intel_menlo: fix build warning
ACPI: Cleanup: Remove unneeded, multiple local dummy variables
ACPI: video - fix permissions on some proc entries
ACPI: video - properly handle errors when registering proc elements
ACPI: video - do not store invalid entries in attached_array list
ACPI: re-name acpi_pm_ops to acpi_suspend_ops
ACER_WMI/ASUS_LAPTOP: fix build bug
thinkpad_acpi: fix possible NULL pointer dereference if kstrdup failed
ACPI: check a return value correctly in acpi_power_get_context()
#if 0 acpi/bay.c:eject_removable_drive()
eeepc-laptop: add hwmon fan control
eeepc-laptop: add backlight
eeepc-laptop: add base driver
ACPI: thinkpad-acpi: bump up version to 0.20
ACPI: thinkpad-acpi: fix selects in Kconfig
ACPI: thinkpad-acpi: use a private workqueue
ACPI: thinkpad-acpi: fluff really minor fix
ACPI: thinkpad-acpi: use uppercase for "LED" on user documentation
...
Fixed conflicts in drivers/acpi/video.c and drivers/misc/intel_menlow.c
manually.
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/Kconfig | 21 | ||||
-rw-r--r-- | drivers/misc/Makefile | 3 | ||||
-rw-r--r-- | drivers/misc/eeepc-laptop.c | 666 | ||||
-rw-r--r-- | drivers/misc/thinkpad_acpi.c | 765 |
4 files changed, 1260 insertions, 195 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 297a48f85446..636af2862308 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig | |||
@@ -140,6 +140,7 @@ config ACER_WMI | |||
140 | depends on EXPERIMENTAL | 140 | depends on EXPERIMENTAL |
141 | depends on ACPI | 141 | depends on ACPI |
142 | depends on LEDS_CLASS | 142 | depends on LEDS_CLASS |
143 | depends on NEW_LEDS | ||
143 | depends on BACKLIGHT_CLASS_DEVICE | 144 | depends on BACKLIGHT_CLASS_DEVICE |
144 | depends on SERIO_I8042 | 145 | depends on SERIO_I8042 |
145 | select ACPI_WMI | 146 | select ACPI_WMI |
@@ -160,6 +161,7 @@ config ASUS_LAPTOP | |||
160 | depends on ACPI | 161 | depends on ACPI |
161 | depends on EXPERIMENTAL && !ACPI_ASUS | 162 | depends on EXPERIMENTAL && !ACPI_ASUS |
162 | depends on LEDS_CLASS | 163 | depends on LEDS_CLASS |
164 | depends on NEW_LEDS | ||
163 | depends on BACKLIGHT_CLASS_DEVICE | 165 | depends on BACKLIGHT_CLASS_DEVICE |
164 | ---help--- | 166 | ---help--- |
165 | This is the new Linux driver for Asus laptops. It may also support some | 167 | This is the new Linux driver for Asus laptops. It may also support some |
@@ -241,10 +243,13 @@ config SONYPI_COMPAT | |||
241 | config THINKPAD_ACPI | 243 | config THINKPAD_ACPI |
242 | tristate "ThinkPad ACPI Laptop Extras" | 244 | tristate "ThinkPad ACPI Laptop Extras" |
243 | depends on X86 && ACPI | 245 | depends on X86 && ACPI |
246 | select BACKLIGHT_LCD_SUPPORT | ||
244 | select BACKLIGHT_CLASS_DEVICE | 247 | select BACKLIGHT_CLASS_DEVICE |
245 | select HWMON | 248 | select HWMON |
246 | select NVRAM | 249 | select NVRAM |
247 | depends on INPUT | 250 | select INPUT |
251 | select NEW_LEDS | ||
252 | select LEDS_CLASS | ||
248 | ---help--- | 253 | ---help--- |
249 | This is a driver for the IBM and Lenovo ThinkPad laptops. It adds | 254 | This is a driver for the IBM and Lenovo ThinkPad laptops. It adds |
250 | support for Fn-Fx key combinations, Bluetooth control, video | 255 | support for Fn-Fx key combinations, Bluetooth control, video |
@@ -344,6 +349,7 @@ config ATMEL_SSC | |||
344 | config INTEL_MENLOW | 349 | config INTEL_MENLOW |
345 | tristate "Thermal Management driver for Intel menlow platform" | 350 | tristate "Thermal Management driver for Intel menlow platform" |
346 | depends on ACPI_THERMAL | 351 | depends on ACPI_THERMAL |
352 | select THERMAL | ||
347 | depends on X86 | 353 | depends on X86 |
348 | ---help--- | 354 | ---help--- |
349 | ACPI thermal management enhancement driver on | 355 | ACPI thermal management enhancement driver on |
@@ -351,6 +357,19 @@ config INTEL_MENLOW | |||
351 | 357 | ||
352 | If unsure, say N. | 358 | If unsure, say N. |
353 | 359 | ||
360 | config EEEPC_LAPTOP | ||
361 | tristate "Eee PC Hotkey Driver (EXPERIMENTAL)" | ||
362 | depends on X86 | ||
363 | depends on ACPI | ||
364 | depends on BACKLIGHT_CLASS_DEVICE | ||
365 | depends on HWMON | ||
366 | depends on EXPERIMENTAL | ||
367 | ---help--- | ||
368 | This driver supports the Fn-Fx keys on Eee PC laptops. | ||
369 | It also adds the ability to switch camera/wlan on/off. | ||
370 | |||
371 | If you have an Eee PC laptop, say Y or M here. | ||
372 | |||
354 | config ENCLOSURE_SERVICES | 373 | config ENCLOSURE_SERVICES |
355 | tristate "Enclosure Services" | 374 | tristate "Enclosure Services" |
356 | default n | 375 | default n |
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 5914da434854..1952875a272e 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile | |||
@@ -7,7 +7,8 @@ obj-$(CONFIG_IBM_ASM) += ibmasm/ | |||
7 | obj-$(CONFIG_HDPU_FEATURES) += hdpuftrs/ | 7 | obj-$(CONFIG_HDPU_FEATURES) += hdpuftrs/ |
8 | obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o | 8 | obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o |
9 | obj-$(CONFIG_ACER_WMI) += acer-wmi.o | 9 | obj-$(CONFIG_ACER_WMI) += acer-wmi.o |
10 | obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o | 10 | obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o |
11 | obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o | ||
11 | obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o | 12 | obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o |
12 | obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o | 13 | obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o |
13 | obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o | 14 | obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o |
diff --git a/drivers/misc/eeepc-laptop.c b/drivers/misc/eeepc-laptop.c new file mode 100644 index 000000000000..6d727609097f --- /dev/null +++ b/drivers/misc/eeepc-laptop.c | |||
@@ -0,0 +1,666 @@ | |||
1 | /* | ||
2 | * eepc-laptop.c - Asus Eee PC extras | ||
3 | * | ||
4 | * Based on asus_acpi.c as patched for the Eee PC by Asus: | ||
5 | * ftp://ftp.asus.com/pub/ASUS/EeePC/701/ASUS_ACPI_071126.rar | ||
6 | * Based on eee.c from eeepc-linux | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | */ | ||
18 | |||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/types.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <linux/backlight.h> | ||
25 | #include <linux/fb.h> | ||
26 | #include <linux/hwmon.h> | ||
27 | #include <linux/hwmon-sysfs.h> | ||
28 | #include <acpi/acpi_drivers.h> | ||
29 | #include <acpi/acpi_bus.h> | ||
30 | #include <linux/uaccess.h> | ||
31 | |||
32 | #define EEEPC_LAPTOP_VERSION "0.1" | ||
33 | |||
34 | #define EEEPC_HOTK_NAME "Eee PC Hotkey Driver" | ||
35 | #define EEEPC_HOTK_FILE "eeepc" | ||
36 | #define EEEPC_HOTK_CLASS "hotkey" | ||
37 | #define EEEPC_HOTK_DEVICE_NAME "Hotkey" | ||
38 | #define EEEPC_HOTK_HID "ASUS010" | ||
39 | |||
40 | #define EEEPC_LOG EEEPC_HOTK_FILE ": " | ||
41 | #define EEEPC_ERR KERN_ERR EEEPC_LOG | ||
42 | #define EEEPC_WARNING KERN_WARNING EEEPC_LOG | ||
43 | #define EEEPC_NOTICE KERN_NOTICE EEEPC_LOG | ||
44 | #define EEEPC_INFO KERN_INFO EEEPC_LOG | ||
45 | |||
46 | /* | ||
47 | * Definitions for Asus EeePC | ||
48 | */ | ||
49 | #define NOTIFY_WLAN_ON 0x10 | ||
50 | #define NOTIFY_BRN_MIN 0x20 | ||
51 | #define NOTIFY_BRN_MAX 0x2f | ||
52 | |||
53 | enum { | ||
54 | DISABLE_ASL_WLAN = 0x0001, | ||
55 | DISABLE_ASL_BLUETOOTH = 0x0002, | ||
56 | DISABLE_ASL_IRDA = 0x0004, | ||
57 | DISABLE_ASL_CAMERA = 0x0008, | ||
58 | DISABLE_ASL_TV = 0x0010, | ||
59 | DISABLE_ASL_GPS = 0x0020, | ||
60 | DISABLE_ASL_DISPLAYSWITCH = 0x0040, | ||
61 | DISABLE_ASL_MODEM = 0x0080, | ||
62 | DISABLE_ASL_CARDREADER = 0x0100 | ||
63 | }; | ||
64 | |||
65 | enum { | ||
66 | CM_ASL_WLAN = 0, | ||
67 | CM_ASL_BLUETOOTH, | ||
68 | CM_ASL_IRDA, | ||
69 | CM_ASL_1394, | ||
70 | CM_ASL_CAMERA, | ||
71 | CM_ASL_TV, | ||
72 | CM_ASL_GPS, | ||
73 | CM_ASL_DVDROM, | ||
74 | CM_ASL_DISPLAYSWITCH, | ||
75 | CM_ASL_PANELBRIGHT, | ||
76 | CM_ASL_BIOSFLASH, | ||
77 | CM_ASL_ACPIFLASH, | ||
78 | CM_ASL_CPUFV, | ||
79 | CM_ASL_CPUTEMPERATURE, | ||
80 | CM_ASL_FANCPU, | ||
81 | CM_ASL_FANCHASSIS, | ||
82 | CM_ASL_USBPORT1, | ||
83 | CM_ASL_USBPORT2, | ||
84 | CM_ASL_USBPORT3, | ||
85 | CM_ASL_MODEM, | ||
86 | CM_ASL_CARDREADER, | ||
87 | CM_ASL_LID | ||
88 | }; | ||
89 | |||
90 | const char *cm_getv[] = { | ||
91 | "WLDG", NULL, NULL, NULL, | ||
92 | "CAMG", NULL, NULL, NULL, | ||
93 | NULL, "PBLG", NULL, NULL, | ||
94 | "CFVG", NULL, NULL, NULL, | ||
95 | "USBG", NULL, NULL, "MODG", | ||
96 | "CRDG", "LIDG" | ||
97 | }; | ||
98 | |||
99 | const char *cm_setv[] = { | ||
100 | "WLDS", NULL, NULL, NULL, | ||
101 | "CAMS", NULL, NULL, NULL, | ||
102 | "SDSP", "PBLS", "HDPS", NULL, | ||
103 | "CFVS", NULL, NULL, NULL, | ||
104 | "USBG", NULL, NULL, "MODS", | ||
105 | "CRDS", NULL | ||
106 | }; | ||
107 | |||
108 | #define EEEPC_EC "\\_SB.PCI0.SBRG.EC0." | ||
109 | |||
110 | #define EEEPC_EC_FAN_PWM EEEPC_EC "SC02" /* Fan PWM duty cycle (%) */ | ||
111 | #define EEEPC_EC_SC02 0x63 | ||
112 | #define EEEPC_EC_FAN_HRPM EEEPC_EC "SC05" /* High byte, fan speed (RPM) */ | ||
113 | #define EEEPC_EC_FAN_LRPM EEEPC_EC "SC06" /* Low byte, fan speed (RPM) */ | ||
114 | #define EEEPC_EC_FAN_CTRL EEEPC_EC "SFB3" /* Byte containing SF25 */ | ||
115 | #define EEEPC_EC_SFB3 0xD3 | ||
116 | |||
117 | /* | ||
118 | * This is the main structure, we can use it to store useful information | ||
119 | * about the hotk device | ||
120 | */ | ||
121 | struct eeepc_hotk { | ||
122 | struct acpi_device *device; /* the device we are in */ | ||
123 | acpi_handle handle; /* the handle of the hotk device */ | ||
124 | u32 cm_supported; /* the control methods supported | ||
125 | by this BIOS */ | ||
126 | uint init_flag; /* Init flags */ | ||
127 | u16 event_count[128]; /* count for each event */ | ||
128 | }; | ||
129 | |||
130 | /* The actual device the driver binds to */ | ||
131 | static struct eeepc_hotk *ehotk; | ||
132 | |||
133 | /* Platform device/driver */ | ||
134 | static struct platform_driver platform_driver = { | ||
135 | .driver = { | ||
136 | .name = EEEPC_HOTK_FILE, | ||
137 | .owner = THIS_MODULE, | ||
138 | } | ||
139 | }; | ||
140 | |||
141 | static struct platform_device *platform_device; | ||
142 | |||
143 | /* | ||
144 | * The hotkey driver declaration | ||
145 | */ | ||
146 | static int eeepc_hotk_add(struct acpi_device *device); | ||
147 | static int eeepc_hotk_remove(struct acpi_device *device, int type); | ||
148 | |||
149 | static const struct acpi_device_id eeepc_device_ids[] = { | ||
150 | {EEEPC_HOTK_HID, 0}, | ||
151 | {"", 0}, | ||
152 | }; | ||
153 | MODULE_DEVICE_TABLE(acpi, eeepc_device_ids); | ||
154 | |||
155 | static struct acpi_driver eeepc_hotk_driver = { | ||
156 | .name = EEEPC_HOTK_NAME, | ||
157 | .class = EEEPC_HOTK_CLASS, | ||
158 | .ids = eeepc_device_ids, | ||
159 | .ops = { | ||
160 | .add = eeepc_hotk_add, | ||
161 | .remove = eeepc_hotk_remove, | ||
162 | }, | ||
163 | }; | ||
164 | |||
165 | /* The backlight device /sys/class/backlight */ | ||
166 | static struct backlight_device *eeepc_backlight_device; | ||
167 | |||
168 | /* The hwmon device */ | ||
169 | static struct device *eeepc_hwmon_device; | ||
170 | |||
171 | /* | ||
172 | * The backlight class declaration | ||
173 | */ | ||
174 | static int read_brightness(struct backlight_device *bd); | ||
175 | static int update_bl_status(struct backlight_device *bd); | ||
176 | static struct backlight_ops eeepcbl_ops = { | ||
177 | .get_brightness = read_brightness, | ||
178 | .update_status = update_bl_status, | ||
179 | }; | ||
180 | |||
181 | MODULE_AUTHOR("Corentin Chary, Eric Cooper"); | ||
182 | MODULE_DESCRIPTION(EEEPC_HOTK_NAME); | ||
183 | MODULE_LICENSE("GPL"); | ||
184 | |||
185 | /* | ||
186 | * ACPI Helpers | ||
187 | */ | ||
188 | static int write_acpi_int(acpi_handle handle, const char *method, int val, | ||
189 | struct acpi_buffer *output) | ||
190 | { | ||
191 | struct acpi_object_list params; | ||
192 | union acpi_object in_obj; | ||
193 | acpi_status status; | ||
194 | |||
195 | params.count = 1; | ||
196 | params.pointer = &in_obj; | ||
197 | in_obj.type = ACPI_TYPE_INTEGER; | ||
198 | in_obj.integer.value = val; | ||
199 | |||
200 | status = acpi_evaluate_object(handle, (char *)method, ¶ms, output); | ||
201 | return (status == AE_OK ? 0 : -1); | ||
202 | } | ||
203 | |||
204 | static int read_acpi_int(acpi_handle handle, const char *method, int *val) | ||
205 | { | ||
206 | acpi_status status; | ||
207 | ulong result; | ||
208 | |||
209 | status = acpi_evaluate_integer(handle, (char *)method, NULL, &result); | ||
210 | if (ACPI_FAILURE(status)) { | ||
211 | *val = -1; | ||
212 | return -1; | ||
213 | } else { | ||
214 | *val = result; | ||
215 | return 0; | ||
216 | } | ||
217 | } | ||
218 | |||
219 | static int set_acpi(int cm, int value) | ||
220 | { | ||
221 | if (ehotk->cm_supported & (0x1 << cm)) { | ||
222 | const char *method = cm_setv[cm]; | ||
223 | if (method == NULL) | ||
224 | return -ENODEV; | ||
225 | if (write_acpi_int(ehotk->handle, method, value, NULL)) | ||
226 | printk(EEEPC_WARNING "Error writing %s\n", method); | ||
227 | } | ||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | static int get_acpi(int cm) | ||
232 | { | ||
233 | int value = -1; | ||
234 | if ((ehotk->cm_supported & (0x1 << cm))) { | ||
235 | const char *method = cm_getv[cm]; | ||
236 | if (method == NULL) | ||
237 | return -ENODEV; | ||
238 | if (read_acpi_int(ehotk->handle, method, &value)) | ||
239 | printk(EEEPC_WARNING "Error reading %s\n", method); | ||
240 | } | ||
241 | return value; | ||
242 | } | ||
243 | |||
244 | /* | ||
245 | * Backlight | ||
246 | */ | ||
247 | static int read_brightness(struct backlight_device *bd) | ||
248 | { | ||
249 | return get_acpi(CM_ASL_PANELBRIGHT); | ||
250 | } | ||
251 | |||
252 | static int set_brightness(struct backlight_device *bd, int value) | ||
253 | { | ||
254 | value = max(0, min(15, value)); | ||
255 | return set_acpi(CM_ASL_PANELBRIGHT, value); | ||
256 | } | ||
257 | |||
258 | static int update_bl_status(struct backlight_device *bd) | ||
259 | { | ||
260 | return set_brightness(bd, bd->props.brightness); | ||
261 | } | ||
262 | |||
263 | /* | ||
264 | * Sys helpers | ||
265 | */ | ||
266 | static int parse_arg(const char *buf, unsigned long count, int *val) | ||
267 | { | ||
268 | if (!count) | ||
269 | return 0; | ||
270 | if (sscanf(buf, "%i", val) != 1) | ||
271 | return -EINVAL; | ||
272 | return count; | ||
273 | } | ||
274 | |||
275 | static ssize_t store_sys_acpi(int cm, const char *buf, size_t count) | ||
276 | { | ||
277 | int rv, value; | ||
278 | |||
279 | rv = parse_arg(buf, count, &value); | ||
280 | if (rv > 0) | ||
281 | set_acpi(cm, value); | ||
282 | return rv; | ||
283 | } | ||
284 | |||
285 | static ssize_t show_sys_acpi(int cm, char *buf) | ||
286 | { | ||
287 | return sprintf(buf, "%d\n", get_acpi(cm)); | ||
288 | } | ||
289 | |||
290 | #define EEEPC_CREATE_DEVICE_ATTR(_name, _cm) \ | ||
291 | static ssize_t show_##_name(struct device *dev, \ | ||
292 | struct device_attribute *attr, \ | ||
293 | char *buf) \ | ||
294 | { \ | ||
295 | return show_sys_acpi(_cm, buf); \ | ||
296 | } \ | ||
297 | static ssize_t store_##_name(struct device *dev, \ | ||
298 | struct device_attribute *attr, \ | ||
299 | const char *buf, size_t count) \ | ||
300 | { \ | ||
301 | return store_sys_acpi(_cm, buf, count); \ | ||
302 | } \ | ||
303 | static struct device_attribute dev_attr_##_name = { \ | ||
304 | .attr = { \ | ||
305 | .name = __stringify(_name), \ | ||
306 | .mode = 0644 }, \ | ||
307 | .show = show_##_name, \ | ||
308 | .store = store_##_name, \ | ||
309 | } | ||
310 | |||
311 | EEEPC_CREATE_DEVICE_ATTR(camera, CM_ASL_CAMERA); | ||
312 | EEEPC_CREATE_DEVICE_ATTR(cardr, CM_ASL_CARDREADER); | ||
313 | EEEPC_CREATE_DEVICE_ATTR(disp, CM_ASL_DISPLAYSWITCH); | ||
314 | EEEPC_CREATE_DEVICE_ATTR(wlan, CM_ASL_WLAN); | ||
315 | |||
316 | static struct attribute *platform_attributes[] = { | ||
317 | &dev_attr_camera.attr, | ||
318 | &dev_attr_cardr.attr, | ||
319 | &dev_attr_disp.attr, | ||
320 | &dev_attr_wlan.attr, | ||
321 | NULL | ||
322 | }; | ||
323 | |||
324 | static struct attribute_group platform_attribute_group = { | ||
325 | .attrs = platform_attributes | ||
326 | }; | ||
327 | |||
328 | /* | ||
329 | * Hotkey functions | ||
330 | */ | ||
331 | static int eeepc_hotk_check(void) | ||
332 | { | ||
333 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
334 | int result; | ||
335 | |||
336 | result = acpi_bus_get_status(ehotk->device); | ||
337 | if (result) | ||
338 | return result; | ||
339 | if (ehotk->device->status.present) { | ||
340 | if (write_acpi_int(ehotk->handle, "INIT", ehotk->init_flag, | ||
341 | &buffer)) { | ||
342 | printk(EEEPC_ERR "Hotkey initialization failed\n"); | ||
343 | return -ENODEV; | ||
344 | } else { | ||
345 | printk(EEEPC_NOTICE "Hotkey init flags 0x%x\n", | ||
346 | ehotk->init_flag); | ||
347 | } | ||
348 | /* get control methods supported */ | ||
349 | if (read_acpi_int(ehotk->handle, "CMSG" | ||
350 | , &ehotk->cm_supported)) { | ||
351 | printk(EEEPC_ERR | ||
352 | "Get control methods supported failed\n"); | ||
353 | return -ENODEV; | ||
354 | } else { | ||
355 | printk(EEEPC_INFO | ||
356 | "Get control methods supported: 0x%x\n", | ||
357 | ehotk->cm_supported); | ||
358 | } | ||
359 | } else { | ||
360 | printk(EEEPC_ERR "Hotkey device not present, aborting\n"); | ||
361 | return -EINVAL; | ||
362 | } | ||
363 | return 0; | ||
364 | } | ||
365 | |||
366 | static void notify_wlan(u32 *event) | ||
367 | { | ||
368 | /* if DISABLE_ASL_WLAN is set, the notify code for fn+f2 | ||
369 | will always be 0x10 */ | ||
370 | if (ehotk->cm_supported & (0x1 << CM_ASL_WLAN)) { | ||
371 | const char *method = cm_getv[CM_ASL_WLAN]; | ||
372 | int value; | ||
373 | if (read_acpi_int(ehotk->handle, method, &value)) | ||
374 | printk(EEEPC_WARNING "Error reading %s\n", | ||
375 | method); | ||
376 | else if (value == 1) | ||
377 | *event = 0x11; | ||
378 | } | ||
379 | } | ||
380 | |||
381 | static void notify_brn(void) | ||
382 | { | ||
383 | struct backlight_device *bd = eeepc_backlight_device; | ||
384 | bd->props.brightness = read_brightness(bd); | ||
385 | } | ||
386 | |||
387 | static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data) | ||
388 | { | ||
389 | if (!ehotk) | ||
390 | return; | ||
391 | if (event == NOTIFY_WLAN_ON && (DISABLE_ASL_WLAN & ehotk->init_flag)) | ||
392 | notify_wlan(&event); | ||
393 | if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) | ||
394 | notify_brn(); | ||
395 | acpi_bus_generate_proc_event(ehotk->device, event, | ||
396 | ehotk->event_count[event % 128]++); | ||
397 | } | ||
398 | |||
399 | static int eeepc_hotk_add(struct acpi_device *device) | ||
400 | { | ||
401 | acpi_status status = AE_OK; | ||
402 | int result; | ||
403 | |||
404 | if (!device) | ||
405 | return -EINVAL; | ||
406 | printk(EEEPC_NOTICE EEEPC_HOTK_NAME "\n"); | ||
407 | ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL); | ||
408 | if (!ehotk) | ||
409 | return -ENOMEM; | ||
410 | ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH; | ||
411 | ehotk->handle = device->handle; | ||
412 | strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME); | ||
413 | strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS); | ||
414 | acpi_driver_data(device) = ehotk; | ||
415 | ehotk->device = device; | ||
416 | result = eeepc_hotk_check(); | ||
417 | if (result) | ||
418 | goto end; | ||
419 | status = acpi_install_notify_handler(ehotk->handle, ACPI_SYSTEM_NOTIFY, | ||
420 | eeepc_hotk_notify, ehotk); | ||
421 | if (ACPI_FAILURE(status)) | ||
422 | printk(EEEPC_ERR "Error installing notify handler\n"); | ||
423 | end: | ||
424 | if (result) { | ||
425 | kfree(ehotk); | ||
426 | ehotk = NULL; | ||
427 | } | ||
428 | return result; | ||
429 | } | ||
430 | |||
431 | static int eeepc_hotk_remove(struct acpi_device *device, int type) | ||
432 | { | ||
433 | acpi_status status = 0; | ||
434 | |||
435 | if (!device || !acpi_driver_data(device)) | ||
436 | return -EINVAL; | ||
437 | status = acpi_remove_notify_handler(ehotk->handle, ACPI_SYSTEM_NOTIFY, | ||
438 | eeepc_hotk_notify); | ||
439 | if (ACPI_FAILURE(status)) | ||
440 | printk(EEEPC_ERR "Error removing notify handler\n"); | ||
441 | kfree(ehotk); | ||
442 | return 0; | ||
443 | } | ||
444 | |||
445 | /* | ||
446 | * Hwmon | ||
447 | */ | ||
448 | static int eeepc_get_fan_pwm(void) | ||
449 | { | ||
450 | int value = 0; | ||
451 | |||
452 | read_acpi_int(NULL, EEEPC_EC_FAN_PWM, &value); | ||
453 | return (value); | ||
454 | } | ||
455 | |||
456 | static void eeepc_set_fan_pwm(int value) | ||
457 | { | ||
458 | value = SENSORS_LIMIT(value, 0, 100); | ||
459 | ec_write(EEEPC_EC_SC02, value); | ||
460 | } | ||
461 | |||
462 | static int eeepc_get_fan_rpm(void) | ||
463 | { | ||
464 | int high = 0; | ||
465 | int low = 0; | ||
466 | |||
467 | read_acpi_int(NULL, EEEPC_EC_FAN_HRPM, &high); | ||
468 | read_acpi_int(NULL, EEEPC_EC_FAN_LRPM, &low); | ||
469 | return (high << 8 | low); | ||
470 | } | ||
471 | |||
472 | static int eeepc_get_fan_ctrl(void) | ||
473 | { | ||
474 | int value = 0; | ||
475 | |||
476 | read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value); | ||
477 | return ((value & 0x02 ? 1 : 0)); | ||
478 | } | ||
479 | |||
480 | static void eeepc_set_fan_ctrl(int manual) | ||
481 | { | ||
482 | int value = 0; | ||
483 | |||
484 | read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value); | ||
485 | if (manual) | ||
486 | value |= 0x02; | ||
487 | else | ||
488 | value &= ~0x02; | ||
489 | ec_write(EEEPC_EC_SFB3, value); | ||
490 | } | ||
491 | |||
492 | static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count) | ||
493 | { | ||
494 | int rv, value; | ||
495 | |||
496 | rv = parse_arg(buf, count, &value); | ||
497 | if (rv > 0) | ||
498 | set(value); | ||
499 | return rv; | ||
500 | } | ||
501 | |||
502 | static ssize_t show_sys_hwmon(int (*get)(void), char *buf) | ||
503 | { | ||
504 | return sprintf(buf, "%d\n", get()); | ||
505 | } | ||
506 | |||
507 | #define EEEPC_CREATE_SENSOR_ATTR(_name, _mode, _set, _get) \ | ||
508 | static ssize_t show_##_name(struct device *dev, \ | ||
509 | struct device_attribute *attr, \ | ||
510 | char *buf) \ | ||
511 | { \ | ||
512 | return show_sys_hwmon(_set, buf); \ | ||
513 | } \ | ||
514 | static ssize_t store_##_name(struct device *dev, \ | ||
515 | struct device_attribute *attr, \ | ||
516 | const char *buf, size_t count) \ | ||
517 | { \ | ||
518 | return store_sys_hwmon(_get, buf, count); \ | ||
519 | } \ | ||
520 | static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0); | ||
521 | |||
522 | EEEPC_CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, eeepc_get_fan_rpm, NULL); | ||
523 | EEEPC_CREATE_SENSOR_ATTR(fan1_pwm, S_IRUGO | S_IWUSR, | ||
524 | eeepc_get_fan_pwm, eeepc_set_fan_pwm); | ||
525 | EEEPC_CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, | ||
526 | eeepc_get_fan_ctrl, eeepc_set_fan_ctrl); | ||
527 | |||
528 | static struct attribute *hwmon_attributes[] = { | ||
529 | &sensor_dev_attr_fan1_pwm.dev_attr.attr, | ||
530 | &sensor_dev_attr_fan1_input.dev_attr.attr, | ||
531 | &sensor_dev_attr_pwm1_enable.dev_attr.attr, | ||
532 | NULL | ||
533 | }; | ||
534 | |||
535 | static struct attribute_group hwmon_attribute_group = { | ||
536 | .attrs = hwmon_attributes | ||
537 | }; | ||
538 | |||
539 | /* | ||
540 | * exit/init | ||
541 | */ | ||
542 | static void eeepc_backlight_exit(void) | ||
543 | { | ||
544 | if (eeepc_backlight_device) | ||
545 | backlight_device_unregister(eeepc_backlight_device); | ||
546 | eeepc_backlight_device = NULL; | ||
547 | } | ||
548 | |||
549 | static void eeepc_hwmon_exit(void) | ||
550 | { | ||
551 | struct device *hwmon; | ||
552 | |||
553 | hwmon = eeepc_hwmon_device; | ||
554 | if (!hwmon) | ||
555 | return ; | ||
556 | hwmon_device_unregister(hwmon); | ||
557 | sysfs_remove_group(&hwmon->kobj, | ||
558 | &hwmon_attribute_group); | ||
559 | eeepc_hwmon_device = NULL; | ||
560 | } | ||
561 | |||
562 | static void __exit eeepc_laptop_exit(void) | ||
563 | { | ||
564 | eeepc_backlight_exit(); | ||
565 | eeepc_hwmon_exit(); | ||
566 | acpi_bus_unregister_driver(&eeepc_hotk_driver); | ||
567 | sysfs_remove_group(&platform_device->dev.kobj, | ||
568 | &platform_attribute_group); | ||
569 | platform_device_unregister(platform_device); | ||
570 | platform_driver_unregister(&platform_driver); | ||
571 | } | ||
572 | |||
573 | static int eeepc_backlight_init(struct device *dev) | ||
574 | { | ||
575 | struct backlight_device *bd; | ||
576 | |||
577 | bd = backlight_device_register(EEEPC_HOTK_FILE, dev, | ||
578 | NULL, &eeepcbl_ops); | ||
579 | if (IS_ERR(bd)) { | ||
580 | printk(EEEPC_ERR | ||
581 | "Could not register eeepc backlight device\n"); | ||
582 | eeepc_backlight_device = NULL; | ||
583 | return PTR_ERR(bd); | ||
584 | } | ||
585 | eeepc_backlight_device = bd; | ||
586 | bd->props.max_brightness = 15; | ||
587 | bd->props.brightness = read_brightness(NULL); | ||
588 | bd->props.power = FB_BLANK_UNBLANK; | ||
589 | backlight_update_status(bd); | ||
590 | return 0; | ||
591 | } | ||
592 | |||
593 | static int eeepc_hwmon_init(struct device *dev) | ||
594 | { | ||
595 | struct device *hwmon; | ||
596 | int result; | ||
597 | |||
598 | hwmon = hwmon_device_register(dev); | ||
599 | if (IS_ERR(hwmon)) { | ||
600 | printk(EEEPC_ERR | ||
601 | "Could not register eeepc hwmon device\n"); | ||
602 | eeepc_hwmon_device = NULL; | ||
603 | return PTR_ERR(hwmon); | ||
604 | } | ||
605 | eeepc_hwmon_device = hwmon; | ||
606 | result = sysfs_create_group(&hwmon->kobj, | ||
607 | &hwmon_attribute_group); | ||
608 | if (result) | ||
609 | eeepc_hwmon_exit(); | ||
610 | return result; | ||
611 | } | ||
612 | |||
613 | static int __init eeepc_laptop_init(void) | ||
614 | { | ||
615 | struct device *dev; | ||
616 | int result; | ||
617 | |||
618 | if (acpi_disabled) | ||
619 | return -ENODEV; | ||
620 | result = acpi_bus_register_driver(&eeepc_hotk_driver); | ||
621 | if (result < 0) | ||
622 | return result; | ||
623 | if (!ehotk) { | ||
624 | acpi_bus_unregister_driver(&eeepc_hotk_driver); | ||
625 | return -ENODEV; | ||
626 | } | ||
627 | dev = acpi_get_physical_device(ehotk->device->handle); | ||
628 | result = eeepc_backlight_init(dev); | ||
629 | if (result) | ||
630 | goto fail_backlight; | ||
631 | result = eeepc_hwmon_init(dev); | ||
632 | if (result) | ||
633 | goto fail_hwmon; | ||
634 | /* Register platform stuff */ | ||
635 | result = platform_driver_register(&platform_driver); | ||
636 | if (result) | ||
637 | goto fail_platform_driver; | ||
638 | platform_device = platform_device_alloc(EEEPC_HOTK_FILE, -1); | ||
639 | if (!platform_device) { | ||
640 | result = -ENOMEM; | ||
641 | goto fail_platform_device1; | ||
642 | } | ||
643 | result = platform_device_add(platform_device); | ||
644 | if (result) | ||
645 | goto fail_platform_device2; | ||
646 | result = sysfs_create_group(&platform_device->dev.kobj, | ||
647 | &platform_attribute_group); | ||
648 | if (result) | ||
649 | goto fail_sysfs; | ||
650 | return 0; | ||
651 | fail_sysfs: | ||
652 | platform_device_del(platform_device); | ||
653 | fail_platform_device2: | ||
654 | platform_device_put(platform_device); | ||
655 | fail_platform_device1: | ||
656 | platform_driver_unregister(&platform_driver); | ||
657 | fail_platform_driver: | ||
658 | eeepc_hwmon_exit(); | ||
659 | fail_hwmon: | ||
660 | eeepc_backlight_exit(); | ||
661 | fail_backlight: | ||
662 | return result; | ||
663 | } | ||
664 | |||
665 | module_init(eeepc_laptop_init); | ||
666 | module_exit(eeepc_laptop_exit); | ||
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 6cb781262f94..3f28f6eabdbf 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c | |||
@@ -21,7 +21,7 @@ | |||
21 | * 02110-1301, USA. | 21 | * 02110-1301, USA. |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #define TPACPI_VERSION "0.19" | 24 | #define TPACPI_VERSION "0.20" |
25 | #define TPACPI_SYSFS_VERSION 0x020200 | 25 | #define TPACPI_SYSFS_VERSION 0x020200 |
26 | 26 | ||
27 | /* | 27 | /* |
@@ -67,6 +67,7 @@ | |||
67 | #include <linux/hwmon.h> | 67 | #include <linux/hwmon.h> |
68 | #include <linux/hwmon-sysfs.h> | 68 | #include <linux/hwmon-sysfs.h> |
69 | #include <linux/input.h> | 69 | #include <linux/input.h> |
70 | #include <linux/leds.h> | ||
70 | #include <asm/uaccess.h> | 71 | #include <asm/uaccess.h> |
71 | 72 | ||
72 | #include <linux/dmi.h> | 73 | #include <linux/dmi.h> |
@@ -85,6 +86,8 @@ | |||
85 | #define TP_CMOS_VOLUME_MUTE 2 | 86 | #define TP_CMOS_VOLUME_MUTE 2 |
86 | #define TP_CMOS_BRIGHTNESS_UP 4 | 87 | #define TP_CMOS_BRIGHTNESS_UP 4 |
87 | #define TP_CMOS_BRIGHTNESS_DOWN 5 | 88 | #define TP_CMOS_BRIGHTNESS_DOWN 5 |
89 | #define TP_CMOS_THINKLIGHT_ON 12 | ||
90 | #define TP_CMOS_THINKLIGHT_OFF 13 | ||
88 | 91 | ||
89 | /* NVRAM Addresses */ | 92 | /* NVRAM Addresses */ |
90 | enum tp_nvram_addr { | 93 | enum tp_nvram_addr { |
@@ -133,8 +136,12 @@ enum { | |||
133 | #define TPACPI_PROC_DIR "ibm" | 136 | #define TPACPI_PROC_DIR "ibm" |
134 | #define TPACPI_ACPI_EVENT_PREFIX "ibm" | 137 | #define TPACPI_ACPI_EVENT_PREFIX "ibm" |
135 | #define TPACPI_DRVR_NAME TPACPI_FILE | 138 | #define TPACPI_DRVR_NAME TPACPI_FILE |
139 | #define TPACPI_DRVR_SHORTNAME "tpacpi" | ||
136 | #define TPACPI_HWMON_DRVR_NAME TPACPI_NAME "_hwmon" | 140 | #define TPACPI_HWMON_DRVR_NAME TPACPI_NAME "_hwmon" |
137 | 141 | ||
142 | #define TPACPI_NVRAM_KTHREAD_NAME "ktpacpi_nvramd" | ||
143 | #define TPACPI_WORKQUEUE_NAME "ktpacpid" | ||
144 | |||
138 | #define TPACPI_MAX_ACPI_ARGS 3 | 145 | #define TPACPI_MAX_ACPI_ARGS 3 |
139 | 146 | ||
140 | /* Debugging */ | 147 | /* Debugging */ |
@@ -225,6 +232,7 @@ static struct { | |||
225 | u32 light:1; | 232 | u32 light:1; |
226 | u32 light_status:1; | 233 | u32 light_status:1; |
227 | u32 bright_16levels:1; | 234 | u32 bright_16levels:1; |
235 | u32 bright_acpimode:1; | ||
228 | u32 wan:1; | 236 | u32 wan:1; |
229 | u32 fan_ctrl_status_undef:1; | 237 | u32 fan_ctrl_status_undef:1; |
230 | u32 input_device_registered:1; | 238 | u32 input_device_registered:1; |
@@ -236,6 +244,11 @@ static struct { | |||
236 | u32 hotkey_poll_active:1; | 244 | u32 hotkey_poll_active:1; |
237 | } tp_features; | 245 | } tp_features; |
238 | 246 | ||
247 | static struct { | ||
248 | u16 hotkey_mask_ff:1; | ||
249 | u16 bright_cmos_ec_unsync:1; | ||
250 | } tp_warned; | ||
251 | |||
239 | struct thinkpad_id_data { | 252 | struct thinkpad_id_data { |
240 | unsigned int vendor; /* ThinkPad vendor: | 253 | unsigned int vendor; /* ThinkPad vendor: |
241 | * PCI_VENDOR_ID_IBM/PCI_VENDOR_ID_LENOVO */ | 254 | * PCI_VENDOR_ID_IBM/PCI_VENDOR_ID_LENOVO */ |
@@ -246,7 +259,8 @@ struct thinkpad_id_data { | |||
246 | u16 bios_model; /* Big Endian, TP-1Y = 0x5931, 0 = unknown */ | 259 | u16 bios_model; /* Big Endian, TP-1Y = 0x5931, 0 = unknown */ |
247 | u16 ec_model; | 260 | u16 ec_model; |
248 | 261 | ||
249 | char *model_str; | 262 | char *model_str; /* ThinkPad T43 */ |
263 | char *nummodel_str; /* 9384A9C for a 9384-A9C model */ | ||
250 | }; | 264 | }; |
251 | static struct thinkpad_id_data thinkpad_id; | 265 | static struct thinkpad_id_data thinkpad_id; |
252 | 266 | ||
@@ -259,6 +273,16 @@ static enum { | |||
259 | static int experimental; | 273 | static int experimental; |
260 | static u32 dbg_level; | 274 | static u32 dbg_level; |
261 | 275 | ||
276 | static struct workqueue_struct *tpacpi_wq; | ||
277 | |||
278 | /* Special LED class that can defer work */ | ||
279 | struct tpacpi_led_classdev { | ||
280 | struct led_classdev led_classdev; | ||
281 | struct work_struct work; | ||
282 | enum led_brightness new_brightness; | ||
283 | unsigned int led; | ||
284 | }; | ||
285 | |||
262 | /**************************************************************************** | 286 | /**************************************************************************** |
263 | **************************************************************************** | 287 | **************************************************************************** |
264 | * | 288 | * |
@@ -807,6 +831,80 @@ static int parse_strtoul(const char *buf, | |||
807 | return 0; | 831 | return 0; |
808 | } | 832 | } |
809 | 833 | ||
834 | static int __init tpacpi_query_bcl_levels(acpi_handle handle) | ||
835 | { | ||
836 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
837 | union acpi_object *obj; | ||
838 | int rc; | ||
839 | |||
840 | if (ACPI_SUCCESS(acpi_evaluate_object(handle, NULL, NULL, &buffer))) { | ||
841 | obj = (union acpi_object *)buffer.pointer; | ||
842 | if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) { | ||
843 | printk(TPACPI_ERR "Unknown _BCL data, " | ||
844 | "please report this to %s\n", TPACPI_MAIL); | ||
845 | rc = 0; | ||
846 | } else { | ||
847 | rc = obj->package.count; | ||
848 | } | ||
849 | } else { | ||
850 | return 0; | ||
851 | } | ||
852 | |||
853 | kfree(buffer.pointer); | ||
854 | return rc; | ||
855 | } | ||
856 | |||
857 | static acpi_status __init tpacpi_acpi_walk_find_bcl(acpi_handle handle, | ||
858 | u32 lvl, void *context, void **rv) | ||
859 | { | ||
860 | char name[ACPI_PATH_SEGMENT_LENGTH]; | ||
861 | struct acpi_buffer buffer = { sizeof(name), &name }; | ||
862 | |||
863 | if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) && | ||
864 | !strncmp("_BCL", name, sizeof(name) - 1)) { | ||
865 | BUG_ON(!rv || !*rv); | ||
866 | **(int **)rv = tpacpi_query_bcl_levels(handle); | ||
867 | return AE_CTRL_TERMINATE; | ||
868 | } else { | ||
869 | return AE_OK; | ||
870 | } | ||
871 | } | ||
872 | |||
873 | /* | ||
874 | * Returns 0 (no ACPI _BCL or _BCL invalid), or size of brightness map | ||
875 | */ | ||
876 | static int __init tpacpi_check_std_acpi_brightness_support(void) | ||
877 | { | ||
878 | int status; | ||
879 | int bcl_levels = 0; | ||
880 | void *bcl_ptr = &bcl_levels; | ||
881 | |||
882 | if (!vid_handle) { | ||
883 | TPACPI_ACPIHANDLE_INIT(vid); | ||
884 | } | ||
885 | if (!vid_handle) | ||
886 | return 0; | ||
887 | |||
888 | /* | ||
889 | * Search for a _BCL method, and execute it. This is safe on all | ||
890 | * ThinkPads, and as a side-effect, _BCL will place a Lenovo Vista | ||
891 | * BIOS in ACPI backlight control mode. We do NOT have to care | ||
892 | * about calling the _BCL method in an enabled video device, any | ||
893 | * will do for our purposes. | ||
894 | */ | ||
895 | |||
896 | status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3, | ||
897 | tpacpi_acpi_walk_find_bcl, NULL, | ||
898 | &bcl_ptr); | ||
899 | |||
900 | if (ACPI_SUCCESS(status) && bcl_levels > 2) { | ||
901 | tp_features.bright_acpimode = 1; | ||
902 | return (bcl_levels - 2); | ||
903 | } | ||
904 | |||
905 | return 0; | ||
906 | } | ||
907 | |||
810 | /************************************************************************* | 908 | /************************************************************************* |
811 | * thinkpad-acpi driver attributes | 909 | * thinkpad-acpi driver attributes |
812 | */ | 910 | */ |
@@ -909,12 +1007,14 @@ static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm) | |||
909 | thinkpad_id.ec_version_str : "unknown"); | 1007 | thinkpad_id.ec_version_str : "unknown"); |
910 | 1008 | ||
911 | if (thinkpad_id.vendor && thinkpad_id.model_str) | 1009 | if (thinkpad_id.vendor && thinkpad_id.model_str) |
912 | printk(TPACPI_INFO "%s %s\n", | 1010 | printk(TPACPI_INFO "%s %s, model %s\n", |
913 | (thinkpad_id.vendor == PCI_VENDOR_ID_IBM) ? | 1011 | (thinkpad_id.vendor == PCI_VENDOR_ID_IBM) ? |
914 | "IBM" : ((thinkpad_id.vendor == | 1012 | "IBM" : ((thinkpad_id.vendor == |
915 | PCI_VENDOR_ID_LENOVO) ? | 1013 | PCI_VENDOR_ID_LENOVO) ? |
916 | "Lenovo" : "Unknown vendor"), | 1014 | "Lenovo" : "Unknown vendor"), |
917 | thinkpad_id.model_str); | 1015 | thinkpad_id.model_str, |
1016 | (thinkpad_id.nummodel_str) ? | ||
1017 | thinkpad_id.nummodel_str : "unknown"); | ||
918 | 1018 | ||
919 | return 0; | 1019 | return 0; |
920 | } | 1020 | } |
@@ -1107,6 +1207,19 @@ static int hotkey_mask_set(u32 mask) | |||
1107 | int rc = 0; | 1207 | int rc = 0; |
1108 | 1208 | ||
1109 | if (tp_features.hotkey_mask) { | 1209 | if (tp_features.hotkey_mask) { |
1210 | if (!tp_warned.hotkey_mask_ff && | ||
1211 | (mask == 0xffff || mask == 0xffffff || | ||
1212 | mask == 0xffffffff)) { | ||
1213 | tp_warned.hotkey_mask_ff = 1; | ||
1214 | printk(TPACPI_NOTICE | ||
1215 | "setting the hotkey mask to 0x%08x is likely " | ||
1216 | "not the best way to go about it\n", mask); | ||
1217 | printk(TPACPI_NOTICE | ||
1218 | "please consider using the driver defaults, " | ||
1219 | "and refer to up-to-date thinkpad-acpi " | ||
1220 | "documentation\n"); | ||
1221 | } | ||
1222 | |||
1110 | HOTKEY_CONFIG_CRITICAL_START | 1223 | HOTKEY_CONFIG_CRITICAL_START |
1111 | for (i = 0; i < 32; i++) { | 1224 | for (i = 0; i < 32; i++) { |
1112 | u32 m = 1 << i; | 1225 | u32 m = 1 << i; |
@@ -1427,8 +1540,7 @@ static void hotkey_poll_setup(int may_warn) | |||
1427 | (tpacpi_inputdev->users > 0 || hotkey_report_mode < 2)) { | 1540 | (tpacpi_inputdev->users > 0 || hotkey_report_mode < 2)) { |
1428 | if (!tpacpi_hotkey_task) { | 1541 | if (!tpacpi_hotkey_task) { |
1429 | tpacpi_hotkey_task = kthread_run(hotkey_kthread, | 1542 | tpacpi_hotkey_task = kthread_run(hotkey_kthread, |
1430 | NULL, | 1543 | NULL, TPACPI_NVRAM_KTHREAD_NAME); |
1431 | TPACPI_FILE "d"); | ||
1432 | if (IS_ERR(tpacpi_hotkey_task)) { | 1544 | if (IS_ERR(tpacpi_hotkey_task)) { |
1433 | tpacpi_hotkey_task = NULL; | 1545 | tpacpi_hotkey_task = NULL; |
1434 | printk(TPACPI_ERR | 1546 | printk(TPACPI_ERR |
@@ -1887,6 +1999,9 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
1887 | KEY_UNKNOWN, /* 0x0D: FN+INSERT */ | 1999 | KEY_UNKNOWN, /* 0x0D: FN+INSERT */ |
1888 | KEY_UNKNOWN, /* 0x0E: FN+DELETE */ | 2000 | KEY_UNKNOWN, /* 0x0E: FN+DELETE */ |
1889 | 2001 | ||
2002 | /* These either have to go through ACPI video, or | ||
2003 | * act like in the IBM ThinkPads, so don't ever | ||
2004 | * enable them by default */ | ||
1890 | KEY_RESERVED, /* 0x0F: FN+HOME (brightness up) */ | 2005 | KEY_RESERVED, /* 0x0F: FN+HOME (brightness up) */ |
1891 | KEY_RESERVED, /* 0x10: FN+END (brightness down) */ | 2006 | KEY_RESERVED, /* 0x10: FN+END (brightness down) */ |
1892 | 2007 | ||
@@ -2091,6 +2206,32 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
2091 | set_bit(SW_TABLET_MODE, tpacpi_inputdev->swbit); | 2206 | set_bit(SW_TABLET_MODE, tpacpi_inputdev->swbit); |
2092 | } | 2207 | } |
2093 | 2208 | ||
2209 | /* Do not issue duplicate brightness change events to | ||
2210 | * userspace */ | ||
2211 | if (!tp_features.bright_acpimode) | ||
2212 | /* update bright_acpimode... */ | ||
2213 | tpacpi_check_std_acpi_brightness_support(); | ||
2214 | |||
2215 | if (tp_features.bright_acpimode) { | ||
2216 | printk(TPACPI_INFO | ||
2217 | "This ThinkPad has standard ACPI backlight " | ||
2218 | "brightness control, supported by the ACPI " | ||
2219 | "video driver\n"); | ||
2220 | printk(TPACPI_NOTICE | ||
2221 | "Disabling thinkpad-acpi brightness events " | ||
2222 | "by default...\n"); | ||
2223 | |||
2224 | /* The hotkey_reserved_mask change below is not | ||
2225 | * necessary while the keys are at KEY_RESERVED in the | ||
2226 | * default map, but better safe than sorry, leave it | ||
2227 | * here as a marker of what we have to do, especially | ||
2228 | * when we finally become able to set this at runtime | ||
2229 | * on response to X.org requests */ | ||
2230 | hotkey_reserved_mask |= | ||
2231 | (1 << TP_ACPI_HOTKEYSCAN_FNHOME) | ||
2232 | | (1 << TP_ACPI_HOTKEYSCAN_FNEND); | ||
2233 | } | ||
2234 | |||
2094 | dbg_printk(TPACPI_DBG_INIT, | 2235 | dbg_printk(TPACPI_DBG_INIT, |
2095 | "enabling hot key handling\n"); | 2236 | "enabling hot key handling\n"); |
2096 | res = hotkey_status_set(1); | 2237 | res = hotkey_status_set(1); |
@@ -3110,13 +3251,82 @@ static struct ibm_struct video_driver_data = { | |||
3110 | TPACPI_HANDLE(lght, root, "\\LGHT"); /* A21e, A2xm/p, T20-22, X20-21 */ | 3251 | TPACPI_HANDLE(lght, root, "\\LGHT"); /* A21e, A2xm/p, T20-22, X20-21 */ |
3111 | TPACPI_HANDLE(ledb, ec, "LEDB"); /* G4x */ | 3252 | TPACPI_HANDLE(ledb, ec, "LEDB"); /* G4x */ |
3112 | 3253 | ||
3254 | static int light_get_status(void) | ||
3255 | { | ||
3256 | int status = 0; | ||
3257 | |||
3258 | if (tp_features.light_status) { | ||
3259 | if (!acpi_evalf(ec_handle, &status, "KBLT", "d")) | ||
3260 | return -EIO; | ||
3261 | return (!!status); | ||
3262 | } | ||
3263 | |||
3264 | return -ENXIO; | ||
3265 | } | ||
3266 | |||
3267 | static int light_set_status(int status) | ||
3268 | { | ||
3269 | int rc; | ||
3270 | |||
3271 | if (tp_features.light) { | ||
3272 | if (cmos_handle) { | ||
3273 | rc = acpi_evalf(cmos_handle, NULL, NULL, "vd", | ||
3274 | (status)? | ||
3275 | TP_CMOS_THINKLIGHT_ON : | ||
3276 | TP_CMOS_THINKLIGHT_OFF); | ||
3277 | } else { | ||
3278 | rc = acpi_evalf(lght_handle, NULL, NULL, "vd", | ||
3279 | (status)? 1 : 0); | ||
3280 | } | ||
3281 | return (rc)? 0 : -EIO; | ||
3282 | } | ||
3283 | |||
3284 | return -ENXIO; | ||
3285 | } | ||
3286 | |||
3287 | static void light_set_status_worker(struct work_struct *work) | ||
3288 | { | ||
3289 | struct tpacpi_led_classdev *data = | ||
3290 | container_of(work, struct tpacpi_led_classdev, work); | ||
3291 | |||
3292 | if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING)) | ||
3293 | light_set_status((data->new_brightness != LED_OFF)); | ||
3294 | } | ||
3295 | |||
3296 | static void light_sysfs_set(struct led_classdev *led_cdev, | ||
3297 | enum led_brightness brightness) | ||
3298 | { | ||
3299 | struct tpacpi_led_classdev *data = | ||
3300 | container_of(led_cdev, | ||
3301 | struct tpacpi_led_classdev, | ||
3302 | led_classdev); | ||
3303 | data->new_brightness = brightness; | ||
3304 | queue_work(tpacpi_wq, &data->work); | ||
3305 | } | ||
3306 | |||
3307 | static enum led_brightness light_sysfs_get(struct led_classdev *led_cdev) | ||
3308 | { | ||
3309 | return (light_get_status() == 1)? LED_FULL : LED_OFF; | ||
3310 | } | ||
3311 | |||
3312 | static struct tpacpi_led_classdev tpacpi_led_thinklight = { | ||
3313 | .led_classdev = { | ||
3314 | .name = "tpacpi::thinklight", | ||
3315 | .brightness_set = &light_sysfs_set, | ||
3316 | .brightness_get = &light_sysfs_get, | ||
3317 | } | ||
3318 | }; | ||
3319 | |||
3113 | static int __init light_init(struct ibm_init_struct *iibm) | 3320 | static int __init light_init(struct ibm_init_struct *iibm) |
3114 | { | 3321 | { |
3322 | int rc = 0; | ||
3323 | |||
3115 | vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n"); | 3324 | vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n"); |
3116 | 3325 | ||
3117 | TPACPI_ACPIHANDLE_INIT(ledb); | 3326 | TPACPI_ACPIHANDLE_INIT(ledb); |
3118 | TPACPI_ACPIHANDLE_INIT(lght); | 3327 | TPACPI_ACPIHANDLE_INIT(lght); |
3119 | TPACPI_ACPIHANDLE_INIT(cmos); | 3328 | TPACPI_ACPIHANDLE_INIT(cmos); |
3329 | INIT_WORK(&tpacpi_led_thinklight.work, light_set_status_worker); | ||
3120 | 3330 | ||
3121 | /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */ | 3331 | /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */ |
3122 | tp_features.light = (cmos_handle || lght_handle) && !ledb_handle; | 3332 | tp_features.light = (cmos_handle || lght_handle) && !ledb_handle; |
@@ -3130,13 +3340,31 @@ static int __init light_init(struct ibm_init_struct *iibm) | |||
3130 | vdbg_printk(TPACPI_DBG_INIT, "light is %s\n", | 3340 | vdbg_printk(TPACPI_DBG_INIT, "light is %s\n", |
3131 | str_supported(tp_features.light)); | 3341 | str_supported(tp_features.light)); |
3132 | 3342 | ||
3133 | return (tp_features.light)? 0 : 1; | 3343 | if (tp_features.light) { |
3344 | rc = led_classdev_register(&tpacpi_pdev->dev, | ||
3345 | &tpacpi_led_thinklight.led_classdev); | ||
3346 | } | ||
3347 | |||
3348 | if (rc < 0) { | ||
3349 | tp_features.light = 0; | ||
3350 | tp_features.light_status = 0; | ||
3351 | } else { | ||
3352 | rc = (tp_features.light)? 0 : 1; | ||
3353 | } | ||
3354 | return rc; | ||
3355 | } | ||
3356 | |||
3357 | static void light_exit(void) | ||
3358 | { | ||
3359 | led_classdev_unregister(&tpacpi_led_thinklight.led_classdev); | ||
3360 | if (work_pending(&tpacpi_led_thinklight.work)) | ||
3361 | flush_workqueue(tpacpi_wq); | ||
3134 | } | 3362 | } |
3135 | 3363 | ||
3136 | static int light_read(char *p) | 3364 | static int light_read(char *p) |
3137 | { | 3365 | { |
3138 | int len = 0; | 3366 | int len = 0; |
3139 | int status = 0; | 3367 | int status; |
3140 | 3368 | ||
3141 | if (!tp_features.light) { | 3369 | if (!tp_features.light) { |
3142 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 3370 | len += sprintf(p + len, "status:\t\tnot supported\n"); |
@@ -3144,8 +3372,9 @@ static int light_read(char *p) | |||
3144 | len += sprintf(p + len, "status:\t\tunknown\n"); | 3372 | len += sprintf(p + len, "status:\t\tunknown\n"); |
3145 | len += sprintf(p + len, "commands:\ton, off\n"); | 3373 | len += sprintf(p + len, "commands:\ton, off\n"); |
3146 | } else { | 3374 | } else { |
3147 | if (!acpi_evalf(ec_handle, &status, "KBLT", "d")) | 3375 | status = light_get_status(); |
3148 | return -EIO; | 3376 | if (status < 0) |
3377 | return status; | ||
3149 | len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0)); | 3378 | len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0)); |
3150 | len += sprintf(p + len, "commands:\ton, off\n"); | 3379 | len += sprintf(p + len, "commands:\ton, off\n"); |
3151 | } | 3380 | } |
@@ -3155,37 +3384,29 @@ static int light_read(char *p) | |||
3155 | 3384 | ||
3156 | static int light_write(char *buf) | 3385 | static int light_write(char *buf) |
3157 | { | 3386 | { |
3158 | int cmos_cmd, lght_cmd; | ||
3159 | char *cmd; | 3387 | char *cmd; |
3160 | int success; | 3388 | int newstatus = 0; |
3161 | 3389 | ||
3162 | if (!tp_features.light) | 3390 | if (!tp_features.light) |
3163 | return -ENODEV; | 3391 | return -ENODEV; |
3164 | 3392 | ||
3165 | while ((cmd = next_cmd(&buf))) { | 3393 | while ((cmd = next_cmd(&buf))) { |
3166 | if (strlencmp(cmd, "on") == 0) { | 3394 | if (strlencmp(cmd, "on") == 0) { |
3167 | cmos_cmd = 0x0c; | 3395 | newstatus = 1; |
3168 | lght_cmd = 1; | ||
3169 | } else if (strlencmp(cmd, "off") == 0) { | 3396 | } else if (strlencmp(cmd, "off") == 0) { |
3170 | cmos_cmd = 0x0d; | 3397 | newstatus = 0; |
3171 | lght_cmd = 0; | ||
3172 | } else | 3398 | } else |
3173 | return -EINVAL; | 3399 | return -EINVAL; |
3174 | |||
3175 | success = cmos_handle ? | ||
3176 | acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd) : | ||
3177 | acpi_evalf(lght_handle, NULL, NULL, "vd", lght_cmd); | ||
3178 | if (!success) | ||
3179 | return -EIO; | ||
3180 | } | 3400 | } |
3181 | 3401 | ||
3182 | return 0; | 3402 | return light_set_status(newstatus); |
3183 | } | 3403 | } |
3184 | 3404 | ||
3185 | static struct ibm_struct light_driver_data = { | 3405 | static struct ibm_struct light_driver_data = { |
3186 | .name = "light", | 3406 | .name = "light", |
3187 | .read = light_read, | 3407 | .read = light_read, |
3188 | .write = light_write, | 3408 | .write = light_write, |
3409 | .exit = light_exit, | ||
3189 | }; | 3410 | }; |
3190 | 3411 | ||
3191 | /************************************************************************* | 3412 | /************************************************************************* |
@@ -3583,6 +3804,12 @@ enum { /* For TPACPI_LED_OLD */ | |||
3583 | TPACPI_LED_EC_HLMS = 0x0e, /* EC reg to select led to command */ | 3804 | TPACPI_LED_EC_HLMS = 0x0e, /* EC reg to select led to command */ |
3584 | }; | 3805 | }; |
3585 | 3806 | ||
3807 | enum led_status_t { | ||
3808 | TPACPI_LED_OFF = 0, | ||
3809 | TPACPI_LED_ON, | ||
3810 | TPACPI_LED_BLINK, | ||
3811 | }; | ||
3812 | |||
3586 | static enum led_access_mode led_supported; | 3813 | static enum led_access_mode led_supported; |
3587 | 3814 | ||
3588 | TPACPI_HANDLE(led, ec, "SLED", /* 570 */ | 3815 | TPACPI_HANDLE(led, ec, "SLED", /* 570 */ |
@@ -3591,8 +3818,174 @@ TPACPI_HANDLE(led, ec, "SLED", /* 570 */ | |||
3591 | "LED", /* all others */ | 3818 | "LED", /* all others */ |
3592 | ); /* R30, R31 */ | 3819 | ); /* R30, R31 */ |
3593 | 3820 | ||
3821 | #define TPACPI_LED_NUMLEDS 8 | ||
3822 | static struct tpacpi_led_classdev *tpacpi_leds; | ||
3823 | static enum led_status_t tpacpi_led_state_cache[TPACPI_LED_NUMLEDS]; | ||
3824 | static const char const *tpacpi_led_names[TPACPI_LED_NUMLEDS] = { | ||
3825 | /* there's a limit of 19 chars + NULL before 2.6.26 */ | ||
3826 | "tpacpi::power", | ||
3827 | "tpacpi:orange:batt", | ||
3828 | "tpacpi:green:batt", | ||
3829 | "tpacpi::dock_active", | ||
3830 | "tpacpi::bay_active", | ||
3831 | "tpacpi::dock_batt", | ||
3832 | "tpacpi::unknown_led", | ||
3833 | "tpacpi::standby", | ||
3834 | }; | ||
3835 | |||
3836 | static int led_get_status(unsigned int led) | ||
3837 | { | ||
3838 | int status; | ||
3839 | enum led_status_t led_s; | ||
3840 | |||
3841 | switch (led_supported) { | ||
3842 | case TPACPI_LED_570: | ||
3843 | if (!acpi_evalf(ec_handle, | ||
3844 | &status, "GLED", "dd", 1 << led)) | ||
3845 | return -EIO; | ||
3846 | led_s = (status == 0)? | ||
3847 | TPACPI_LED_OFF : | ||
3848 | ((status == 1)? | ||
3849 | TPACPI_LED_ON : | ||
3850 | TPACPI_LED_BLINK); | ||
3851 | tpacpi_led_state_cache[led] = led_s; | ||
3852 | return led_s; | ||
3853 | default: | ||
3854 | return -ENXIO; | ||
3855 | } | ||
3856 | |||
3857 | /* not reached */ | ||
3858 | } | ||
3859 | |||
3860 | static int led_set_status(unsigned int led, enum led_status_t ledstatus) | ||
3861 | { | ||
3862 | /* off, on, blink. Index is led_status_t */ | ||
3863 | static const int const led_sled_arg1[] = { 0, 1, 3 }; | ||
3864 | static const int const led_exp_hlbl[] = { 0, 0, 1 }; /* led# * */ | ||
3865 | static const int const led_exp_hlcl[] = { 0, 1, 1 }; /* led# * */ | ||
3866 | static const int const led_led_arg1[] = { 0, 0x80, 0xc0 }; | ||
3867 | |||
3868 | int rc = 0; | ||
3869 | |||
3870 | switch (led_supported) { | ||
3871 | case TPACPI_LED_570: | ||
3872 | /* 570 */ | ||
3873 | led = 1 << led; | ||
3874 | if (!acpi_evalf(led_handle, NULL, NULL, "vdd", | ||
3875 | led, led_sled_arg1[ledstatus])) | ||
3876 | rc = -EIO; | ||
3877 | break; | ||
3878 | case TPACPI_LED_OLD: | ||
3879 | /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */ | ||
3880 | led = 1 << led; | ||
3881 | rc = ec_write(TPACPI_LED_EC_HLMS, led); | ||
3882 | if (rc >= 0) | ||
3883 | rc = ec_write(TPACPI_LED_EC_HLBL, | ||
3884 | led * led_exp_hlbl[ledstatus]); | ||
3885 | if (rc >= 0) | ||
3886 | rc = ec_write(TPACPI_LED_EC_HLCL, | ||
3887 | led * led_exp_hlcl[ledstatus]); | ||
3888 | break; | ||
3889 | case TPACPI_LED_NEW: | ||
3890 | /* all others */ | ||
3891 | if (!acpi_evalf(led_handle, NULL, NULL, "vdd", | ||
3892 | led, led_led_arg1[ledstatus])) | ||
3893 | rc = -EIO; | ||
3894 | break; | ||
3895 | default: | ||
3896 | rc = -ENXIO; | ||
3897 | } | ||
3898 | |||
3899 | if (!rc) | ||
3900 | tpacpi_led_state_cache[led] = ledstatus; | ||
3901 | |||
3902 | return rc; | ||
3903 | } | ||
3904 | |||
3905 | static void led_sysfs_set_status(unsigned int led, | ||
3906 | enum led_brightness brightness) | ||
3907 | { | ||
3908 | led_set_status(led, | ||
3909 | (brightness == LED_OFF) ? | ||
3910 | TPACPI_LED_OFF : | ||
3911 | (tpacpi_led_state_cache[led] == TPACPI_LED_BLINK) ? | ||
3912 | TPACPI_LED_BLINK : TPACPI_LED_ON); | ||
3913 | } | ||
3914 | |||
3915 | static void led_set_status_worker(struct work_struct *work) | ||
3916 | { | ||
3917 | struct tpacpi_led_classdev *data = | ||
3918 | container_of(work, struct tpacpi_led_classdev, work); | ||
3919 | |||
3920 | if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING)) | ||
3921 | led_sysfs_set_status(data->led, data->new_brightness); | ||
3922 | } | ||
3923 | |||
3924 | static void led_sysfs_set(struct led_classdev *led_cdev, | ||
3925 | enum led_brightness brightness) | ||
3926 | { | ||
3927 | struct tpacpi_led_classdev *data = container_of(led_cdev, | ||
3928 | struct tpacpi_led_classdev, led_classdev); | ||
3929 | |||
3930 | data->new_brightness = brightness; | ||
3931 | queue_work(tpacpi_wq, &data->work); | ||
3932 | } | ||
3933 | |||
3934 | static int led_sysfs_blink_set(struct led_classdev *led_cdev, | ||
3935 | unsigned long *delay_on, unsigned long *delay_off) | ||
3936 | { | ||
3937 | struct tpacpi_led_classdev *data = container_of(led_cdev, | ||
3938 | struct tpacpi_led_classdev, led_classdev); | ||
3939 | |||
3940 | /* Can we choose the flash rate? */ | ||
3941 | if (*delay_on == 0 && *delay_off == 0) { | ||
3942 | /* yes. set them to the hardware blink rate (1 Hz) */ | ||
3943 | *delay_on = 500; /* ms */ | ||
3944 | *delay_off = 500; /* ms */ | ||
3945 | } else if ((*delay_on != 500) || (*delay_off != 500)) | ||
3946 | return -EINVAL; | ||
3947 | |||
3948 | data->new_brightness = TPACPI_LED_BLINK; | ||
3949 | queue_work(tpacpi_wq, &data->work); | ||
3950 | |||
3951 | return 0; | ||
3952 | } | ||
3953 | |||
3954 | static enum led_brightness led_sysfs_get(struct led_classdev *led_cdev) | ||
3955 | { | ||
3956 | int rc; | ||
3957 | |||
3958 | struct tpacpi_led_classdev *data = container_of(led_cdev, | ||
3959 | struct tpacpi_led_classdev, led_classdev); | ||
3960 | |||
3961 | rc = led_get_status(data->led); | ||
3962 | |||
3963 | if (rc == TPACPI_LED_OFF || rc < 0) | ||
3964 | rc = LED_OFF; /* no error handling in led class :( */ | ||
3965 | else | ||
3966 | rc = LED_FULL; | ||
3967 | |||
3968 | return rc; | ||
3969 | } | ||
3970 | |||
3971 | static void led_exit(void) | ||
3972 | { | ||
3973 | unsigned int i; | ||
3974 | |||
3975 | for (i = 0; i < TPACPI_LED_NUMLEDS; i++) { | ||
3976 | if (tpacpi_leds[i].led_classdev.name) | ||
3977 | led_classdev_unregister(&tpacpi_leds[i].led_classdev); | ||
3978 | } | ||
3979 | |||
3980 | kfree(tpacpi_leds); | ||
3981 | tpacpi_leds = NULL; | ||
3982 | } | ||
3983 | |||
3594 | static int __init led_init(struct ibm_init_struct *iibm) | 3984 | static int __init led_init(struct ibm_init_struct *iibm) |
3595 | { | 3985 | { |
3986 | unsigned int i; | ||
3987 | int rc; | ||
3988 | |||
3596 | vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n"); | 3989 | vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n"); |
3597 | 3990 | ||
3598 | TPACPI_ACPIHANDLE_INIT(led); | 3991 | TPACPI_ACPIHANDLE_INIT(led); |
@@ -3613,10 +4006,41 @@ static int __init led_init(struct ibm_init_struct *iibm) | |||
3613 | vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n", | 4006 | vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n", |
3614 | str_supported(led_supported), led_supported); | 4007 | str_supported(led_supported), led_supported); |
3615 | 4008 | ||
4009 | tpacpi_leds = kzalloc(sizeof(*tpacpi_leds) * TPACPI_LED_NUMLEDS, | ||
4010 | GFP_KERNEL); | ||
4011 | if (!tpacpi_leds) { | ||
4012 | printk(TPACPI_ERR "Out of memory for LED data\n"); | ||
4013 | return -ENOMEM; | ||
4014 | } | ||
4015 | |||
4016 | for (i = 0; i < TPACPI_LED_NUMLEDS; i++) { | ||
4017 | tpacpi_leds[i].led = i; | ||
4018 | |||
4019 | tpacpi_leds[i].led_classdev.brightness_set = &led_sysfs_set; | ||
4020 | tpacpi_leds[i].led_classdev.blink_set = &led_sysfs_blink_set; | ||
4021 | if (led_supported == TPACPI_LED_570) | ||
4022 | tpacpi_leds[i].led_classdev.brightness_get = | ||
4023 | &led_sysfs_get; | ||
4024 | |||
4025 | tpacpi_leds[i].led_classdev.name = tpacpi_led_names[i]; | ||
4026 | |||
4027 | INIT_WORK(&tpacpi_leds[i].work, led_set_status_worker); | ||
4028 | |||
4029 | rc = led_classdev_register(&tpacpi_pdev->dev, | ||
4030 | &tpacpi_leds[i].led_classdev); | ||
4031 | if (rc < 0) { | ||
4032 | tpacpi_leds[i].led_classdev.name = NULL; | ||
4033 | led_exit(); | ||
4034 | return rc; | ||
4035 | } | ||
4036 | } | ||
4037 | |||
3616 | return (led_supported != TPACPI_LED_NONE)? 0 : 1; | 4038 | return (led_supported != TPACPI_LED_NONE)? 0 : 1; |
3617 | } | 4039 | } |
3618 | 4040 | ||
3619 | #define led_status(s) ((s) == 0 ? "off" : ((s) == 1 ? "on" : "blinking")) | 4041 | #define str_led_status(s) \ |
4042 | ((s) == TPACPI_LED_OFF ? "off" : \ | ||
4043 | ((s) == TPACPI_LED_ON ? "on" : "blinking")) | ||
3620 | 4044 | ||
3621 | static int led_read(char *p) | 4045 | static int led_read(char *p) |
3622 | { | 4046 | { |
@@ -3632,11 +4056,11 @@ static int led_read(char *p) | |||
3632 | /* 570 */ | 4056 | /* 570 */ |
3633 | int i, status; | 4057 | int i, status; |
3634 | for (i = 0; i < 8; i++) { | 4058 | for (i = 0; i < 8; i++) { |
3635 | if (!acpi_evalf(ec_handle, | 4059 | status = led_get_status(i); |
3636 | &status, "GLED", "dd", 1 << i)) | 4060 | if (status < 0) |
3637 | return -EIO; | 4061 | return -EIO; |
3638 | len += sprintf(p + len, "%d:\t\t%s\n", | 4062 | len += sprintf(p + len, "%d:\t\t%s\n", |
3639 | i, led_status(status)); | 4063 | i, str_led_status(status)); |
3640 | } | 4064 | } |
3641 | } | 4065 | } |
3642 | 4066 | ||
@@ -3646,16 +4070,11 @@ static int led_read(char *p) | |||
3646 | return len; | 4070 | return len; |
3647 | } | 4071 | } |
3648 | 4072 | ||
3649 | /* off, on, blink */ | ||
3650 | static const int led_sled_arg1[] = { 0, 1, 3 }; | ||
3651 | static const int led_exp_hlbl[] = { 0, 0, 1 }; /* led# * */ | ||
3652 | static const int led_exp_hlcl[] = { 0, 1, 1 }; /* led# * */ | ||
3653 | static const int led_led_arg1[] = { 0, 0x80, 0xc0 }; | ||
3654 | |||
3655 | static int led_write(char *buf) | 4073 | static int led_write(char *buf) |
3656 | { | 4074 | { |
3657 | char *cmd; | 4075 | char *cmd; |
3658 | int led, ind, ret; | 4076 | int led, rc; |
4077 | enum led_status_t s; | ||
3659 | 4078 | ||
3660 | if (!led_supported) | 4079 | if (!led_supported) |
3661 | return -ENODEV; | 4080 | return -ENODEV; |
@@ -3665,38 +4084,18 @@ static int led_write(char *buf) | |||
3665 | return -EINVAL; | 4084 | return -EINVAL; |
3666 | 4085 | ||
3667 | if (strstr(cmd, "off")) { | 4086 | if (strstr(cmd, "off")) { |
3668 | ind = 0; | 4087 | s = TPACPI_LED_OFF; |
3669 | } else if (strstr(cmd, "on")) { | 4088 | } else if (strstr(cmd, "on")) { |
3670 | ind = 1; | 4089 | s = TPACPI_LED_ON; |
3671 | } else if (strstr(cmd, "blink")) { | 4090 | } else if (strstr(cmd, "blink")) { |
3672 | ind = 2; | 4091 | s = TPACPI_LED_BLINK; |
3673 | } else | ||
3674 | return -EINVAL; | ||
3675 | |||
3676 | if (led_supported == TPACPI_LED_570) { | ||
3677 | /* 570 */ | ||
3678 | led = 1 << led; | ||
3679 | if (!acpi_evalf(led_handle, NULL, NULL, "vdd", | ||
3680 | led, led_sled_arg1[ind])) | ||
3681 | return -EIO; | ||
3682 | } else if (led_supported == TPACPI_LED_OLD) { | ||
3683 | /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */ | ||
3684 | led = 1 << led; | ||
3685 | ret = ec_write(TPACPI_LED_EC_HLMS, led); | ||
3686 | if (ret >= 0) | ||
3687 | ret = ec_write(TPACPI_LED_EC_HLBL, | ||
3688 | led * led_exp_hlbl[ind]); | ||
3689 | if (ret >= 0) | ||
3690 | ret = ec_write(TPACPI_LED_EC_HLCL, | ||
3691 | led * led_exp_hlcl[ind]); | ||
3692 | if (ret < 0) | ||
3693 | return ret; | ||
3694 | } else { | 4092 | } else { |
3695 | /* all others */ | 4093 | return -EINVAL; |
3696 | if (!acpi_evalf(led_handle, NULL, NULL, "vdd", | ||
3697 | led, led_led_arg1[ind])) | ||
3698 | return -EIO; | ||
3699 | } | 4094 | } |
4095 | |||
4096 | rc = led_set_status(led, s); | ||
4097 | if (rc < 0) | ||
4098 | return rc; | ||
3700 | } | 4099 | } |
3701 | 4100 | ||
3702 | return 0; | 4101 | return 0; |
@@ -3706,6 +4105,7 @@ static struct ibm_struct led_driver_data = { | |||
3706 | .name = "led", | 4105 | .name = "led", |
3707 | .read = led_read, | 4106 | .read = led_read, |
3708 | .write = led_write, | 4107 | .write = led_write, |
4108 | .exit = led_exit, | ||
3709 | }; | 4109 | }; |
3710 | 4110 | ||
3711 | /************************************************************************* | 4111 | /************************************************************************* |
@@ -4170,8 +4570,16 @@ static struct ibm_struct ecdump_driver_data = { | |||
4170 | 4570 | ||
4171 | #define TPACPI_BACKLIGHT_DEV_NAME "thinkpad_screen" | 4571 | #define TPACPI_BACKLIGHT_DEV_NAME "thinkpad_screen" |
4172 | 4572 | ||
4573 | enum { | ||
4574 | TP_EC_BACKLIGHT = 0x31, | ||
4575 | |||
4576 | /* TP_EC_BACKLIGHT bitmasks */ | ||
4577 | TP_EC_BACKLIGHT_LVLMSK = 0x1F, | ||
4578 | TP_EC_BACKLIGHT_CMDMSK = 0xE0, | ||
4579 | TP_EC_BACKLIGHT_MAPSW = 0x20, | ||
4580 | }; | ||
4581 | |||
4173 | static struct backlight_device *ibm_backlight_device; | 4582 | static struct backlight_device *ibm_backlight_device; |
4174 | static int brightness_offset = 0x31; | ||
4175 | static int brightness_mode; | 4583 | static int brightness_mode; |
4176 | static unsigned int brightness_enable = 2; /* 2 = auto, 0 = no, 1 = yes */ | 4584 | static unsigned int brightness_enable = 2; /* 2 = auto, 0 = no, 1 = yes */ |
4177 | 4585 | ||
@@ -4180,16 +4588,24 @@ static struct mutex brightness_mutex; | |||
4180 | /* | 4588 | /* |
4181 | * ThinkPads can read brightness from two places: EC 0x31, or | 4589 | * ThinkPads can read brightness from two places: EC 0x31, or |
4182 | * CMOS NVRAM byte 0x5E, bits 0-3. | 4590 | * CMOS NVRAM byte 0x5E, bits 0-3. |
4591 | * | ||
4592 | * EC 0x31 has the following layout | ||
4593 | * Bit 7: unknown function | ||
4594 | * Bit 6: unknown function | ||
4595 | * Bit 5: Z: honour scale changes, NZ: ignore scale changes | ||
4596 | * Bit 4: must be set to zero to avoid problems | ||
4597 | * Bit 3-0: backlight brightness level | ||
4598 | * | ||
4599 | * brightness_get_raw returns status data in the EC 0x31 layout | ||
4183 | */ | 4600 | */ |
4184 | static int brightness_get(struct backlight_device *bd) | 4601 | static int brightness_get_raw(int *status) |
4185 | { | 4602 | { |
4186 | u8 lec = 0, lcmos = 0, level = 0; | 4603 | u8 lec = 0, lcmos = 0, level = 0; |
4187 | 4604 | ||
4188 | if (brightness_mode & 1) { | 4605 | if (brightness_mode & 1) { |
4189 | if (!acpi_ec_read(brightness_offset, &lec)) | 4606 | if (!acpi_ec_read(TP_EC_BACKLIGHT, &lec)) |
4190 | return -EIO; | 4607 | return -EIO; |
4191 | lec &= (tp_features.bright_16levels)? 0x0f : 0x07; | 4608 | level = lec & TP_EC_BACKLIGHT_LVLMSK; |
4192 | level = lec; | ||
4193 | }; | 4609 | }; |
4194 | if (brightness_mode & 2) { | 4610 | if (brightness_mode & 2) { |
4195 | lcmos = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS) | 4611 | lcmos = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS) |
@@ -4199,16 +4615,27 @@ static int brightness_get(struct backlight_device *bd) | |||
4199 | level = lcmos; | 4615 | level = lcmos; |
4200 | } | 4616 | } |
4201 | 4617 | ||
4202 | if (brightness_mode == 3 && lec != lcmos) { | 4618 | if (brightness_mode == 3) { |
4203 | printk(TPACPI_ERR | 4619 | *status = lec; /* Prefer EC, CMOS is just a backing store */ |
4204 | "CMOS NVRAM (%u) and EC (%u) do not agree " | 4620 | lec &= TP_EC_BACKLIGHT_LVLMSK; |
4205 | "on display brightness level\n", | 4621 | if (lec == lcmos) |
4206 | (unsigned int) lcmos, | 4622 | tp_warned.bright_cmos_ec_unsync = 0; |
4207 | (unsigned int) lec); | 4623 | else { |
4208 | return -EIO; | 4624 | if (!tp_warned.bright_cmos_ec_unsync) { |
4625 | printk(TPACPI_ERR | ||
4626 | "CMOS NVRAM (%u) and EC (%u) do not " | ||
4627 | "agree on display brightness level\n", | ||
4628 | (unsigned int) lcmos, | ||
4629 | (unsigned int) lec); | ||
4630 | tp_warned.bright_cmos_ec_unsync = 1; | ||
4631 | } | ||
4632 | return -EIO; | ||
4633 | } | ||
4634 | } else { | ||
4635 | *status = level; | ||
4209 | } | 4636 | } |
4210 | 4637 | ||
4211 | return level; | 4638 | return 0; |
4212 | } | 4639 | } |
4213 | 4640 | ||
4214 | /* May return EINTR which can always be mapped to ERESTARTSYS */ | 4641 | /* May return EINTR which can always be mapped to ERESTARTSYS */ |
@@ -4216,19 +4643,22 @@ static int brightness_set(int value) | |||
4216 | { | 4643 | { |
4217 | int cmos_cmd, inc, i, res; | 4644 | int cmos_cmd, inc, i, res; |
4218 | int current_value; | 4645 | int current_value; |
4646 | int command_bits; | ||
4219 | 4647 | ||
4220 | if (value > ((tp_features.bright_16levels)? 15 : 7)) | 4648 | if (value > ((tp_features.bright_16levels)? 15 : 7) || |
4649 | value < 0) | ||
4221 | return -EINVAL; | 4650 | return -EINVAL; |
4222 | 4651 | ||
4223 | res = mutex_lock_interruptible(&brightness_mutex); | 4652 | res = mutex_lock_interruptible(&brightness_mutex); |
4224 | if (res < 0) | 4653 | if (res < 0) |
4225 | return res; | 4654 | return res; |
4226 | 4655 | ||
4227 | current_value = brightness_get(NULL); | 4656 | res = brightness_get_raw(¤t_value); |
4228 | if (current_value < 0) { | 4657 | if (res < 0) |
4229 | res = current_value; | ||
4230 | goto errout; | 4658 | goto errout; |
4231 | } | 4659 | |
4660 | command_bits = current_value & TP_EC_BACKLIGHT_CMDMSK; | ||
4661 | current_value &= TP_EC_BACKLIGHT_LVLMSK; | ||
4232 | 4662 | ||
4233 | cmos_cmd = value > current_value ? | 4663 | cmos_cmd = value > current_value ? |
4234 | TP_CMOS_BRIGHTNESS_UP : | 4664 | TP_CMOS_BRIGHTNESS_UP : |
@@ -4243,7 +4673,8 @@ static int brightness_set(int value) | |||
4243 | goto errout; | 4673 | goto errout; |
4244 | } | 4674 | } |
4245 | if ((brightness_mode & 1) && | 4675 | if ((brightness_mode & 1) && |
4246 | !acpi_ec_write(brightness_offset, i + inc)) { | 4676 | !acpi_ec_write(TP_EC_BACKLIGHT, |
4677 | (i + inc) | command_bits)) { | ||
4247 | res = -EIO; | 4678 | res = -EIO; |
4248 | goto errout;; | 4679 | goto errout;; |
4249 | } | 4680 | } |
@@ -4266,106 +4697,23 @@ static int brightness_update_status(struct backlight_device *bd) | |||
4266 | bd->props.brightness : 0); | 4697 | bd->props.brightness : 0); |
4267 | } | 4698 | } |
4268 | 4699 | ||
4269 | static struct backlight_ops ibm_backlight_data = { | 4700 | static int brightness_get(struct backlight_device *bd) |
4270 | .get_brightness = brightness_get, | ||
4271 | .update_status = brightness_update_status, | ||
4272 | }; | ||
4273 | |||
4274 | /* --------------------------------------------------------------------- */ | ||
4275 | |||
4276 | static int __init tpacpi_query_bcll_levels(acpi_handle handle) | ||
4277 | { | ||
4278 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
4279 | union acpi_object *obj; | ||
4280 | int rc; | ||
4281 | |||
4282 | if (ACPI_SUCCESS(acpi_evaluate_object(handle, NULL, NULL, &buffer))) { | ||
4283 | obj = (union acpi_object *)buffer.pointer; | ||
4284 | if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) { | ||
4285 | printk(TPACPI_ERR "Unknown BCLL data, " | ||
4286 | "please report this to %s\n", TPACPI_MAIL); | ||
4287 | rc = 0; | ||
4288 | } else { | ||
4289 | rc = obj->package.count; | ||
4290 | } | ||
4291 | } else { | ||
4292 | return 0; | ||
4293 | } | ||
4294 | |||
4295 | kfree(buffer.pointer); | ||
4296 | return rc; | ||
4297 | } | ||
4298 | |||
4299 | static acpi_status __init brightness_find_bcll(acpi_handle handle, u32 lvl, | ||
4300 | void *context, void **rv) | ||
4301 | { | ||
4302 | char name[ACPI_PATH_SEGMENT_LENGTH]; | ||
4303 | struct acpi_buffer buffer = { sizeof(name), &name }; | ||
4304 | |||
4305 | if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) && | ||
4306 | !strncmp("BCLL", name, sizeof(name) - 1)) { | ||
4307 | if (tpacpi_query_bcll_levels(handle) == 16) { | ||
4308 | *rv = handle; | ||
4309 | return AE_CTRL_TERMINATE; | ||
4310 | } else { | ||
4311 | return AE_OK; | ||
4312 | } | ||
4313 | } else { | ||
4314 | return AE_OK; | ||
4315 | } | ||
4316 | } | ||
4317 | |||
4318 | static int __init brightness_check_levels(void) | ||
4319 | { | 4701 | { |
4320 | int status; | 4702 | int status, res; |
4321 | void *found_node = NULL; | ||
4322 | 4703 | ||
4323 | if (!vid_handle) { | 4704 | res = brightness_get_raw(&status); |
4324 | TPACPI_ACPIHANDLE_INIT(vid); | 4705 | if (res < 0) |
4325 | } | 4706 | return 0; /* FIXME: teach backlight about error handling */ |
4326 | if (!vid_handle) | ||
4327 | return 0; | ||
4328 | |||
4329 | /* Search for a BCLL package with 16 levels */ | ||
4330 | status = acpi_walk_namespace(ACPI_TYPE_PACKAGE, vid_handle, 3, | ||
4331 | brightness_find_bcll, NULL, | ||
4332 | &found_node); | ||
4333 | |||
4334 | return (ACPI_SUCCESS(status) && found_node != NULL); | ||
4335 | } | ||
4336 | |||
4337 | static acpi_status __init brightness_find_bcl(acpi_handle handle, u32 lvl, | ||
4338 | void *context, void **rv) | ||
4339 | { | ||
4340 | char name[ACPI_PATH_SEGMENT_LENGTH]; | ||
4341 | struct acpi_buffer buffer = { sizeof(name), &name }; | ||
4342 | 4707 | ||
4343 | if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) && | 4708 | return status & TP_EC_BACKLIGHT_LVLMSK; |
4344 | !strncmp("_BCL", name, sizeof(name) - 1)) { | ||
4345 | *rv = handle; | ||
4346 | return AE_CTRL_TERMINATE; | ||
4347 | } else { | ||
4348 | return AE_OK; | ||
4349 | } | ||
4350 | } | 4709 | } |
4351 | 4710 | ||
4352 | static int __init brightness_check_std_acpi_support(void) | 4711 | static struct backlight_ops ibm_backlight_data = { |
4353 | { | 4712 | .get_brightness = brightness_get, |
4354 | int status; | 4713 | .update_status = brightness_update_status, |
4355 | void *found_node = NULL; | 4714 | }; |
4356 | |||
4357 | if (!vid_handle) { | ||
4358 | TPACPI_ACPIHANDLE_INIT(vid); | ||
4359 | } | ||
4360 | if (!vid_handle) | ||
4361 | return 0; | ||
4362 | |||
4363 | /* Search for a _BCL method, but don't execute it */ | ||
4364 | status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3, | ||
4365 | brightness_find_bcl, NULL, &found_node); | ||
4366 | 4715 | ||
4367 | return (ACPI_SUCCESS(status) && found_node != NULL); | 4716 | /* --------------------------------------------------------------------- */ |
4368 | } | ||
4369 | 4717 | ||
4370 | static int __init brightness_init(struct ibm_init_struct *iibm) | 4718 | static int __init brightness_init(struct ibm_init_struct *iibm) |
4371 | { | 4719 | { |
@@ -4375,13 +4723,19 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
4375 | 4723 | ||
4376 | mutex_init(&brightness_mutex); | 4724 | mutex_init(&brightness_mutex); |
4377 | 4725 | ||
4378 | if (!brightness_enable) { | 4726 | /* |
4379 | dbg_printk(TPACPI_DBG_INIT, | 4727 | * We always attempt to detect acpi support, so as to switch |
4380 | "brightness support disabled by " | 4728 | * Lenovo Vista BIOS to ACPI brightness mode even if we are not |
4381 | "module parameter\n"); | 4729 | * going to publish a backlight interface |
4382 | return 1; | 4730 | */ |
4383 | } else if (brightness_enable > 1) { | 4731 | b = tpacpi_check_std_acpi_brightness_support(); |
4384 | if (brightness_check_std_acpi_support()) { | 4732 | if (b > 0) { |
4733 | if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) { | ||
4734 | printk(TPACPI_NOTICE | ||
4735 | "Lenovo BIOS switched to ACPI backlight " | ||
4736 | "control mode\n"); | ||
4737 | } | ||
4738 | if (brightness_enable > 1) { | ||
4385 | printk(TPACPI_NOTICE | 4739 | printk(TPACPI_NOTICE |
4386 | "standard ACPI backlight interface " | 4740 | "standard ACPI backlight interface " |
4387 | "available, not loading native one...\n"); | 4741 | "available, not loading native one...\n"); |
@@ -4389,6 +4743,22 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
4389 | } | 4743 | } |
4390 | } | 4744 | } |
4391 | 4745 | ||
4746 | if (!brightness_enable) { | ||
4747 | dbg_printk(TPACPI_DBG_INIT, | ||
4748 | "brightness support disabled by " | ||
4749 | "module parameter\n"); | ||
4750 | return 1; | ||
4751 | } | ||
4752 | |||
4753 | if (b > 16) { | ||
4754 | printk(TPACPI_ERR | ||
4755 | "Unsupported brightness interface, " | ||
4756 | "please contact %s\n", TPACPI_MAIL); | ||
4757 | return 1; | ||
4758 | } | ||
4759 | if (b == 16) | ||
4760 | tp_features.bright_16levels = 1; | ||
4761 | |||
4392 | if (!brightness_mode) { | 4762 | if (!brightness_mode) { |
4393 | if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) | 4763 | if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) |
4394 | brightness_mode = 2; | 4764 | brightness_mode = 2; |
@@ -4402,12 +4772,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
4402 | if (brightness_mode > 3) | 4772 | if (brightness_mode > 3) |
4403 | return -EINVAL; | 4773 | return -EINVAL; |
4404 | 4774 | ||
4405 | tp_features.bright_16levels = | 4775 | if (brightness_get_raw(&b) < 0) |
4406 | thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO && | ||
4407 | brightness_check_levels(); | ||
4408 | |||
4409 | b = brightness_get(NULL); | ||
4410 | if (b < 0) | ||
4411 | return 1; | 4776 | return 1; |
4412 | 4777 | ||
4413 | if (tp_features.bright_16levels) | 4778 | if (tp_features.bright_16levels) |
@@ -4425,7 +4790,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
4425 | 4790 | ||
4426 | ibm_backlight_device->props.max_brightness = | 4791 | ibm_backlight_device->props.max_brightness = |
4427 | (tp_features.bright_16levels)? 15 : 7; | 4792 | (tp_features.bright_16levels)? 15 : 7; |
4428 | ibm_backlight_device->props.brightness = b; | 4793 | ibm_backlight_device->props.brightness = b & TP_EC_BACKLIGHT_LVLMSK; |
4429 | backlight_update_status(ibm_backlight_device); | 4794 | backlight_update_status(ibm_backlight_device); |
4430 | 4795 | ||
4431 | return 0; | 4796 | return 0; |
@@ -5046,11 +5411,11 @@ static void fan_watchdog_reset(void) | |||
5046 | if (fan_watchdog_maxinterval > 0 && | 5411 | if (fan_watchdog_maxinterval > 0 && |
5047 | tpacpi_lifecycle != TPACPI_LIFE_EXITING) { | 5412 | tpacpi_lifecycle != TPACPI_LIFE_EXITING) { |
5048 | fan_watchdog_active = 1; | 5413 | fan_watchdog_active = 1; |
5049 | if (!schedule_delayed_work(&fan_watchdog_task, | 5414 | if (!queue_delayed_work(tpacpi_wq, &fan_watchdog_task, |
5050 | msecs_to_jiffies(fan_watchdog_maxinterval | 5415 | msecs_to_jiffies(fan_watchdog_maxinterval |
5051 | * 1000))) { | 5416 | * 1000))) { |
5052 | printk(TPACPI_ERR | 5417 | printk(TPACPI_ERR |
5053 | "failed to schedule the fan watchdog, " | 5418 | "failed to queue the fan watchdog, " |
5054 | "watchdog will not trigger\n"); | 5419 | "watchdog will not trigger\n"); |
5055 | } | 5420 | } |
5056 | } else | 5421 | } else |
@@ -5420,7 +5785,7 @@ static void fan_exit(void) | |||
5420 | &driver_attr_fan_watchdog); | 5785 | &driver_attr_fan_watchdog); |
5421 | 5786 | ||
5422 | cancel_delayed_work(&fan_watchdog_task); | 5787 | cancel_delayed_work(&fan_watchdog_task); |
5423 | flush_scheduled_work(); | 5788 | flush_workqueue(tpacpi_wq); |
5424 | } | 5789 | } |
5425 | 5790 | ||
5426 | static int fan_read(char *p) | 5791 | static int fan_read(char *p) |
@@ -5826,10 +6191,13 @@ static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp) | |||
5826 | 6191 | ||
5827 | tp->model_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_VERSION), | 6192 | tp->model_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_VERSION), |
5828 | GFP_KERNEL); | 6193 | GFP_KERNEL); |
5829 | if (strnicmp(tp->model_str, "ThinkPad", 8) != 0) { | 6194 | if (tp->model_str && strnicmp(tp->model_str, "ThinkPad", 8) != 0) { |
5830 | kfree(tp->model_str); | 6195 | kfree(tp->model_str); |
5831 | tp->model_str = NULL; | 6196 | tp->model_str = NULL; |
5832 | } | 6197 | } |
6198 | |||
6199 | tp->nummodel_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_NAME), | ||
6200 | GFP_KERNEL); | ||
5833 | } | 6201 | } |
5834 | 6202 | ||
5835 | static int __init probe_for_thinkpad(void) | 6203 | static int __init probe_for_thinkpad(void) |
@@ -6071,6 +6439,9 @@ static void thinkpad_acpi_module_exit(void) | |||
6071 | if (proc_dir) | 6439 | if (proc_dir) |
6072 | remove_proc_entry(TPACPI_PROC_DIR, acpi_root_dir); | 6440 | remove_proc_entry(TPACPI_PROC_DIR, acpi_root_dir); |
6073 | 6441 | ||
6442 | if (tpacpi_wq) | ||
6443 | destroy_workqueue(tpacpi_wq); | ||
6444 | |||
6074 | kfree(thinkpad_id.bios_version_str); | 6445 | kfree(thinkpad_id.bios_version_str); |
6075 | kfree(thinkpad_id.ec_version_str); | 6446 | kfree(thinkpad_id.ec_version_str); |
6076 | kfree(thinkpad_id.model_str); | 6447 | kfree(thinkpad_id.model_str); |
@@ -6101,6 +6472,12 @@ static int __init thinkpad_acpi_module_init(void) | |||
6101 | TPACPI_ACPIHANDLE_INIT(ecrd); | 6472 | TPACPI_ACPIHANDLE_INIT(ecrd); |
6102 | TPACPI_ACPIHANDLE_INIT(ecwr); | 6473 | TPACPI_ACPIHANDLE_INIT(ecwr); |
6103 | 6474 | ||
6475 | tpacpi_wq = create_singlethread_workqueue(TPACPI_WORKQUEUE_NAME); | ||
6476 | if (!tpacpi_wq) { | ||
6477 | thinkpad_acpi_module_exit(); | ||
6478 | return -ENOMEM; | ||
6479 | } | ||
6480 | |||
6104 | proc_dir = proc_mkdir(TPACPI_PROC_DIR, acpi_root_dir); | 6481 | proc_dir = proc_mkdir(TPACPI_PROC_DIR, acpi_root_dir); |
6105 | if (!proc_dir) { | 6482 | if (!proc_dir) { |
6106 | printk(TPACPI_ERR | 6483 | printk(TPACPI_ERR |
@@ -6223,6 +6600,8 @@ static int __init thinkpad_acpi_module_init(void) | |||
6223 | /* Please remove this in year 2009 */ | 6600 | /* Please remove this in year 2009 */ |
6224 | MODULE_ALIAS("ibm_acpi"); | 6601 | MODULE_ALIAS("ibm_acpi"); |
6225 | 6602 | ||
6603 | MODULE_ALIAS(TPACPI_DRVR_SHORTNAME); | ||
6604 | |||
6226 | /* | 6605 | /* |
6227 | * DMI matching for module autoloading | 6606 | * DMI matching for module autoloading |
6228 | * | 6607 | * |