diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-06-01 19:51:37 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-06-01 19:51:37 -0400 |
commit | 48445159e9ecb44a96a4de06c6ae7c54eb43ba5b (patch) | |
tree | 992657156c63746d6d43c3b6f7386227a0df34a4 | |
parent | af4f8ba31a4e328677bec493ceeaf112ca193b65 (diff) | |
parent | a2f01a899347fd97cb18094e5a55640cab552818 (diff) |
Merge branch 'for_linus' of git://cavan.codon.org.uk/platform-drivers-x86
Pull x86 platform driver updates from Matthew Garrett:
"Some significant improvements for the Sony driver on newer machines,
but other than that mostly just minor fixes and a patch to remove the
broken rfkill code from the Dell driver."
* 'for_linus' of git://cavan.codon.org.uk/platform-drivers-x86: (35 commits)
apple-gmux: Fix up the suspend/resume patch
dell-laptop: Remove rfkill code
toshiba_acpi: Fix mis-merge
dell-laptop: Add touchpad led support for Dell V3450
acer-wmi: add 3 laptops to video backlight vendor mode quirk table
sony-laptop: add touchpad enable/disable function
sony-laptop: add missing Fn key combos for 0x100 handlers
sony-laptop: add support for more WWAN modems
sony-laptop: new keyboard backlight handle
sony-laptop: add high speed battery charging function
sony-laptop: support automatic resume on lid open
sony-laptop: adjust error handling in finding SNC handles
sony-laptop: add thermal profiles support
sony-laptop: support battery care functions
sony-laptop: additional debug statements
sony-laptop: improve SNC initialization and acpi notify callback code
sony-laptop: use kstrtoul to parse sysfs values
sony-laptop: generalise ACPI calls into SNC functions
sony-laptop: fix return path when no ACPI buffer is allocated
sony-laptop: use soft rfkill status stored in hw
...
-rw-r--r-- | drivers/platform/x86/acer-wmi.c | 24 | ||||
-rw-r--r-- | drivers/platform/x86/apple-gmux.c | 4 | ||||
-rw-r--r-- | drivers/platform/x86/dell-laptop.c | 308 | ||||
-rw-r--r-- | drivers/platform/x86/fujitsu-tablet.c | 34 | ||||
-rw-r--r-- | drivers/platform/x86/hdaps.c | 2 | ||||
-rw-r--r-- | drivers/platform/x86/hp-wmi.c | 10 | ||||
-rw-r--r-- | drivers/platform/x86/ideapad-laptop.c | 9 | ||||
-rw-r--r-- | drivers/platform/x86/sony-laptop.c | 1498 | ||||
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 2 | ||||
-rw-r--r-- | drivers/platform/x86/toshiba_acpi.c | 141 | ||||
-rw-r--r-- | drivers/platform/x86/xo1-rfkill.c | 13 |
11 files changed, 1416 insertions, 629 deletions
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index c1a3fd8e1243..ce875dc365e5 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c | |||
@@ -523,6 +523,30 @@ static const struct dmi_system_id video_vendor_dmi_table[] = { | |||
523 | DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4750"), | 523 | DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4750"), |
524 | }, | 524 | }, |
525 | }, | 525 | }, |
526 | { | ||
527 | .callback = video_set_backlight_video_vendor, | ||
528 | .ident = "Acer Extensa 5235", | ||
529 | .matches = { | ||
530 | DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), | ||
531 | DMI_MATCH(DMI_PRODUCT_NAME, "Extensa 5235"), | ||
532 | }, | ||
533 | }, | ||
534 | { | ||
535 | .callback = video_set_backlight_video_vendor, | ||
536 | .ident = "Acer TravelMate 5760", | ||
537 | .matches = { | ||
538 | DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), | ||
539 | DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 5760"), | ||
540 | }, | ||
541 | }, | ||
542 | { | ||
543 | .callback = video_set_backlight_video_vendor, | ||
544 | .ident = "Acer Aspire 5750", | ||
545 | .matches = { | ||
546 | DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), | ||
547 | DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5750"), | ||
548 | }, | ||
549 | }, | ||
526 | {} | 550 | {} |
527 | }; | 551 | }; |
528 | 552 | ||
diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index 8a582bdfdc76..694a15a56230 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c | |||
@@ -87,6 +87,9 @@ static int gmux_update_status(struct backlight_device *bd) | |||
87 | struct apple_gmux_data *gmux_data = bl_get_data(bd); | 87 | struct apple_gmux_data *gmux_data = bl_get_data(bd); |
88 | u32 brightness = bd->props.brightness; | 88 | u32 brightness = bd->props.brightness; |
89 | 89 | ||
90 | if (bd->props.state & BL_CORE_SUSPENDED) | ||
91 | return 0; | ||
92 | |||
90 | /* | 93 | /* |
91 | * Older gmux versions require writing out lower bytes first then | 94 | * Older gmux versions require writing out lower bytes first then |
92 | * setting the upper byte to 0 to flush the values. Newer versions | 95 | * setting the upper byte to 0 to flush the values. Newer versions |
@@ -102,6 +105,7 @@ static int gmux_update_status(struct backlight_device *bd) | |||
102 | } | 105 | } |
103 | 106 | ||
104 | static const struct backlight_ops gmux_bl_ops = { | 107 | static const struct backlight_ops gmux_bl_ops = { |
108 | .options = BL_CORE_SUSPENDRESUME, | ||
105 | .get_brightness = gmux_get_brightness, | 109 | .get_brightness = gmux_get_brightness, |
106 | .update_status = gmux_update_status, | 110 | .update_status = gmux_update_status, |
107 | }; | 111 | }; |
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index e6c08ee8d46c..5f78aac9b163 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c | |||
@@ -21,7 +21,6 @@ | |||
21 | #include <linux/err.h> | 21 | #include <linux/err.h> |
22 | #include <linux/dmi.h> | 22 | #include <linux/dmi.h> |
23 | #include <linux/io.h> | 23 | #include <linux/io.h> |
24 | #include <linux/rfkill.h> | ||
25 | #include <linux/power_supply.h> | 24 | #include <linux/power_supply.h> |
26 | #include <linux/acpi.h> | 25 | #include <linux/acpi.h> |
27 | #include <linux/mm.h> | 26 | #include <linux/mm.h> |
@@ -90,11 +89,8 @@ static struct platform_driver platform_driver = { | |||
90 | 89 | ||
91 | static struct platform_device *platform_device; | 90 | static struct platform_device *platform_device; |
92 | static struct backlight_device *dell_backlight_device; | 91 | static struct backlight_device *dell_backlight_device; |
93 | static struct rfkill *wifi_rfkill; | ||
94 | static struct rfkill *bluetooth_rfkill; | ||
95 | static struct rfkill *wwan_rfkill; | ||
96 | 92 | ||
97 | static const struct dmi_system_id __initdata dell_device_table[] = { | 93 | static const struct dmi_system_id dell_device_table[] __initconst = { |
98 | { | 94 | { |
99 | .ident = "Dell laptop", | 95 | .ident = "Dell laptop", |
100 | .matches = { | 96 | .matches = { |
@@ -119,96 +115,94 @@ static const struct dmi_system_id __initdata dell_device_table[] = { | |||
119 | }; | 115 | }; |
120 | MODULE_DEVICE_TABLE(dmi, dell_device_table); | 116 | MODULE_DEVICE_TABLE(dmi, dell_device_table); |
121 | 117 | ||
122 | static struct dmi_system_id __devinitdata dell_blacklist[] = { | 118 | static struct dmi_system_id __devinitdata dell_quirks[] = { |
123 | /* Supported by compal-laptop */ | ||
124 | { | ||
125 | .ident = "Dell Mini 9", | ||
126 | .matches = { | ||
127 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
128 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"), | ||
129 | }, | ||
130 | }, | ||
131 | { | 119 | { |
132 | .ident = "Dell Mini 10", | 120 | .callback = dmi_matched, |
121 | .ident = "Dell Vostro V130", | ||
133 | .matches = { | 122 | .matches = { |
134 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | 123 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), |
135 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"), | 124 | DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V130"), |
136 | }, | 125 | }, |
126 | .driver_data = &quirk_dell_vostro_v130, | ||
137 | }, | 127 | }, |
138 | { | 128 | { |
139 | .ident = "Dell Mini 10v", | 129 | .callback = dmi_matched, |
130 | .ident = "Dell Vostro V131", | ||
140 | .matches = { | 131 | .matches = { |
141 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | 132 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), |
142 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"), | 133 | DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"), |
143 | }, | 134 | }, |
135 | .driver_data = &quirk_dell_vostro_v130, | ||
144 | }, | 136 | }, |
145 | { | 137 | { |
146 | .ident = "Dell Mini 1012", | 138 | .callback = dmi_matched, |
139 | .ident = "Dell Vostro 3350", | ||
147 | .matches = { | 140 | .matches = { |
148 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | 141 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), |
149 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1012"), | 142 | DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3350"), |
150 | }, | 143 | }, |
144 | .driver_data = &quirk_dell_vostro_v130, | ||
151 | }, | 145 | }, |
152 | { | 146 | { |
153 | .ident = "Dell Inspiron 11z", | 147 | .callback = dmi_matched, |
148 | .ident = "Dell Vostro 3555", | ||
154 | .matches = { | 149 | .matches = { |
155 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | 150 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), |
156 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"), | 151 | DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3555"), |
157 | }, | 152 | }, |
153 | .driver_data = &quirk_dell_vostro_v130, | ||
158 | }, | 154 | }, |
159 | { | 155 | { |
160 | .ident = "Dell Mini 12", | 156 | .callback = dmi_matched, |
157 | .ident = "Dell Inspiron N311z", | ||
161 | .matches = { | 158 | .matches = { |
162 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | 159 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), |
163 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"), | 160 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron N311z"), |
164 | }, | 161 | }, |
162 | .driver_data = &quirk_dell_vostro_v130, | ||
165 | }, | 163 | }, |
166 | {} | ||
167 | }; | ||
168 | |||
169 | static struct dmi_system_id __devinitdata dell_quirks[] = { | ||
170 | { | 164 | { |
171 | .callback = dmi_matched, | 165 | .callback = dmi_matched, |
172 | .ident = "Dell Vostro V130", | 166 | .ident = "Dell Inspiron M5110", |
173 | .matches = { | 167 | .matches = { |
174 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | 168 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), |
175 | DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V130"), | 169 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron M5110"), |
176 | }, | 170 | }, |
177 | .driver_data = &quirk_dell_vostro_v130, | 171 | .driver_data = &quirk_dell_vostro_v130, |
178 | }, | 172 | }, |
179 | { | 173 | { |
180 | .callback = dmi_matched, | 174 | .callback = dmi_matched, |
181 | .ident = "Dell Vostro V131", | 175 | .ident = "Dell Vostro 3360", |
182 | .matches = { | 176 | .matches = { |
183 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | 177 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), |
184 | DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"), | 178 | DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3360"), |
185 | }, | 179 | }, |
186 | .driver_data = &quirk_dell_vostro_v130, | 180 | .driver_data = &quirk_dell_vostro_v130, |
187 | }, | 181 | }, |
188 | { | 182 | { |
189 | .callback = dmi_matched, | 183 | .callback = dmi_matched, |
190 | .ident = "Dell Vostro 3555", | 184 | .ident = "Dell Vostro 3460", |
191 | .matches = { | 185 | .matches = { |
192 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | 186 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), |
193 | DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3555"), | 187 | DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3460"), |
194 | }, | 188 | }, |
195 | .driver_data = &quirk_dell_vostro_v130, | 189 | .driver_data = &quirk_dell_vostro_v130, |
196 | }, | 190 | }, |
197 | { | 191 | { |
198 | .callback = dmi_matched, | 192 | .callback = dmi_matched, |
199 | .ident = "Dell Inspiron N311z", | 193 | .ident = "Dell Vostro 3560", |
200 | .matches = { | 194 | .matches = { |
201 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | 195 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), |
202 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron N311z"), | 196 | DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3560"), |
203 | }, | 197 | }, |
204 | .driver_data = &quirk_dell_vostro_v130, | 198 | .driver_data = &quirk_dell_vostro_v130, |
205 | }, | 199 | }, |
206 | { | 200 | { |
207 | .callback = dmi_matched, | 201 | .callback = dmi_matched, |
208 | .ident = "Dell Inspiron M5110", | 202 | .ident = "Dell Vostro 3450", |
209 | .matches = { | 203 | .matches = { |
210 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | 204 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), |
211 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron M5110"), | 205 | DMI_MATCH(DMI_PRODUCT_NAME, "Dell System Vostro 3450"), |
212 | }, | 206 | }, |
213 | .driver_data = &quirk_dell_vostro_v130, | 207 | .driver_data = &quirk_dell_vostro_v130, |
214 | }, | 208 | }, |
@@ -305,94 +299,6 @@ dell_send_request(struct calling_interface_buffer *buffer, int class, | |||
305 | return buffer; | 299 | return buffer; |
306 | } | 300 | } |
307 | 301 | ||
308 | /* Derived from information in DellWirelessCtl.cpp: | ||
309 | Class 17, select 11 is radio control. It returns an array of 32-bit values. | ||
310 | |||
311 | Input byte 0 = 0: Wireless information | ||
312 | |||
313 | result[0]: return code | ||
314 | result[1]: | ||
315 | Bit 0: Hardware switch supported | ||
316 | Bit 1: Wifi locator supported | ||
317 | Bit 2: Wifi is supported | ||
318 | Bit 3: Bluetooth is supported | ||
319 | Bit 4: WWAN is supported | ||
320 | Bit 5: Wireless keyboard supported | ||
321 | Bits 6-7: Reserved | ||
322 | Bit 8: Wifi is installed | ||
323 | Bit 9: Bluetooth is installed | ||
324 | Bit 10: WWAN is installed | ||
325 | Bits 11-15: Reserved | ||
326 | Bit 16: Hardware switch is on | ||
327 | Bit 17: Wifi is blocked | ||
328 | Bit 18: Bluetooth is blocked | ||
329 | Bit 19: WWAN is blocked | ||
330 | Bits 20-31: Reserved | ||
331 | result[2]: NVRAM size in bytes | ||
332 | result[3]: NVRAM format version number | ||
333 | |||
334 | Input byte 0 = 2: Wireless switch configuration | ||
335 | result[0]: return code | ||
336 | result[1]: | ||
337 | Bit 0: Wifi controlled by switch | ||
338 | Bit 1: Bluetooth controlled by switch | ||
339 | Bit 2: WWAN controlled by switch | ||
340 | Bits 3-6: Reserved | ||
341 | Bit 7: Wireless switch config locked | ||
342 | Bit 8: Wifi locator enabled | ||
343 | Bits 9-14: Reserved | ||
344 | Bit 15: Wifi locator setting locked | ||
345 | Bits 16-31: Reserved | ||
346 | */ | ||
347 | |||
348 | static int dell_rfkill_set(void *data, bool blocked) | ||
349 | { | ||
350 | int disable = blocked ? 1 : 0; | ||
351 | unsigned long radio = (unsigned long)data; | ||
352 | int hwswitch_bit = (unsigned long)data - 1; | ||
353 | int ret = 0; | ||
354 | |||
355 | get_buffer(); | ||
356 | dell_send_request(buffer, 17, 11); | ||
357 | |||
358 | /* If the hardware switch controls this radio, and the hardware | ||
359 | switch is disabled, don't allow changing the software state */ | ||
360 | if ((hwswitch_state & BIT(hwswitch_bit)) && | ||
361 | !(buffer->output[1] & BIT(16))) { | ||
362 | ret = -EINVAL; | ||
363 | goto out; | ||
364 | } | ||
365 | |||
366 | buffer->input[0] = (1 | (radio<<8) | (disable << 16)); | ||
367 | dell_send_request(buffer, 17, 11); | ||
368 | |||
369 | out: | ||
370 | release_buffer(); | ||
371 | return ret; | ||
372 | } | ||
373 | |||
374 | static void dell_rfkill_query(struct rfkill *rfkill, void *data) | ||
375 | { | ||
376 | int status; | ||
377 | int bit = (unsigned long)data + 16; | ||
378 | int hwswitch_bit = (unsigned long)data - 1; | ||
379 | |||
380 | get_buffer(); | ||
381 | dell_send_request(buffer, 17, 11); | ||
382 | status = buffer->output[1]; | ||
383 | release_buffer(); | ||
384 | |||
385 | rfkill_set_sw_state(rfkill, !!(status & BIT(bit))); | ||
386 | |||
387 | if (hwswitch_state & (BIT(hwswitch_bit))) | ||
388 | rfkill_set_hw_state(rfkill, !(status & BIT(16))); | ||
389 | } | ||
390 | |||
391 | static const struct rfkill_ops dell_rfkill_ops = { | ||
392 | .set_block = dell_rfkill_set, | ||
393 | .query = dell_rfkill_query, | ||
394 | }; | ||
395 | |||
396 | static struct dentry *dell_laptop_dir; | 302 | static struct dentry *dell_laptop_dir; |
397 | 303 | ||
398 | static int dell_debugfs_show(struct seq_file *s, void *data) | 304 | static int dell_debugfs_show(struct seq_file *s, void *data) |
@@ -462,108 +368,6 @@ static const struct file_operations dell_debugfs_fops = { | |||
462 | .release = single_release, | 368 | .release = single_release, |
463 | }; | 369 | }; |
464 | 370 | ||
465 | static void dell_update_rfkill(struct work_struct *ignored) | ||
466 | { | ||
467 | if (wifi_rfkill) | ||
468 | dell_rfkill_query(wifi_rfkill, (void *)1); | ||
469 | if (bluetooth_rfkill) | ||
470 | dell_rfkill_query(bluetooth_rfkill, (void *)2); | ||
471 | if (wwan_rfkill) | ||
472 | dell_rfkill_query(wwan_rfkill, (void *)3); | ||
473 | } | ||
474 | static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill); | ||
475 | |||
476 | |||
477 | static int __init dell_setup_rfkill(void) | ||
478 | { | ||
479 | int status; | ||
480 | int ret; | ||
481 | |||
482 | if (dmi_check_system(dell_blacklist)) { | ||
483 | pr_info("Blacklisted hardware detected - not enabling rfkill\n"); | ||
484 | return 0; | ||
485 | } | ||
486 | |||
487 | get_buffer(); | ||
488 | dell_send_request(buffer, 17, 11); | ||
489 | status = buffer->output[1]; | ||
490 | buffer->input[0] = 0x2; | ||
491 | dell_send_request(buffer, 17, 11); | ||
492 | hwswitch_state = buffer->output[1]; | ||
493 | release_buffer(); | ||
494 | |||
495 | if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) { | ||
496 | wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev, | ||
497 | RFKILL_TYPE_WLAN, | ||
498 | &dell_rfkill_ops, (void *) 1); | ||
499 | if (!wifi_rfkill) { | ||
500 | ret = -ENOMEM; | ||
501 | goto err_wifi; | ||
502 | } | ||
503 | ret = rfkill_register(wifi_rfkill); | ||
504 | if (ret) | ||
505 | goto err_wifi; | ||
506 | } | ||
507 | |||
508 | if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) { | ||
509 | bluetooth_rfkill = rfkill_alloc("dell-bluetooth", | ||
510 | &platform_device->dev, | ||
511 | RFKILL_TYPE_BLUETOOTH, | ||
512 | &dell_rfkill_ops, (void *) 2); | ||
513 | if (!bluetooth_rfkill) { | ||
514 | ret = -ENOMEM; | ||
515 | goto err_bluetooth; | ||
516 | } | ||
517 | ret = rfkill_register(bluetooth_rfkill); | ||
518 | if (ret) | ||
519 | goto err_bluetooth; | ||
520 | } | ||
521 | |||
522 | if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) { | ||
523 | wwan_rfkill = rfkill_alloc("dell-wwan", | ||
524 | &platform_device->dev, | ||
525 | RFKILL_TYPE_WWAN, | ||
526 | &dell_rfkill_ops, (void *) 3); | ||
527 | if (!wwan_rfkill) { | ||
528 | ret = -ENOMEM; | ||
529 | goto err_wwan; | ||
530 | } | ||
531 | ret = rfkill_register(wwan_rfkill); | ||
532 | if (ret) | ||
533 | goto err_wwan; | ||
534 | } | ||
535 | |||
536 | return 0; | ||
537 | err_wwan: | ||
538 | rfkill_destroy(wwan_rfkill); | ||
539 | if (bluetooth_rfkill) | ||
540 | rfkill_unregister(bluetooth_rfkill); | ||
541 | err_bluetooth: | ||
542 | rfkill_destroy(bluetooth_rfkill); | ||
543 | if (wifi_rfkill) | ||
544 | rfkill_unregister(wifi_rfkill); | ||
545 | err_wifi: | ||
546 | rfkill_destroy(wifi_rfkill); | ||
547 | |||
548 | return ret; | ||
549 | } | ||
550 | |||
551 | static void dell_cleanup_rfkill(void) | ||
552 | { | ||
553 | if (wifi_rfkill) { | ||
554 | rfkill_unregister(wifi_rfkill); | ||
555 | rfkill_destroy(wifi_rfkill); | ||
556 | } | ||
557 | if (bluetooth_rfkill) { | ||
558 | rfkill_unregister(bluetooth_rfkill); | ||
559 | rfkill_destroy(bluetooth_rfkill); | ||
560 | } | ||
561 | if (wwan_rfkill) { | ||
562 | rfkill_unregister(wwan_rfkill); | ||
563 | rfkill_destroy(wwan_rfkill); | ||
564 | } | ||
565 | } | ||
566 | |||
567 | static int dell_send_intensity(struct backlight_device *bd) | 371 | static int dell_send_intensity(struct backlight_device *bd) |
568 | { | 372 | { |
569 | int ret = 0; | 373 | int ret = 0; |
@@ -655,30 +459,6 @@ static void touchpad_led_exit(void) | |||
655 | led_classdev_unregister(&touchpad_led); | 459 | led_classdev_unregister(&touchpad_led); |
656 | } | 460 | } |
657 | 461 | ||
658 | static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str, | ||
659 | struct serio *port) | ||
660 | { | ||
661 | static bool extended; | ||
662 | |||
663 | if (str & 0x20) | ||
664 | return false; | ||
665 | |||
666 | if (unlikely(data == 0xe0)) { | ||
667 | extended = true; | ||
668 | return false; | ||
669 | } else if (unlikely(extended)) { | ||
670 | switch (data) { | ||
671 | case 0x8: | ||
672 | schedule_delayed_work(&dell_rfkill_work, | ||
673 | round_jiffies_relative(HZ)); | ||
674 | break; | ||
675 | } | ||
676 | extended = false; | ||
677 | } | ||
678 | |||
679 | return false; | ||
680 | } | ||
681 | |||
682 | static int __init dell_init(void) | 462 | static int __init dell_init(void) |
683 | { | 463 | { |
684 | int max_intensity = 0; | 464 | int max_intensity = 0; |
@@ -720,26 +500,10 @@ static int __init dell_init(void) | |||
720 | goto fail_buffer; | 500 | goto fail_buffer; |
721 | buffer = page_address(bufferpage); | 501 | buffer = page_address(bufferpage); |
722 | 502 | ||
723 | ret = dell_setup_rfkill(); | ||
724 | |||
725 | if (ret) { | ||
726 | pr_warn("Unable to setup rfkill\n"); | ||
727 | goto fail_rfkill; | ||
728 | } | ||
729 | |||
730 | ret = i8042_install_filter(dell_laptop_i8042_filter); | ||
731 | if (ret) { | ||
732 | pr_warn("Unable to install key filter\n"); | ||
733 | goto fail_filter; | ||
734 | } | ||
735 | |||
736 | if (quirks && quirks->touchpad_led) | 503 | if (quirks && quirks->touchpad_led) |
737 | touchpad_led_init(&platform_device->dev); | 504 | touchpad_led_init(&platform_device->dev); |
738 | 505 | ||
739 | dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL); | 506 | dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL); |
740 | if (dell_laptop_dir != NULL) | ||
741 | debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL, | ||
742 | &dell_debugfs_fops); | ||
743 | 507 | ||
744 | #ifdef CONFIG_ACPI | 508 | #ifdef CONFIG_ACPI |
745 | /* In the event of an ACPI backlight being available, don't | 509 | /* In the event of an ACPI backlight being available, don't |
@@ -782,11 +546,6 @@ static int __init dell_init(void) | |||
782 | return 0; | 546 | return 0; |
783 | 547 | ||
784 | fail_backlight: | 548 | fail_backlight: |
785 | i8042_remove_filter(dell_laptop_i8042_filter); | ||
786 | cancel_delayed_work_sync(&dell_rfkill_work); | ||
787 | fail_filter: | ||
788 | dell_cleanup_rfkill(); | ||
789 | fail_rfkill: | ||
790 | free_page((unsigned long)bufferpage); | 549 | free_page((unsigned long)bufferpage); |
791 | fail_buffer: | 550 | fail_buffer: |
792 | platform_device_del(platform_device); | 551 | platform_device_del(platform_device); |
@@ -804,10 +563,7 @@ static void __exit dell_exit(void) | |||
804 | debugfs_remove_recursive(dell_laptop_dir); | 563 | debugfs_remove_recursive(dell_laptop_dir); |
805 | if (quirks && quirks->touchpad_led) | 564 | if (quirks && quirks->touchpad_led) |
806 | touchpad_led_exit(); | 565 | touchpad_led_exit(); |
807 | i8042_remove_filter(dell_laptop_i8042_filter); | ||
808 | cancel_delayed_work_sync(&dell_rfkill_work); | ||
809 | backlight_device_unregister(dell_backlight_device); | 566 | backlight_device_unregister(dell_backlight_device); |
810 | dell_cleanup_rfkill(); | ||
811 | if (platform_device) { | 567 | if (platform_device) { |
812 | platform_device_unregister(platform_device); | 568 | platform_device_unregister(platform_device); |
813 | platform_driver_unregister(&platform_driver); | 569 | platform_driver_unregister(&platform_driver); |
diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujitsu-tablet.c index 580d80a73c3a..da267eae8ba8 100644 --- a/drivers/platform/x86/fujitsu-tablet.c +++ b/drivers/platform/x86/fujitsu-tablet.c | |||
@@ -16,6 +16,8 @@ | |||
16 | * 59 Temple Place Suite 330, Boston, MA 02111-1307, USA. | 16 | * 59 Temple Place Suite 330, Boston, MA 02111-1307, USA. |
17 | */ | 17 | */ |
18 | 18 | ||
19 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
20 | |||
19 | #include <linux/kernel.h> | 21 | #include <linux/kernel.h> |
20 | #include <linux/module.h> | 22 | #include <linux/module.h> |
21 | #include <linux/init.h> | 23 | #include <linux/init.h> |
@@ -34,7 +36,8 @@ | |||
34 | #define ACPI_FUJITSU_CLASS "fujitsu" | 36 | #define ACPI_FUJITSU_CLASS "fujitsu" |
35 | 37 | ||
36 | #define INVERT_TABLET_MODE_BIT 0x01 | 38 | #define INVERT_TABLET_MODE_BIT 0x01 |
37 | #define FORCE_TABLET_MODE_IF_UNDOCK 0x02 | 39 | #define INVERT_DOCK_STATE_BIT 0x02 |
40 | #define FORCE_TABLET_MODE_IF_UNDOCK 0x04 | ||
38 | 41 | ||
39 | #define KEYMAP_LEN 16 | 42 | #define KEYMAP_LEN 16 |
40 | 43 | ||
@@ -161,6 +164,8 @@ static void fujitsu_send_state(void) | |||
161 | state = fujitsu_read_register(0xdd); | 164 | state = fujitsu_read_register(0xdd); |
162 | 165 | ||
163 | dock = state & 0x02; | 166 | dock = state & 0x02; |
167 | if (fujitsu.config.quirks & INVERT_DOCK_STATE_BIT) | ||
168 | dock = !dock; | ||
164 | 169 | ||
165 | if ((fujitsu.config.quirks & FORCE_TABLET_MODE_IF_UNDOCK) && (!dock)) { | 170 | if ((fujitsu.config.quirks & FORCE_TABLET_MODE_IF_UNDOCK) && (!dock)) { |
166 | tablet_mode = 1; | 171 | tablet_mode = 1; |
@@ -221,9 +226,6 @@ static int __devinit input_fujitsu_setup(struct device *parent, | |||
221 | input_set_capability(idev, EV_SW, SW_DOCK); | 226 | input_set_capability(idev, EV_SW, SW_DOCK); |
222 | input_set_capability(idev, EV_SW, SW_TABLET_MODE); | 227 | input_set_capability(idev, EV_SW, SW_TABLET_MODE); |
223 | 228 | ||
224 | input_set_capability(idev, EV_SW, SW_DOCK); | ||
225 | input_set_capability(idev, EV_SW, SW_TABLET_MODE); | ||
226 | |||
227 | error = input_register_device(idev); | 229 | error = input_register_device(idev); |
228 | if (error) { | 230 | if (error) { |
229 | input_free_device(idev); | 231 | input_free_device(idev); |
@@ -275,25 +277,31 @@ static irqreturn_t fujitsu_interrupt(int irq, void *dev_id) | |||
275 | return IRQ_HANDLED; | 277 | return IRQ_HANDLED; |
276 | } | 278 | } |
277 | 279 | ||
278 | static int __devinit fujitsu_dmi_default(const struct dmi_system_id *dmi) | 280 | static void __devinit fujitsu_dmi_common(const struct dmi_system_id *dmi) |
279 | { | 281 | { |
280 | printk(KERN_INFO MODULENAME ": %s\n", dmi->ident); | 282 | pr_info("%s\n", dmi->ident); |
281 | memcpy(fujitsu.config.keymap, dmi->driver_data, | 283 | memcpy(fujitsu.config.keymap, dmi->driver_data, |
282 | sizeof(fujitsu.config.keymap)); | 284 | sizeof(fujitsu.config.keymap)); |
285 | } | ||
286 | |||
287 | static int __devinit fujitsu_dmi_lifebook(const struct dmi_system_id *dmi) | ||
288 | { | ||
289 | fujitsu_dmi_common(dmi); | ||
290 | fujitsu.config.quirks |= INVERT_TABLET_MODE_BIT; | ||
283 | return 1; | 291 | return 1; |
284 | } | 292 | } |
285 | 293 | ||
286 | static int __devinit fujitsu_dmi_stylistic(const struct dmi_system_id *dmi) | 294 | static int __devinit fujitsu_dmi_stylistic(const struct dmi_system_id *dmi) |
287 | { | 295 | { |
288 | fujitsu_dmi_default(dmi); | 296 | fujitsu_dmi_common(dmi); |
289 | fujitsu.config.quirks |= FORCE_TABLET_MODE_IF_UNDOCK; | 297 | fujitsu.config.quirks |= FORCE_TABLET_MODE_IF_UNDOCK; |
290 | fujitsu.config.quirks |= INVERT_TABLET_MODE_BIT; | 298 | fujitsu.config.quirks |= INVERT_DOCK_STATE_BIT; |
291 | return 1; | 299 | return 1; |
292 | } | 300 | } |
293 | 301 | ||
294 | static struct dmi_system_id dmi_ids[] __initconst = { | 302 | static struct dmi_system_id dmi_ids[] __initconst = { |
295 | { | 303 | { |
296 | .callback = fujitsu_dmi_default, | 304 | .callback = fujitsu_dmi_lifebook, |
297 | .ident = "Fujitsu Siemens P/T Series", | 305 | .ident = "Fujitsu Siemens P/T Series", |
298 | .matches = { | 306 | .matches = { |
299 | DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), | 307 | DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), |
@@ -302,7 +310,7 @@ static struct dmi_system_id dmi_ids[] __initconst = { | |||
302 | .driver_data = keymap_Lifebook_Tseries | 310 | .driver_data = keymap_Lifebook_Tseries |
303 | }, | 311 | }, |
304 | { | 312 | { |
305 | .callback = fujitsu_dmi_default, | 313 | .callback = fujitsu_dmi_lifebook, |
306 | .ident = "Fujitsu Lifebook T Series", | 314 | .ident = "Fujitsu Lifebook T Series", |
307 | .matches = { | 315 | .matches = { |
308 | DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), | 316 | DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), |
@@ -320,7 +328,7 @@ static struct dmi_system_id dmi_ids[] __initconst = { | |||
320 | .driver_data = keymap_Stylistic_Tseries | 328 | .driver_data = keymap_Stylistic_Tseries |
321 | }, | 329 | }, |
322 | { | 330 | { |
323 | .callback = fujitsu_dmi_default, | 331 | .callback = fujitsu_dmi_lifebook, |
324 | .ident = "Fujitsu LifeBook U810", | 332 | .ident = "Fujitsu LifeBook U810", |
325 | .matches = { | 333 | .matches = { |
326 | DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), | 334 | DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), |
@@ -347,7 +355,7 @@ static struct dmi_system_id dmi_ids[] __initconst = { | |||
347 | .driver_data = keymap_Stylistic_ST5xxx | 355 | .driver_data = keymap_Stylistic_ST5xxx |
348 | }, | 356 | }, |
349 | { | 357 | { |
350 | .callback = fujitsu_dmi_default, | 358 | .callback = fujitsu_dmi_lifebook, |
351 | .ident = "Unknown (using defaults)", | 359 | .ident = "Unknown (using defaults)", |
352 | .matches = { | 360 | .matches = { |
353 | DMI_MATCH(DMI_SYS_VENDOR, ""), | 361 | DMI_MATCH(DMI_SYS_VENDOR, ""), |
@@ -473,6 +481,6 @@ module_exit(fujitsu_module_exit); | |||
473 | MODULE_AUTHOR("Robert Gerlach <khnz@gmx.de>"); | 481 | MODULE_AUTHOR("Robert Gerlach <khnz@gmx.de>"); |
474 | MODULE_DESCRIPTION("Fujitsu tablet pc extras driver"); | 482 | MODULE_DESCRIPTION("Fujitsu tablet pc extras driver"); |
475 | MODULE_LICENSE("GPL"); | 483 | MODULE_LICENSE("GPL"); |
476 | MODULE_VERSION("2.4"); | 484 | MODULE_VERSION("2.5"); |
477 | 485 | ||
478 | MODULE_DEVICE_TABLE(acpi, fujitsu_ids); | 486 | MODULE_DEVICE_TABLE(acpi, fujitsu_ids); |
diff --git a/drivers/platform/x86/hdaps.c b/drivers/platform/x86/hdaps.c index 7387f97a2941..24a3ae065f1b 100644 --- a/drivers/platform/x86/hdaps.c +++ b/drivers/platform/x86/hdaps.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * hdaps.c - driver for IBM's Hard Drive Active Protection System | 2 | * hdaps.c - driver for IBM's Hard Drive Active Protection System |
3 | * | 3 | * |
4 | * Copyright (C) 2005 Robert Love <rml@novell.com> | 4 | * Copyright (C) 2005 Robert Love <rml@novell.com> |
5 | * Copyright (C) 2005 Jesper Juhl <jesper.juhl@gmail.com> | 5 | * Copyright (C) 2005 Jesper Juhl <jj@chaosbits.net> |
6 | * | 6 | * |
7 | * The HardDisk Active Protection System (hdaps) is present in IBM ThinkPads | 7 | * The HardDisk Active Protection System (hdaps) is present in IBM ThinkPads |
8 | * starting with the R40, T41, and X40. It provides a basic two-axis | 8 | * starting with the R40, T41, and X40. It provides a basic two-axis |
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index e2faa3cbb792..387183a2d6dd 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c | |||
@@ -634,6 +634,8 @@ static int __devinit hp_wmi_rfkill_setup(struct platform_device *device) | |||
634 | RFKILL_TYPE_WLAN, | 634 | RFKILL_TYPE_WLAN, |
635 | &hp_wmi_rfkill_ops, | 635 | &hp_wmi_rfkill_ops, |
636 | (void *) HPWMI_WIFI); | 636 | (void *) HPWMI_WIFI); |
637 | if (!wifi_rfkill) | ||
638 | return -ENOMEM; | ||
637 | rfkill_init_sw_state(wifi_rfkill, | 639 | rfkill_init_sw_state(wifi_rfkill, |
638 | hp_wmi_get_sw_state(HPWMI_WIFI)); | 640 | hp_wmi_get_sw_state(HPWMI_WIFI)); |
639 | rfkill_set_hw_state(wifi_rfkill, | 641 | rfkill_set_hw_state(wifi_rfkill, |
@@ -648,6 +650,10 @@ static int __devinit hp_wmi_rfkill_setup(struct platform_device *device) | |||
648 | RFKILL_TYPE_BLUETOOTH, | 650 | RFKILL_TYPE_BLUETOOTH, |
649 | &hp_wmi_rfkill_ops, | 651 | &hp_wmi_rfkill_ops, |
650 | (void *) HPWMI_BLUETOOTH); | 652 | (void *) HPWMI_BLUETOOTH); |
653 | if (!bluetooth_rfkill) { | ||
654 | err = -ENOMEM; | ||
655 | goto register_wifi_error; | ||
656 | } | ||
651 | rfkill_init_sw_state(bluetooth_rfkill, | 657 | rfkill_init_sw_state(bluetooth_rfkill, |
652 | hp_wmi_get_sw_state(HPWMI_BLUETOOTH)); | 658 | hp_wmi_get_sw_state(HPWMI_BLUETOOTH)); |
653 | rfkill_set_hw_state(bluetooth_rfkill, | 659 | rfkill_set_hw_state(bluetooth_rfkill, |
@@ -662,6 +668,10 @@ static int __devinit hp_wmi_rfkill_setup(struct platform_device *device) | |||
662 | RFKILL_TYPE_WWAN, | 668 | RFKILL_TYPE_WWAN, |
663 | &hp_wmi_rfkill_ops, | 669 | &hp_wmi_rfkill_ops, |
664 | (void *) HPWMI_WWAN); | 670 | (void *) HPWMI_WWAN); |
671 | if (!wwan_rfkill) { | ||
672 | err = -ENOMEM; | ||
673 | goto register_bluetooth_error; | ||
674 | } | ||
665 | rfkill_init_sw_state(wwan_rfkill, | 675 | rfkill_init_sw_state(wwan_rfkill, |
666 | hp_wmi_get_sw_state(HPWMI_WWAN)); | 676 | hp_wmi_get_sw_state(HPWMI_WWAN)); |
667 | rfkill_set_hw_state(wwan_rfkill, | 677 | rfkill_set_hw_state(wwan_rfkill, |
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index ac902f7a9baa..4f20f8dd3d7c 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c | |||
@@ -194,7 +194,6 @@ static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data) | |||
194 | /* | 194 | /* |
195 | * debugfs | 195 | * debugfs |
196 | */ | 196 | */ |
197 | #define DEBUGFS_EVENT_LEN (4096) | ||
198 | static int debugfs_status_show(struct seq_file *s, void *data) | 197 | static int debugfs_status_show(struct seq_file *s, void *data) |
199 | { | 198 | { |
200 | unsigned long value; | 199 | unsigned long value; |
@@ -315,7 +314,7 @@ static int __devinit ideapad_debugfs_init(struct ideapad_private *priv) | |||
315 | node = debugfs_create_file("status", S_IRUGO, priv->debug, NULL, | 314 | node = debugfs_create_file("status", S_IRUGO, priv->debug, NULL, |
316 | &debugfs_status_fops); | 315 | &debugfs_status_fops); |
317 | if (!node) { | 316 | if (!node) { |
318 | pr_err("failed to create event in debugfs"); | 317 | pr_err("failed to create status in debugfs"); |
319 | goto errout; | 318 | goto errout; |
320 | } | 319 | } |
321 | 320 | ||
@@ -785,6 +784,10 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) | |||
785 | case 9: | 784 | case 9: |
786 | ideapad_sync_rfk_state(priv); | 785 | ideapad_sync_rfk_state(priv); |
787 | break; | 786 | break; |
787 | case 13: | ||
788 | case 6: | ||
789 | ideapad_input_report(priv, vpc_bit); | ||
790 | break; | ||
788 | case 4: | 791 | case 4: |
789 | ideapad_backlight_notify_brightness(priv); | 792 | ideapad_backlight_notify_brightness(priv); |
790 | break; | 793 | break; |
@@ -795,7 +798,7 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) | |||
795 | ideapad_backlight_notify_power(priv); | 798 | ideapad_backlight_notify_power(priv); |
796 | break; | 799 | break; |
797 | default: | 800 | default: |
798 | ideapad_input_report(priv, vpc_bit); | 801 | pr_info("Unknown event: %lu\n", vpc_bit); |
799 | } | 802 | } |
800 | } | 803 | } |
801 | } | 804 | } |
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 8a51795aa02a..210d4ae547c2 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c | |||
@@ -141,6 +141,27 @@ MODULE_PARM_DESC(kbd_backlight_timeout, | |||
141 | "(default: 0)"); | 141 | "(default: 0)"); |
142 | 142 | ||
143 | static void sony_nc_kbd_backlight_resume(void); | 143 | static void sony_nc_kbd_backlight_resume(void); |
144 | static int sony_nc_kbd_backlight_setup(struct platform_device *pd, | ||
145 | unsigned int handle); | ||
146 | static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd); | ||
147 | |||
148 | static int sony_nc_battery_care_setup(struct platform_device *pd, | ||
149 | unsigned int handle); | ||
150 | static void sony_nc_battery_care_cleanup(struct platform_device *pd); | ||
151 | |||
152 | static int sony_nc_thermal_setup(struct platform_device *pd); | ||
153 | static void sony_nc_thermal_cleanup(struct platform_device *pd); | ||
154 | static void sony_nc_thermal_resume(void); | ||
155 | |||
156 | static int sony_nc_lid_resume_setup(struct platform_device *pd); | ||
157 | static void sony_nc_lid_resume_cleanup(struct platform_device *pd); | ||
158 | |||
159 | static int sony_nc_highspeed_charging_setup(struct platform_device *pd); | ||
160 | static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd); | ||
161 | |||
162 | static int sony_nc_touchpad_setup(struct platform_device *pd, | ||
163 | unsigned int handle); | ||
164 | static void sony_nc_touchpad_cleanup(struct platform_device *pd); | ||
144 | 165 | ||
145 | enum sony_nc_rfkill { | 166 | enum sony_nc_rfkill { |
146 | SONY_WIFI, | 167 | SONY_WIFI, |
@@ -153,6 +174,9 @@ enum sony_nc_rfkill { | |||
153 | static int sony_rfkill_handle; | 174 | static int sony_rfkill_handle; |
154 | static struct rfkill *sony_rfkill_devices[N_SONY_RFKILL]; | 175 | static struct rfkill *sony_rfkill_devices[N_SONY_RFKILL]; |
155 | static int sony_rfkill_address[N_SONY_RFKILL] = {0x300, 0x500, 0x700, 0x900}; | 176 | static int sony_rfkill_address[N_SONY_RFKILL] = {0x300, 0x500, 0x700, 0x900}; |
177 | static int sony_nc_rfkill_setup(struct acpi_device *device, | ||
178 | unsigned int handle); | ||
179 | static void sony_nc_rfkill_cleanup(void); | ||
156 | static void sony_nc_rfkill_update(void); | 180 | static void sony_nc_rfkill_update(void); |
157 | 181 | ||
158 | /*********** Input Devices ***********/ | 182 | /*********** Input Devices ***********/ |
@@ -691,59 +715,97 @@ static struct acpi_device *sony_nc_acpi_device = NULL; | |||
691 | 715 | ||
692 | /* | 716 | /* |
693 | * acpi_evaluate_object wrappers | 717 | * acpi_evaluate_object wrappers |
718 | * all useful calls into SNC methods take one or zero parameters and return | ||
719 | * integers or arrays. | ||
694 | */ | 720 | */ |
695 | static int acpi_callgetfunc(acpi_handle handle, char *name, int *result) | 721 | static union acpi_object *__call_snc_method(acpi_handle handle, char *method, |
722 | u64 *value) | ||
696 | { | 723 | { |
697 | struct acpi_buffer output; | 724 | union acpi_object *result = NULL; |
698 | union acpi_object out_obj; | 725 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; |
699 | acpi_status status; | 726 | acpi_status status; |
700 | 727 | ||
701 | output.length = sizeof(out_obj); | 728 | if (value) { |
702 | output.pointer = &out_obj; | 729 | struct acpi_object_list params; |
730 | union acpi_object in; | ||
731 | in.type = ACPI_TYPE_INTEGER; | ||
732 | in.integer.value = *value; | ||
733 | params.count = 1; | ||
734 | params.pointer = ∈ | ||
735 | status = acpi_evaluate_object(handle, method, ¶ms, &output); | ||
736 | dprintk("__call_snc_method: [%s:0x%.8x%.8x]\n", method, | ||
737 | (unsigned int)(*value >> 32), | ||
738 | (unsigned int)*value & 0xffffffff); | ||
739 | } else { | ||
740 | status = acpi_evaluate_object(handle, method, NULL, &output); | ||
741 | dprintk("__call_snc_method: [%s]\n", method); | ||
742 | } | ||
703 | 743 | ||
704 | status = acpi_evaluate_object(handle, name, NULL, &output); | 744 | if (ACPI_FAILURE(status)) { |
705 | if ((status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER)) { | 745 | pr_err("Failed to evaluate [%s]\n", method); |
706 | *result = out_obj.integer.value; | 746 | return NULL; |
707 | return 0; | ||
708 | } | 747 | } |
709 | 748 | ||
710 | pr_warn("acpi_callreadfunc failed\n"); | 749 | result = (union acpi_object *) output.pointer; |
750 | if (!result) | ||
751 | dprintk("No return object [%s]\n", method); | ||
711 | 752 | ||
712 | return -1; | 753 | return result; |
713 | } | 754 | } |
714 | 755 | ||
715 | static int acpi_callsetfunc(acpi_handle handle, char *name, int value, | 756 | static int sony_nc_int_call(acpi_handle handle, char *name, int *value, |
716 | int *result) | 757 | int *result) |
717 | { | 758 | { |
718 | struct acpi_object_list params; | 759 | union acpi_object *object = NULL; |
719 | union acpi_object in_obj; | 760 | if (value) { |
720 | struct acpi_buffer output; | 761 | u64 v = *value; |
721 | union acpi_object out_obj; | 762 | object = __call_snc_method(handle, name, &v); |
722 | acpi_status status; | 763 | } else |
723 | 764 | object = __call_snc_method(handle, name, NULL); | |
724 | params.count = 1; | ||
725 | params.pointer = &in_obj; | ||
726 | in_obj.type = ACPI_TYPE_INTEGER; | ||
727 | in_obj.integer.value = value; | ||
728 | 765 | ||
729 | output.length = sizeof(out_obj); | 766 | if (!object) |
730 | output.pointer = &out_obj; | 767 | return -EINVAL; |
731 | 768 | ||
732 | status = acpi_evaluate_object(handle, name, ¶ms, &output); | 769 | if (object->type != ACPI_TYPE_INTEGER) { |
733 | if (status == AE_OK) { | 770 | pr_warn("Invalid acpi_object: expected 0x%x got 0x%x\n", |
734 | if (result != NULL) { | 771 | ACPI_TYPE_INTEGER, object->type); |
735 | if (out_obj.type != ACPI_TYPE_INTEGER) { | 772 | kfree(object); |
736 | pr_warn("acpi_evaluate_object bad return type\n"); | 773 | return -EINVAL; |
737 | return -1; | ||
738 | } | ||
739 | *result = out_obj.integer.value; | ||
740 | } | ||
741 | return 0; | ||
742 | } | 774 | } |
743 | 775 | ||
744 | pr_warn("acpi_evaluate_object failed\n"); | 776 | if (result) |
777 | *result = object->integer.value; | ||
778 | |||
779 | kfree(object); | ||
780 | return 0; | ||
781 | } | ||
782 | |||
783 | #define MIN(a, b) (a > b ? b : a) | ||
784 | static int sony_nc_buffer_call(acpi_handle handle, char *name, u64 *value, | ||
785 | void *buffer, size_t buflen) | ||
786 | { | ||
787 | size_t len = len; | ||
788 | union acpi_object *object = __call_snc_method(handle, name, value); | ||
789 | |||
790 | if (!object) | ||
791 | return -EINVAL; | ||
792 | |||
793 | if (object->type == ACPI_TYPE_BUFFER) | ||
794 | len = MIN(buflen, object->buffer.length); | ||
795 | |||
796 | else if (object->type == ACPI_TYPE_INTEGER) | ||
797 | len = MIN(buflen, sizeof(object->integer.value)); | ||
798 | |||
799 | else { | ||
800 | pr_warn("Invalid acpi_object: expected 0x%x got 0x%x\n", | ||
801 | ACPI_TYPE_BUFFER, object->type); | ||
802 | kfree(object); | ||
803 | return -EINVAL; | ||
804 | } | ||
745 | 805 | ||
746 | return -1; | 806 | memcpy(buffer, object->buffer.pointer, len); |
807 | kfree(object); | ||
808 | return 0; | ||
747 | } | 809 | } |
748 | 810 | ||
749 | struct sony_nc_handles { | 811 | struct sony_nc_handles { |
@@ -770,16 +832,17 @@ static ssize_t sony_nc_handles_show(struct device *dev, | |||
770 | 832 | ||
771 | static int sony_nc_handles_setup(struct platform_device *pd) | 833 | static int sony_nc_handles_setup(struct platform_device *pd) |
772 | { | 834 | { |
773 | int i; | 835 | int i, r, result, arg; |
774 | int result; | ||
775 | 836 | ||
776 | handles = kzalloc(sizeof(*handles), GFP_KERNEL); | 837 | handles = kzalloc(sizeof(*handles), GFP_KERNEL); |
777 | if (!handles) | 838 | if (!handles) |
778 | return -ENOMEM; | 839 | return -ENOMEM; |
779 | 840 | ||
780 | for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { | 841 | for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { |
781 | if (!acpi_callsetfunc(sony_nc_acpi_handle, | 842 | arg = i + 0x20; |
782 | "SN00", i + 0x20, &result)) { | 843 | r = sony_nc_int_call(sony_nc_acpi_handle, "SN00", &arg, |
844 | &result); | ||
845 | if (!r) { | ||
783 | dprintk("caching handle 0x%.4x (offset: 0x%.2x)\n", | 846 | dprintk("caching handle 0x%.4x (offset: 0x%.2x)\n", |
784 | result, i); | 847 | result, i); |
785 | handles->cap[i] = result; | 848 | handles->cap[i] = result; |
@@ -819,8 +882,8 @@ static int sony_find_snc_handle(int handle) | |||
819 | int i; | 882 | int i; |
820 | 883 | ||
821 | /* not initialized yet, return early */ | 884 | /* not initialized yet, return early */ |
822 | if (!handles) | 885 | if (!handles || !handle) |
823 | return -1; | 886 | return -EINVAL; |
824 | 887 | ||
825 | for (i = 0; i < 0x10; i++) { | 888 | for (i = 0; i < 0x10; i++) { |
826 | if (handles->cap[i] == handle) { | 889 | if (handles->cap[i] == handle) { |
@@ -830,21 +893,20 @@ static int sony_find_snc_handle(int handle) | |||
830 | } | 893 | } |
831 | } | 894 | } |
832 | dprintk("handle 0x%.4x not found\n", handle); | 895 | dprintk("handle 0x%.4x not found\n", handle); |
833 | return -1; | 896 | return -EINVAL; |
834 | } | 897 | } |
835 | 898 | ||
836 | static int sony_call_snc_handle(int handle, int argument, int *result) | 899 | static int sony_call_snc_handle(int handle, int argument, int *result) |
837 | { | 900 | { |
838 | int ret = 0; | 901 | int arg, ret = 0; |
839 | int offset = sony_find_snc_handle(handle); | 902 | int offset = sony_find_snc_handle(handle); |
840 | 903 | ||
841 | if (offset < 0) | 904 | if (offset < 0) |
842 | return -1; | 905 | return offset; |
843 | 906 | ||
844 | ret = acpi_callsetfunc(sony_nc_acpi_handle, "SN07", offset | argument, | 907 | arg = offset | argument; |
845 | result); | 908 | ret = sony_nc_int_call(sony_nc_acpi_handle, "SN07", &arg, result); |
846 | dprintk("called SN07 with 0x%.4x (result: 0x%.4x)\n", offset | argument, | 909 | dprintk("called SN07 with 0x%.4x (result: 0x%.4x)\n", arg, *result); |
847 | *result); | ||
848 | return ret; | 910 | return ret; |
849 | } | 911 | } |
850 | 912 | ||
@@ -889,14 +951,16 @@ static int boolean_validate(const int direction, const int value) | |||
889 | static ssize_t sony_nc_sysfs_show(struct device *dev, struct device_attribute *attr, | 951 | static ssize_t sony_nc_sysfs_show(struct device *dev, struct device_attribute *attr, |
890 | char *buffer) | 952 | char *buffer) |
891 | { | 953 | { |
892 | int value; | 954 | int value, ret = 0; |
893 | struct sony_nc_value *item = | 955 | struct sony_nc_value *item = |
894 | container_of(attr, struct sony_nc_value, devattr); | 956 | container_of(attr, struct sony_nc_value, devattr); |
895 | 957 | ||
896 | if (!*item->acpiget) | 958 | if (!*item->acpiget) |
897 | return -EIO; | 959 | return -EIO; |
898 | 960 | ||
899 | if (acpi_callgetfunc(sony_nc_acpi_handle, *item->acpiget, &value) < 0) | 961 | ret = sony_nc_int_call(sony_nc_acpi_handle, *item->acpiget, NULL, |
962 | &value); | ||
963 | if (ret < 0) | ||
900 | return -EIO; | 964 | return -EIO; |
901 | 965 | ||
902 | if (item->validate) | 966 | if (item->validate) |
@@ -909,7 +973,8 @@ static ssize_t sony_nc_sysfs_store(struct device *dev, | |||
909 | struct device_attribute *attr, | 973 | struct device_attribute *attr, |
910 | const char *buffer, size_t count) | 974 | const char *buffer, size_t count) |
911 | { | 975 | { |
912 | int value; | 976 | unsigned long value = 0; |
977 | int ret = 0; | ||
913 | struct sony_nc_value *item = | 978 | struct sony_nc_value *item = |
914 | container_of(attr, struct sony_nc_value, devattr); | 979 | container_of(attr, struct sony_nc_value, devattr); |
915 | 980 | ||
@@ -919,7 +984,8 @@ static ssize_t sony_nc_sysfs_store(struct device *dev, | |||
919 | if (count > 31) | 984 | if (count > 31) |
920 | return -EINVAL; | 985 | return -EINVAL; |
921 | 986 | ||
922 | value = simple_strtoul(buffer, NULL, 10); | 987 | if (kstrtoul(buffer, 10, &value)) |
988 | return -EINVAL; | ||
923 | 989 | ||
924 | if (item->validate) | 990 | if (item->validate) |
925 | value = item->validate(SNC_VALIDATE_IN, value); | 991 | value = item->validate(SNC_VALIDATE_IN, value); |
@@ -927,8 +993,11 @@ static ssize_t sony_nc_sysfs_store(struct device *dev, | |||
927 | if (value < 0) | 993 | if (value < 0) |
928 | return value; | 994 | return value; |
929 | 995 | ||
930 | if (acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset, value, NULL) < 0) | 996 | ret = sony_nc_int_call(sony_nc_acpi_handle, *item->acpiset, |
997 | (int *)&value, NULL); | ||
998 | if (ret < 0) | ||
931 | return -EIO; | 999 | return -EIO; |
1000 | |||
932 | item->value = value; | 1001 | item->value = value; |
933 | item->valid = 1; | 1002 | item->valid = 1; |
934 | return count; | 1003 | return count; |
@@ -948,15 +1017,15 @@ struct sony_backlight_props sony_bl_props; | |||
948 | 1017 | ||
949 | static int sony_backlight_update_status(struct backlight_device *bd) | 1018 | static int sony_backlight_update_status(struct backlight_device *bd) |
950 | { | 1019 | { |
951 | return acpi_callsetfunc(sony_nc_acpi_handle, "SBRT", | 1020 | int arg = bd->props.brightness + 1; |
952 | bd->props.brightness + 1, NULL); | 1021 | return sony_nc_int_call(sony_nc_acpi_handle, "SBRT", &arg, NULL); |
953 | } | 1022 | } |
954 | 1023 | ||
955 | static int sony_backlight_get_brightness(struct backlight_device *bd) | 1024 | static int sony_backlight_get_brightness(struct backlight_device *bd) |
956 | { | 1025 | { |
957 | int value; | 1026 | int value; |
958 | 1027 | ||
959 | if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value)) | 1028 | if (sony_nc_int_call(sony_nc_acpi_handle, "GBRT", NULL, &value)) |
960 | return 0; | 1029 | return 0; |
961 | /* brightness levels are 1-based, while backlight ones are 0-based */ | 1030 | /* brightness levels are 1-based, while backlight ones are 0-based */ |
962 | return value - 1; | 1031 | return value - 1; |
@@ -1024,10 +1093,14 @@ static struct sony_nc_event sony_100_events[] = { | |||
1024 | { 0x06, SONYPI_EVENT_FNKEY_RELEASED }, | 1093 | { 0x06, SONYPI_EVENT_FNKEY_RELEASED }, |
1025 | { 0x87, SONYPI_EVENT_FNKEY_F7 }, | 1094 | { 0x87, SONYPI_EVENT_FNKEY_F7 }, |
1026 | { 0x07, SONYPI_EVENT_FNKEY_RELEASED }, | 1095 | { 0x07, SONYPI_EVENT_FNKEY_RELEASED }, |
1096 | { 0x88, SONYPI_EVENT_FNKEY_F8 }, | ||
1097 | { 0x08, SONYPI_EVENT_FNKEY_RELEASED }, | ||
1027 | { 0x89, SONYPI_EVENT_FNKEY_F9 }, | 1098 | { 0x89, SONYPI_EVENT_FNKEY_F9 }, |
1028 | { 0x09, SONYPI_EVENT_FNKEY_RELEASED }, | 1099 | { 0x09, SONYPI_EVENT_FNKEY_RELEASED }, |
1029 | { 0x8A, SONYPI_EVENT_FNKEY_F10 }, | 1100 | { 0x8A, SONYPI_EVENT_FNKEY_F10 }, |
1030 | { 0x0A, SONYPI_EVENT_FNKEY_RELEASED }, | 1101 | { 0x0A, SONYPI_EVENT_FNKEY_RELEASED }, |
1102 | { 0x8B, SONYPI_EVENT_FNKEY_F11 }, | ||
1103 | { 0x0B, SONYPI_EVENT_FNKEY_RELEASED }, | ||
1031 | { 0x8C, SONYPI_EVENT_FNKEY_F12 }, | 1104 | { 0x8C, SONYPI_EVENT_FNKEY_F12 }, |
1032 | { 0x0C, SONYPI_EVENT_FNKEY_RELEASED }, | 1105 | { 0x0C, SONYPI_EVENT_FNKEY_RELEASED }, |
1033 | { 0x9d, SONYPI_EVENT_ZOOM_PRESSED }, | 1106 | { 0x9d, SONYPI_EVENT_ZOOM_PRESSED }, |
@@ -1063,63 +1136,116 @@ static struct sony_nc_event sony_127_events[] = { | |||
1063 | { 0, 0 }, | 1136 | { 0, 0 }, |
1064 | }; | 1137 | }; |
1065 | 1138 | ||
1139 | static int sony_nc_hotkeys_decode(u32 event, unsigned int handle) | ||
1140 | { | ||
1141 | int ret = -EINVAL; | ||
1142 | unsigned int result = 0; | ||
1143 | struct sony_nc_event *key_event; | ||
1144 | |||
1145 | if (sony_call_snc_handle(handle, 0x200, &result)) { | ||
1146 | dprintk("Unable to decode event 0x%.2x 0x%.2x\n", handle, | ||
1147 | event); | ||
1148 | return -EINVAL; | ||
1149 | } | ||
1150 | |||
1151 | result &= 0xFF; | ||
1152 | |||
1153 | if (handle == 0x0100) | ||
1154 | key_event = sony_100_events; | ||
1155 | else | ||
1156 | key_event = sony_127_events; | ||
1157 | |||
1158 | for (; key_event->data; key_event++) { | ||
1159 | if (key_event->data == result) { | ||
1160 | ret = key_event->event; | ||
1161 | break; | ||
1162 | } | ||
1163 | } | ||
1164 | |||
1165 | if (!key_event->data) | ||
1166 | pr_info("Unknown hotkey 0x%.2x/0x%.2x (handle 0x%.2x)\n", | ||
1167 | event, result, handle); | ||
1168 | |||
1169 | return ret; | ||
1170 | } | ||
1171 | |||
1066 | /* | 1172 | /* |
1067 | * ACPI callbacks | 1173 | * ACPI callbacks |
1068 | */ | 1174 | */ |
1069 | static void sony_nc_notify(struct acpi_device *device, u32 event) | 1175 | static void sony_nc_notify(struct acpi_device *device, u32 event) |
1070 | { | 1176 | { |
1071 | u32 ev = event; | 1177 | u32 real_ev = event; |
1178 | u8 ev_type = 0; | ||
1179 | dprintk("sony_nc_notify, event: 0x%.2x\n", event); | ||
1180 | |||
1181 | if (event >= 0x90) { | ||
1182 | unsigned int result = 0; | ||
1183 | unsigned int arg = 0; | ||
1184 | unsigned int handle = 0; | ||
1185 | unsigned int offset = event - 0x90; | ||
1186 | |||
1187 | if (offset >= ARRAY_SIZE(handles->cap)) { | ||
1188 | pr_err("Event 0x%x outside of capabilities list\n", | ||
1189 | event); | ||
1190 | return; | ||
1191 | } | ||
1192 | handle = handles->cap[offset]; | ||
1193 | |||
1194 | /* list of handles known for generating events */ | ||
1195 | switch (handle) { | ||
1196 | /* hotkey event */ | ||
1197 | case 0x0100: | ||
1198 | case 0x0127: | ||
1199 | ev_type = 1; | ||
1200 | real_ev = sony_nc_hotkeys_decode(event, handle); | ||
1201 | |||
1202 | if (real_ev > 0) | ||
1203 | sony_laptop_report_input_event(real_ev); | ||
1204 | else | ||
1205 | /* restore the original event for reporting */ | ||
1206 | real_ev = event; | ||
1072 | 1207 | ||
1073 | if (ev >= 0x90) { | 1208 | break; |
1074 | /* New-style event */ | ||
1075 | int result; | ||
1076 | int key_handle = 0; | ||
1077 | ev -= 0x90; | ||
1078 | |||
1079 | if (sony_find_snc_handle(0x100) == ev) | ||
1080 | key_handle = 0x100; | ||
1081 | if (sony_find_snc_handle(0x127) == ev) | ||
1082 | key_handle = 0x127; | ||
1083 | |||
1084 | if (key_handle) { | ||
1085 | struct sony_nc_event *key_event; | ||
1086 | |||
1087 | if (sony_call_snc_handle(key_handle, 0x200, &result)) { | ||
1088 | dprintk("sony_nc_notify, unable to decode" | ||
1089 | " event 0x%.2x 0x%.2x\n", key_handle, | ||
1090 | ev); | ||
1091 | /* restore the original event */ | ||
1092 | ev = event; | ||
1093 | } else { | ||
1094 | ev = result & 0xFF; | ||
1095 | |||
1096 | if (key_handle == 0x100) | ||
1097 | key_event = sony_100_events; | ||
1098 | else | ||
1099 | key_event = sony_127_events; | ||
1100 | |||
1101 | for (; key_event->data; key_event++) { | ||
1102 | if (key_event->data == ev) { | ||
1103 | ev = key_event->event; | ||
1104 | break; | ||
1105 | } | ||
1106 | } | ||
1107 | 1209 | ||
1108 | if (!key_event->data) | 1210 | /* wlan switch */ |
1109 | pr_info("Unknown event: 0x%x 0x%x\n", | 1211 | case 0x0124: |
1110 | key_handle, ev); | 1212 | case 0x0135: |
1111 | else | 1213 | /* events on this handle are reported when the |
1112 | sony_laptop_report_input_event(ev); | 1214 | * switch changes position or for battery |
1113 | } | 1215 | * events. We'll notify both of them but only |
1114 | } else if (sony_find_snc_handle(sony_rfkill_handle) == ev) { | 1216 | * update the rfkill device status when the |
1115 | sony_nc_rfkill_update(); | 1217 | * switch is moved. |
1116 | return; | 1218 | */ |
1219 | ev_type = 2; | ||
1220 | sony_call_snc_handle(handle, 0x0100, &result); | ||
1221 | real_ev = result & 0x03; | ||
1222 | |||
1223 | /* hw switch event */ | ||
1224 | if (real_ev == 1) | ||
1225 | sony_nc_rfkill_update(); | ||
1226 | |||
1227 | break; | ||
1228 | |||
1229 | default: | ||
1230 | dprintk("Unknown event 0x%x for handle 0x%x\n", | ||
1231 | event, handle); | ||
1232 | break; | ||
1117 | } | 1233 | } |
1118 | } else | ||
1119 | sony_laptop_report_input_event(ev); | ||
1120 | 1234 | ||
1121 | dprintk("sony_nc_notify, event: 0x%.2x\n", ev); | 1235 | /* clear the event (and the event reason when present) */ |
1122 | acpi_bus_generate_proc_event(sony_nc_acpi_device, 1, ev); | 1236 | arg = 1 << offset; |
1237 | sony_nc_int_call(sony_nc_acpi_handle, "SN05", &arg, &result); | ||
1238 | |||
1239 | } else { | ||
1240 | /* old style event */ | ||
1241 | ev_type = 1; | ||
1242 | sony_laptop_report_input_event(real_ev); | ||
1243 | } | ||
1244 | |||
1245 | acpi_bus_generate_proc_event(sony_nc_acpi_device, ev_type, real_ev); | ||
1246 | |||
1247 | acpi_bus_generate_netlink_event(sony_nc_acpi_device->pnp.device_class, | ||
1248 | dev_name(&sony_nc_acpi_device->dev), ev_type, real_ev); | ||
1123 | } | 1249 | } |
1124 | 1250 | ||
1125 | static acpi_status sony_walk_callback(acpi_handle handle, u32 level, | 1251 | static acpi_status sony_walk_callback(acpi_handle handle, u32 level, |
@@ -1140,20 +1266,190 @@ static acpi_status sony_walk_callback(acpi_handle handle, u32 level, | |||
1140 | /* | 1266 | /* |
1141 | * ACPI device | 1267 | * ACPI device |
1142 | */ | 1268 | */ |
1143 | static int sony_nc_function_setup(struct acpi_device *device) | 1269 | static void sony_nc_function_setup(struct acpi_device *device, |
1270 | struct platform_device *pf_device) | ||
1144 | { | 1271 | { |
1145 | int result; | 1272 | unsigned int i, result, bitmask, arg; |
1273 | |||
1274 | if (!handles) | ||
1275 | return; | ||
1276 | |||
1277 | /* setup found handles here */ | ||
1278 | for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { | ||
1279 | unsigned int handle = handles->cap[i]; | ||
1280 | |||
1281 | if (!handle) | ||
1282 | continue; | ||
1283 | |||
1284 | dprintk("setting up handle 0x%.4x\n", handle); | ||
1285 | |||
1286 | switch (handle) { | ||
1287 | case 0x0100: | ||
1288 | case 0x0101: | ||
1289 | case 0x0127: | ||
1290 | /* setup hotkeys */ | ||
1291 | sony_call_snc_handle(handle, 0, &result); | ||
1292 | break; | ||
1293 | case 0x0102: | ||
1294 | /* setup hotkeys */ | ||
1295 | sony_call_snc_handle(handle, 0x100, &result); | ||
1296 | break; | ||
1297 | case 0x0105: | ||
1298 | case 0x0148: | ||
1299 | /* touchpad enable/disable */ | ||
1300 | result = sony_nc_touchpad_setup(pf_device, handle); | ||
1301 | if (result) | ||
1302 | pr_err("couldn't set up touchpad control function (%d)\n", | ||
1303 | result); | ||
1304 | break; | ||
1305 | case 0x0115: | ||
1306 | case 0x0136: | ||
1307 | case 0x013f: | ||
1308 | result = sony_nc_battery_care_setup(pf_device, handle); | ||
1309 | if (result) | ||
1310 | pr_err("couldn't set up battery care function (%d)\n", | ||
1311 | result); | ||
1312 | break; | ||
1313 | case 0x0119: | ||
1314 | result = sony_nc_lid_resume_setup(pf_device); | ||
1315 | if (result) | ||
1316 | pr_err("couldn't set up lid resume function (%d)\n", | ||
1317 | result); | ||
1318 | break; | ||
1319 | case 0x0122: | ||
1320 | result = sony_nc_thermal_setup(pf_device); | ||
1321 | if (result) | ||
1322 | pr_err("couldn't set up thermal profile function (%d)\n", | ||
1323 | result); | ||
1324 | break; | ||
1325 | case 0x0131: | ||
1326 | result = sony_nc_highspeed_charging_setup(pf_device); | ||
1327 | if (result) | ||
1328 | pr_err("couldn't set up high speed charging function (%d)\n", | ||
1329 | result); | ||
1330 | break; | ||
1331 | case 0x0124: | ||
1332 | case 0x0135: | ||
1333 | result = sony_nc_rfkill_setup(device, handle); | ||
1334 | if (result) | ||
1335 | pr_err("couldn't set up rfkill support (%d)\n", | ||
1336 | result); | ||
1337 | break; | ||
1338 | case 0x0137: | ||
1339 | case 0x0143: | ||
1340 | result = sony_nc_kbd_backlight_setup(pf_device, handle); | ||
1341 | if (result) | ||
1342 | pr_err("couldn't set up keyboard backlight function (%d)\n", | ||
1343 | result); | ||
1344 | break; | ||
1345 | default: | ||
1346 | continue; | ||
1347 | } | ||
1348 | } | ||
1146 | 1349 | ||
1147 | /* Enable all events */ | 1350 | /* Enable all events */ |
1148 | acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0xffff, &result); | 1351 | arg = 0x10; |
1352 | if (!sony_nc_int_call(sony_nc_acpi_handle, "SN00", &arg, &bitmask)) | ||
1353 | sony_nc_int_call(sony_nc_acpi_handle, "SN02", &bitmask, | ||
1354 | &result); | ||
1355 | } | ||
1356 | |||
1357 | static void sony_nc_function_cleanup(struct platform_device *pd) | ||
1358 | { | ||
1359 | unsigned int i, result, bitmask, handle; | ||
1149 | 1360 | ||
1150 | /* Setup hotkeys */ | 1361 | /* get enabled events and disable them */ |
1151 | sony_call_snc_handle(0x0100, 0, &result); | 1362 | sony_nc_int_call(sony_nc_acpi_handle, "SN01", NULL, &bitmask); |
1152 | sony_call_snc_handle(0x0101, 0, &result); | 1363 | sony_nc_int_call(sony_nc_acpi_handle, "SN03", &bitmask, &result); |
1153 | sony_call_snc_handle(0x0102, 0x100, &result); | ||
1154 | sony_call_snc_handle(0x0127, 0, &result); | ||
1155 | 1364 | ||
1156 | return 0; | 1365 | /* cleanup handles here */ |
1366 | for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { | ||
1367 | |||
1368 | handle = handles->cap[i]; | ||
1369 | |||
1370 | if (!handle) | ||
1371 | continue; | ||
1372 | |||
1373 | switch (handle) { | ||
1374 | case 0x0105: | ||
1375 | case 0x0148: | ||
1376 | sony_nc_touchpad_cleanup(pd); | ||
1377 | break; | ||
1378 | case 0x0115: | ||
1379 | case 0x0136: | ||
1380 | case 0x013f: | ||
1381 | sony_nc_battery_care_cleanup(pd); | ||
1382 | break; | ||
1383 | case 0x0119: | ||
1384 | sony_nc_lid_resume_cleanup(pd); | ||
1385 | break; | ||
1386 | case 0x0122: | ||
1387 | sony_nc_thermal_cleanup(pd); | ||
1388 | break; | ||
1389 | case 0x0131: | ||
1390 | sony_nc_highspeed_charging_cleanup(pd); | ||
1391 | break; | ||
1392 | case 0x0124: | ||
1393 | case 0x0135: | ||
1394 | sony_nc_rfkill_cleanup(); | ||
1395 | break; | ||
1396 | case 0x0137: | ||
1397 | case 0x0143: | ||
1398 | sony_nc_kbd_backlight_cleanup(pd); | ||
1399 | break; | ||
1400 | default: | ||
1401 | continue; | ||
1402 | } | ||
1403 | } | ||
1404 | |||
1405 | /* finally cleanup the handles list */ | ||
1406 | sony_nc_handles_cleanup(pd); | ||
1407 | } | ||
1408 | |||
1409 | static void sony_nc_function_resume(void) | ||
1410 | { | ||
1411 | unsigned int i, result, bitmask, arg; | ||
1412 | |||
1413 | dprintk("Resuming SNC device\n"); | ||
1414 | |||
1415 | for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { | ||
1416 | unsigned int handle = handles->cap[i]; | ||
1417 | |||
1418 | if (!handle) | ||
1419 | continue; | ||
1420 | |||
1421 | switch (handle) { | ||
1422 | case 0x0100: | ||
1423 | case 0x0101: | ||
1424 | case 0x0127: | ||
1425 | /* re-enable hotkeys */ | ||
1426 | sony_call_snc_handle(handle, 0, &result); | ||
1427 | break; | ||
1428 | case 0x0102: | ||
1429 | /* re-enable hotkeys */ | ||
1430 | sony_call_snc_handle(handle, 0x100, &result); | ||
1431 | break; | ||
1432 | case 0x0122: | ||
1433 | sony_nc_thermal_resume(); | ||
1434 | break; | ||
1435 | case 0x0124: | ||
1436 | case 0x0135: | ||
1437 | sony_nc_rfkill_update(); | ||
1438 | break; | ||
1439 | case 0x0137: | ||
1440 | case 0x0143: | ||
1441 | sony_nc_kbd_backlight_resume(); | ||
1442 | break; | ||
1443 | default: | ||
1444 | continue; | ||
1445 | } | ||
1446 | } | ||
1447 | |||
1448 | /* Enable all events */ | ||
1449 | arg = 0x10; | ||
1450 | if (!sony_nc_int_call(sony_nc_acpi_handle, "SN00", &arg, &bitmask)) | ||
1451 | sony_nc_int_call(sony_nc_acpi_handle, "SN02", &bitmask, | ||
1452 | &result); | ||
1157 | } | 1453 | } |
1158 | 1454 | ||
1159 | static int sony_nc_resume(struct acpi_device *device) | 1455 | static int sony_nc_resume(struct acpi_device *device) |
@@ -1166,8 +1462,8 @@ static int sony_nc_resume(struct acpi_device *device) | |||
1166 | 1462 | ||
1167 | if (!item->valid) | 1463 | if (!item->valid) |
1168 | continue; | 1464 | continue; |
1169 | ret = acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset, | 1465 | ret = sony_nc_int_call(sony_nc_acpi_handle, *item->acpiset, |
1170 | item->value, NULL); | 1466 | &item->value, NULL); |
1171 | if (ret < 0) { | 1467 | if (ret < 0) { |
1172 | pr_err("%s: %d\n", __func__, ret); | 1468 | pr_err("%s: %d\n", __func__, ret); |
1173 | break; | 1469 | break; |
@@ -1176,21 +1472,14 @@ static int sony_nc_resume(struct acpi_device *device) | |||
1176 | 1472 | ||
1177 | if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON", | 1473 | if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON", |
1178 | &handle))) { | 1474 | &handle))) { |
1179 | if (acpi_callsetfunc(sony_nc_acpi_handle, "ECON", 1, NULL)) | 1475 | int arg = 1; |
1476 | if (sony_nc_int_call(sony_nc_acpi_handle, "ECON", &arg, NULL)) | ||
1180 | dprintk("ECON Method failed\n"); | 1477 | dprintk("ECON Method failed\n"); |
1181 | } | 1478 | } |
1182 | 1479 | ||
1183 | if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00", | 1480 | if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00", |
1184 | &handle))) { | 1481 | &handle))) |
1185 | dprintk("Doing SNC setup\n"); | 1482 | sony_nc_function_resume(); |
1186 | sony_nc_function_setup(device); | ||
1187 | } | ||
1188 | |||
1189 | /* re-read rfkill state */ | ||
1190 | sony_nc_rfkill_update(); | ||
1191 | |||
1192 | /* restore kbd backlight states */ | ||
1193 | sony_nc_kbd_backlight_resume(); | ||
1194 | 1483 | ||
1195 | return 0; | 1484 | return 0; |
1196 | } | 1485 | } |
@@ -1213,7 +1502,7 @@ static int sony_nc_rfkill_set(void *data, bool blocked) | |||
1213 | int argument = sony_rfkill_address[(long) data] + 0x100; | 1502 | int argument = sony_rfkill_address[(long) data] + 0x100; |
1214 | 1503 | ||
1215 | if (!blocked) | 1504 | if (!blocked) |
1216 | argument |= 0xff0000; | 1505 | argument |= 0x030000; |
1217 | 1506 | ||
1218 | return sony_call_snc_handle(sony_rfkill_handle, argument, &result); | 1507 | return sony_call_snc_handle(sony_rfkill_handle, argument, &result); |
1219 | } | 1508 | } |
@@ -1230,7 +1519,7 @@ static int sony_nc_setup_rfkill(struct acpi_device *device, | |||
1230 | enum rfkill_type type; | 1519 | enum rfkill_type type; |
1231 | const char *name; | 1520 | const char *name; |
1232 | int result; | 1521 | int result; |
1233 | bool hwblock; | 1522 | bool hwblock, swblock; |
1234 | 1523 | ||
1235 | switch (nc_type) { | 1524 | switch (nc_type) { |
1236 | case SONY_WIFI: | 1525 | case SONY_WIFI: |
@@ -1258,8 +1547,21 @@ static int sony_nc_setup_rfkill(struct acpi_device *device, | |||
1258 | if (!rfk) | 1547 | if (!rfk) |
1259 | return -ENOMEM; | 1548 | return -ENOMEM; |
1260 | 1549 | ||
1261 | sony_call_snc_handle(sony_rfkill_handle, 0x200, &result); | 1550 | if (sony_call_snc_handle(sony_rfkill_handle, 0x200, &result) < 0) { |
1551 | rfkill_destroy(rfk); | ||
1552 | return -1; | ||
1553 | } | ||
1262 | hwblock = !(result & 0x1); | 1554 | hwblock = !(result & 0x1); |
1555 | |||
1556 | if (sony_call_snc_handle(sony_rfkill_handle, | ||
1557 | sony_rfkill_address[nc_type], | ||
1558 | &result) < 0) { | ||
1559 | rfkill_destroy(rfk); | ||
1560 | return -1; | ||
1561 | } | ||
1562 | swblock = !(result & 0x2); | ||
1563 | |||
1564 | rfkill_init_sw_state(rfk, swblock); | ||
1263 | rfkill_set_hw_state(rfk, hwblock); | 1565 | rfkill_set_hw_state(rfk, hwblock); |
1264 | 1566 | ||
1265 | err = rfkill_register(rfk); | 1567 | err = rfkill_register(rfk); |
@@ -1295,101 +1597,79 @@ static void sony_nc_rfkill_update(void) | |||
1295 | 1597 | ||
1296 | sony_call_snc_handle(sony_rfkill_handle, argument, &result); | 1598 | sony_call_snc_handle(sony_rfkill_handle, argument, &result); |
1297 | rfkill_set_states(sony_rfkill_devices[i], | 1599 | rfkill_set_states(sony_rfkill_devices[i], |
1298 | !(result & 0xf), false); | 1600 | !(result & 0x2), false); |
1299 | } | 1601 | } |
1300 | } | 1602 | } |
1301 | 1603 | ||
1302 | static void sony_nc_rfkill_setup(struct acpi_device *device) | 1604 | static int sony_nc_rfkill_setup(struct acpi_device *device, |
1605 | unsigned int handle) | ||
1303 | { | 1606 | { |
1304 | int offset; | 1607 | u64 offset; |
1305 | u8 dev_code, i; | 1608 | int i; |
1306 | acpi_status status; | 1609 | unsigned char buffer[32] = { 0 }; |
1307 | struct acpi_object_list params; | ||
1308 | union acpi_object in_obj; | ||
1309 | union acpi_object *device_enum; | ||
1310 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
1311 | |||
1312 | offset = sony_find_snc_handle(0x124); | ||
1313 | if (offset == -1) { | ||
1314 | offset = sony_find_snc_handle(0x135); | ||
1315 | if (offset == -1) | ||
1316 | return; | ||
1317 | else | ||
1318 | sony_rfkill_handle = 0x135; | ||
1319 | } else | ||
1320 | sony_rfkill_handle = 0x124; | ||
1321 | dprintk("Found rkfill handle: 0x%.4x\n", sony_rfkill_handle); | ||
1322 | |||
1323 | /* need to read the whole buffer returned by the acpi call to SN06 | ||
1324 | * here otherwise we may miss some features | ||
1325 | */ | ||
1326 | params.count = 1; | ||
1327 | params.pointer = &in_obj; | ||
1328 | in_obj.type = ACPI_TYPE_INTEGER; | ||
1329 | in_obj.integer.value = offset; | ||
1330 | status = acpi_evaluate_object(sony_nc_acpi_handle, "SN06", ¶ms, | ||
1331 | &buffer); | ||
1332 | if (ACPI_FAILURE(status)) { | ||
1333 | dprintk("Radio device enumeration failed\n"); | ||
1334 | return; | ||
1335 | } | ||
1336 | |||
1337 | device_enum = (union acpi_object *) buffer.pointer; | ||
1338 | if (!device_enum) { | ||
1339 | pr_err("No SN06 return object\n"); | ||
1340 | goto out_no_enum; | ||
1341 | } | ||
1342 | if (device_enum->type != ACPI_TYPE_BUFFER) { | ||
1343 | pr_err("Invalid SN06 return object 0x%.2x\n", | ||
1344 | device_enum->type); | ||
1345 | goto out_no_enum; | ||
1346 | } | ||
1347 | 1610 | ||
1348 | /* the buffer is filled with magic numbers describing the devices | 1611 | offset = sony_find_snc_handle(handle); |
1349 | * available, 0xff terminates the enumeration | 1612 | sony_rfkill_handle = handle; |
1613 | |||
1614 | i = sony_nc_buffer_call(sony_nc_acpi_handle, "SN06", &offset, buffer, | ||
1615 | 32); | ||
1616 | if (i < 0) | ||
1617 | return i; | ||
1618 | |||
1619 | /* The buffer is filled with magic numbers describing the devices | ||
1620 | * available, 0xff terminates the enumeration. | ||
1621 | * Known codes: | ||
1622 | * 0x00 WLAN | ||
1623 | * 0x10 BLUETOOTH | ||
1624 | * 0x20 WWAN GPRS-EDGE | ||
1625 | * 0x21 WWAN HSDPA | ||
1626 | * 0x22 WWAN EV-DO | ||
1627 | * 0x23 WWAN GPS | ||
1628 | * 0x25 Gobi WWAN no GPS | ||
1629 | * 0x26 Gobi WWAN + GPS | ||
1630 | * 0x28 Gobi WWAN no GPS | ||
1631 | * 0x29 Gobi WWAN + GPS | ||
1632 | * 0x30 WIMAX | ||
1633 | * 0x50 Gobi WWAN no GPS | ||
1634 | * 0x51 Gobi WWAN + GPS | ||
1635 | * 0x70 no SIM card slot | ||
1636 | * 0x71 SIM card slot | ||
1350 | */ | 1637 | */ |
1351 | for (i = 0; i < device_enum->buffer.length; i++) { | 1638 | for (i = 0; i < ARRAY_SIZE(buffer); i++) { |
1352 | 1639 | ||
1353 | dev_code = *(device_enum->buffer.pointer + i); | 1640 | if (buffer[i] == 0xff) |
1354 | if (dev_code == 0xff) | ||
1355 | break; | 1641 | break; |
1356 | 1642 | ||
1357 | dprintk("Radio devices, looking at 0x%.2x\n", dev_code); | 1643 | dprintk("Radio devices, found 0x%.2x\n", buffer[i]); |
1358 | 1644 | ||
1359 | if (dev_code == 0 && !sony_rfkill_devices[SONY_WIFI]) | 1645 | if (buffer[i] == 0 && !sony_rfkill_devices[SONY_WIFI]) |
1360 | sony_nc_setup_rfkill(device, SONY_WIFI); | 1646 | sony_nc_setup_rfkill(device, SONY_WIFI); |
1361 | 1647 | ||
1362 | if (dev_code == 0x10 && !sony_rfkill_devices[SONY_BLUETOOTH]) | 1648 | if (buffer[i] == 0x10 && !sony_rfkill_devices[SONY_BLUETOOTH]) |
1363 | sony_nc_setup_rfkill(device, SONY_BLUETOOTH); | 1649 | sony_nc_setup_rfkill(device, SONY_BLUETOOTH); |
1364 | 1650 | ||
1365 | if ((0xf0 & dev_code) == 0x20 && | 1651 | if (((0xf0 & buffer[i]) == 0x20 || |
1652 | (0xf0 & buffer[i]) == 0x50) && | ||
1366 | !sony_rfkill_devices[SONY_WWAN]) | 1653 | !sony_rfkill_devices[SONY_WWAN]) |
1367 | sony_nc_setup_rfkill(device, SONY_WWAN); | 1654 | sony_nc_setup_rfkill(device, SONY_WWAN); |
1368 | 1655 | ||
1369 | if (dev_code == 0x30 && !sony_rfkill_devices[SONY_WIMAX]) | 1656 | if (buffer[i] == 0x30 && !sony_rfkill_devices[SONY_WIMAX]) |
1370 | sony_nc_setup_rfkill(device, SONY_WIMAX); | 1657 | sony_nc_setup_rfkill(device, SONY_WIMAX); |
1371 | } | 1658 | } |
1372 | 1659 | return 0; | |
1373 | out_no_enum: | ||
1374 | kfree(buffer.pointer); | ||
1375 | return; | ||
1376 | } | 1660 | } |
1377 | 1661 | ||
1378 | /* Keyboard backlight feature */ | 1662 | /* Keyboard backlight feature */ |
1379 | #define KBDBL_HANDLER 0x137 | ||
1380 | #define KBDBL_PRESENT 0xB00 | ||
1381 | #define SET_MODE 0xC00 | ||
1382 | #define SET_STATE 0xD00 | ||
1383 | #define SET_TIMEOUT 0xE00 | ||
1384 | |||
1385 | struct kbd_backlight { | 1663 | struct kbd_backlight { |
1386 | int mode; | 1664 | unsigned int handle; |
1387 | int timeout; | 1665 | unsigned int base; |
1666 | unsigned int mode; | ||
1667 | unsigned int timeout; | ||
1388 | struct device_attribute mode_attr; | 1668 | struct device_attribute mode_attr; |
1389 | struct device_attribute timeout_attr; | 1669 | struct device_attribute timeout_attr; |
1390 | }; | 1670 | }; |
1391 | 1671 | ||
1392 | static struct kbd_backlight *kbdbl_handle; | 1672 | static struct kbd_backlight *kbdbl_ctl; |
1393 | 1673 | ||
1394 | static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value) | 1674 | static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value) |
1395 | { | 1675 | { |
@@ -1398,15 +1678,15 @@ static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value) | |||
1398 | if (value > 1) | 1678 | if (value > 1) |
1399 | return -EINVAL; | 1679 | return -EINVAL; |
1400 | 1680 | ||
1401 | if (sony_call_snc_handle(KBDBL_HANDLER, | 1681 | if (sony_call_snc_handle(kbdbl_ctl->handle, |
1402 | (value << 0x10) | SET_MODE, &result)) | 1682 | (value << 0x10) | (kbdbl_ctl->base), &result)) |
1403 | return -EIO; | 1683 | return -EIO; |
1404 | 1684 | ||
1405 | /* Try to turn the light on/off immediately */ | 1685 | /* Try to turn the light on/off immediately */ |
1406 | sony_call_snc_handle(KBDBL_HANDLER, (value << 0x10) | SET_STATE, | 1686 | sony_call_snc_handle(kbdbl_ctl->handle, |
1407 | &result); | 1687 | (value << 0x10) | (kbdbl_ctl->base + 0x100), &result); |
1408 | 1688 | ||
1409 | kbdbl_handle->mode = value; | 1689 | kbdbl_ctl->mode = value; |
1410 | 1690 | ||
1411 | return 0; | 1691 | return 0; |
1412 | } | 1692 | } |
@@ -1421,7 +1701,7 @@ static ssize_t sony_nc_kbd_backlight_mode_store(struct device *dev, | |||
1421 | if (count > 31) | 1701 | if (count > 31) |
1422 | return -EINVAL; | 1702 | return -EINVAL; |
1423 | 1703 | ||
1424 | if (strict_strtoul(buffer, 10, &value)) | 1704 | if (kstrtoul(buffer, 10, &value)) |
1425 | return -EINVAL; | 1705 | return -EINVAL; |
1426 | 1706 | ||
1427 | ret = __sony_nc_kbd_backlight_mode_set(value); | 1707 | ret = __sony_nc_kbd_backlight_mode_set(value); |
@@ -1435,7 +1715,7 @@ static ssize_t sony_nc_kbd_backlight_mode_show(struct device *dev, | |||
1435 | struct device_attribute *attr, char *buffer) | 1715 | struct device_attribute *attr, char *buffer) |
1436 | { | 1716 | { |
1437 | ssize_t count = 0; | 1717 | ssize_t count = 0; |
1438 | count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_handle->mode); | 1718 | count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_ctl->mode); |
1439 | return count; | 1719 | return count; |
1440 | } | 1720 | } |
1441 | 1721 | ||
@@ -1446,11 +1726,11 @@ static int __sony_nc_kbd_backlight_timeout_set(u8 value) | |||
1446 | if (value > 3) | 1726 | if (value > 3) |
1447 | return -EINVAL; | 1727 | return -EINVAL; |
1448 | 1728 | ||
1449 | if (sony_call_snc_handle(KBDBL_HANDLER, | 1729 | if (sony_call_snc_handle(kbdbl_ctl->handle, (value << 0x10) | |
1450 | (value << 0x10) | SET_TIMEOUT, &result)) | 1730 | (kbdbl_ctl->base + 0x200), &result)) |
1451 | return -EIO; | 1731 | return -EIO; |
1452 | 1732 | ||
1453 | kbdbl_handle->timeout = value; | 1733 | kbdbl_ctl->timeout = value; |
1454 | 1734 | ||
1455 | return 0; | 1735 | return 0; |
1456 | } | 1736 | } |
@@ -1465,7 +1745,7 @@ static ssize_t sony_nc_kbd_backlight_timeout_store(struct device *dev, | |||
1465 | if (count > 31) | 1745 | if (count > 31) |
1466 | return -EINVAL; | 1746 | return -EINVAL; |
1467 | 1747 | ||
1468 | if (strict_strtoul(buffer, 10, &value)) | 1748 | if (kstrtoul(buffer, 10, &value)) |
1469 | return -EINVAL; | 1749 | return -EINVAL; |
1470 | 1750 | ||
1471 | ret = __sony_nc_kbd_backlight_timeout_set(value); | 1751 | ret = __sony_nc_kbd_backlight_timeout_set(value); |
@@ -1479,39 +1759,58 @@ static ssize_t sony_nc_kbd_backlight_timeout_show(struct device *dev, | |||
1479 | struct device_attribute *attr, char *buffer) | 1759 | struct device_attribute *attr, char *buffer) |
1480 | { | 1760 | { |
1481 | ssize_t count = 0; | 1761 | ssize_t count = 0; |
1482 | count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_handle->timeout); | 1762 | count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_ctl->timeout); |
1483 | return count; | 1763 | return count; |
1484 | } | 1764 | } |
1485 | 1765 | ||
1486 | static int sony_nc_kbd_backlight_setup(struct platform_device *pd) | 1766 | static int sony_nc_kbd_backlight_setup(struct platform_device *pd, |
1767 | unsigned int handle) | ||
1487 | { | 1768 | { |
1488 | int result; | 1769 | int result; |
1770 | int ret = 0; | ||
1489 | 1771 | ||
1490 | if (sony_call_snc_handle(KBDBL_HANDLER, KBDBL_PRESENT, &result)) | 1772 | /* verify the kbd backlight presence, these handles are not used for |
1491 | return 0; | 1773 | * keyboard backlight only |
1492 | if (!(result & 0x02)) | 1774 | */ |
1775 | ret = sony_call_snc_handle(handle, handle == 0x0137 ? 0x0B00 : 0x0100, | ||
1776 | &result); | ||
1777 | if (ret) | ||
1778 | return ret; | ||
1779 | |||
1780 | if ((handle == 0x0137 && !(result & 0x02)) || | ||
1781 | !(result & 0x01)) { | ||
1782 | dprintk("no backlight keyboard found\n"); | ||
1493 | return 0; | 1783 | return 0; |
1784 | } | ||
1494 | 1785 | ||
1495 | kbdbl_handle = kzalloc(sizeof(*kbdbl_handle), GFP_KERNEL); | 1786 | kbdbl_ctl = kzalloc(sizeof(*kbdbl_ctl), GFP_KERNEL); |
1496 | if (!kbdbl_handle) | 1787 | if (!kbdbl_ctl) |
1497 | return -ENOMEM; | 1788 | return -ENOMEM; |
1498 | 1789 | ||
1499 | sysfs_attr_init(&kbdbl_handle->mode_attr.attr); | 1790 | kbdbl_ctl->handle = handle; |
1500 | kbdbl_handle->mode_attr.attr.name = "kbd_backlight"; | 1791 | if (handle == 0x0137) |
1501 | kbdbl_handle->mode_attr.attr.mode = S_IRUGO | S_IWUSR; | 1792 | kbdbl_ctl->base = 0x0C00; |
1502 | kbdbl_handle->mode_attr.show = sony_nc_kbd_backlight_mode_show; | 1793 | else |
1503 | kbdbl_handle->mode_attr.store = sony_nc_kbd_backlight_mode_store; | 1794 | kbdbl_ctl->base = 0x4000; |
1795 | |||
1796 | sysfs_attr_init(&kbdbl_ctl->mode_attr.attr); | ||
1797 | kbdbl_ctl->mode_attr.attr.name = "kbd_backlight"; | ||
1798 | kbdbl_ctl->mode_attr.attr.mode = S_IRUGO | S_IWUSR; | ||
1799 | kbdbl_ctl->mode_attr.show = sony_nc_kbd_backlight_mode_show; | ||
1800 | kbdbl_ctl->mode_attr.store = sony_nc_kbd_backlight_mode_store; | ||
1504 | 1801 | ||
1505 | sysfs_attr_init(&kbdbl_handle->timeout_attr.attr); | 1802 | sysfs_attr_init(&kbdbl_ctl->timeout_attr.attr); |
1506 | kbdbl_handle->timeout_attr.attr.name = "kbd_backlight_timeout"; | 1803 | kbdbl_ctl->timeout_attr.attr.name = "kbd_backlight_timeout"; |
1507 | kbdbl_handle->timeout_attr.attr.mode = S_IRUGO | S_IWUSR; | 1804 | kbdbl_ctl->timeout_attr.attr.mode = S_IRUGO | S_IWUSR; |
1508 | kbdbl_handle->timeout_attr.show = sony_nc_kbd_backlight_timeout_show; | 1805 | kbdbl_ctl->timeout_attr.show = sony_nc_kbd_backlight_timeout_show; |
1509 | kbdbl_handle->timeout_attr.store = sony_nc_kbd_backlight_timeout_store; | 1806 | kbdbl_ctl->timeout_attr.store = sony_nc_kbd_backlight_timeout_store; |
1510 | 1807 | ||
1511 | if (device_create_file(&pd->dev, &kbdbl_handle->mode_attr)) | 1808 | ret = device_create_file(&pd->dev, &kbdbl_ctl->mode_attr); |
1809 | if (ret) | ||
1512 | goto outkzalloc; | 1810 | goto outkzalloc; |
1513 | 1811 | ||
1514 | if (device_create_file(&pd->dev, &kbdbl_handle->timeout_attr)) | 1812 | ret = device_create_file(&pd->dev, &kbdbl_ctl->timeout_attr); |
1813 | if (ret) | ||
1515 | goto outmode; | 1814 | goto outmode; |
1516 | 1815 | ||
1517 | __sony_nc_kbd_backlight_mode_set(kbd_backlight); | 1816 | __sony_nc_kbd_backlight_mode_set(kbd_backlight); |
@@ -1520,57 +1819,661 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd) | |||
1520 | return 0; | 1819 | return 0; |
1521 | 1820 | ||
1522 | outmode: | 1821 | outmode: |
1523 | device_remove_file(&pd->dev, &kbdbl_handle->mode_attr); | 1822 | device_remove_file(&pd->dev, &kbdbl_ctl->mode_attr); |
1524 | outkzalloc: | 1823 | outkzalloc: |
1525 | kfree(kbdbl_handle); | 1824 | kfree(kbdbl_ctl); |
1526 | kbdbl_handle = NULL; | 1825 | kbdbl_ctl = NULL; |
1527 | return -1; | 1826 | return ret; |
1528 | } | 1827 | } |
1529 | 1828 | ||
1530 | static int sony_nc_kbd_backlight_cleanup(struct platform_device *pd) | 1829 | static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd) |
1531 | { | 1830 | { |
1532 | if (kbdbl_handle) { | 1831 | if (kbdbl_ctl) { |
1533 | int result; | 1832 | int result; |
1534 | 1833 | ||
1535 | device_remove_file(&pd->dev, &kbdbl_handle->mode_attr); | 1834 | device_remove_file(&pd->dev, &kbdbl_ctl->mode_attr); |
1536 | device_remove_file(&pd->dev, &kbdbl_handle->timeout_attr); | 1835 | device_remove_file(&pd->dev, &kbdbl_ctl->timeout_attr); |
1537 | 1836 | ||
1538 | /* restore the default hw behaviour */ | 1837 | /* restore the default hw behaviour */ |
1539 | sony_call_snc_handle(KBDBL_HANDLER, 0x1000 | SET_MODE, &result); | 1838 | sony_call_snc_handle(kbdbl_ctl->handle, |
1540 | sony_call_snc_handle(KBDBL_HANDLER, SET_TIMEOUT, &result); | 1839 | kbdbl_ctl->base | 0x10000, &result); |
1840 | sony_call_snc_handle(kbdbl_ctl->handle, | ||
1841 | kbdbl_ctl->base + 0x200, &result); | ||
1541 | 1842 | ||
1542 | kfree(kbdbl_handle); | 1843 | kfree(kbdbl_ctl); |
1844 | kbdbl_ctl = NULL; | ||
1543 | } | 1845 | } |
1544 | return 0; | ||
1545 | } | 1846 | } |
1546 | 1847 | ||
1547 | static void sony_nc_kbd_backlight_resume(void) | 1848 | static void sony_nc_kbd_backlight_resume(void) |
1548 | { | 1849 | { |
1549 | int ignore = 0; | 1850 | int ignore = 0; |
1550 | 1851 | ||
1551 | if (!kbdbl_handle) | 1852 | if (!kbdbl_ctl) |
1552 | return; | 1853 | return; |
1553 | 1854 | ||
1554 | if (kbdbl_handle->mode == 0) | 1855 | if (kbdbl_ctl->mode == 0) |
1555 | sony_call_snc_handle(KBDBL_HANDLER, SET_MODE, &ignore); | 1856 | sony_call_snc_handle(kbdbl_ctl->handle, kbdbl_ctl->base, |
1556 | |||
1557 | if (kbdbl_handle->timeout != 0) | ||
1558 | sony_call_snc_handle(KBDBL_HANDLER, | ||
1559 | (kbdbl_handle->timeout << 0x10) | SET_TIMEOUT, | ||
1560 | &ignore); | 1857 | &ignore); |
1858 | |||
1859 | if (kbdbl_ctl->timeout != 0) | ||
1860 | sony_call_snc_handle(kbdbl_ctl->handle, | ||
1861 | (kbdbl_ctl->base + 0x200) | | ||
1862 | (kbdbl_ctl->timeout << 0x10), &ignore); | ||
1863 | } | ||
1864 | |||
1865 | struct battery_care_control { | ||
1866 | struct device_attribute attrs[2]; | ||
1867 | unsigned int handle; | ||
1868 | }; | ||
1869 | static struct battery_care_control *bcare_ctl; | ||
1870 | |||
1871 | static ssize_t sony_nc_battery_care_limit_store(struct device *dev, | ||
1872 | struct device_attribute *attr, | ||
1873 | const char *buffer, size_t count) | ||
1874 | { | ||
1875 | unsigned int result, cmd; | ||
1876 | unsigned long value; | ||
1877 | |||
1878 | if (count > 31) | ||
1879 | return -EINVAL; | ||
1880 | |||
1881 | if (kstrtoul(buffer, 10, &value)) | ||
1882 | return -EINVAL; | ||
1883 | |||
1884 | /* limit values (2 bits): | ||
1885 | * 00 - none | ||
1886 | * 01 - 80% | ||
1887 | * 10 - 50% | ||
1888 | * 11 - 100% | ||
1889 | * | ||
1890 | * bit 0: 0 disable BCL, 1 enable BCL | ||
1891 | * bit 1: 1 tell to store the battery limit (see bits 6,7) too | ||
1892 | * bits 2,3: reserved | ||
1893 | * bits 4,5: store the limit into the EC | ||
1894 | * bits 6,7: store the limit into the battery | ||
1895 | */ | ||
1896 | |||
1897 | /* | ||
1898 | * handle 0x0115 should allow storing on battery too; | ||
1899 | * handle 0x0136 same as 0x0115 + health status; | ||
1900 | * handle 0x013f, same as 0x0136 but no storing on the battery | ||
1901 | * | ||
1902 | * Store only inside the EC for now, regardless the handle number | ||
1903 | */ | ||
1904 | if (value == 0) | ||
1905 | /* disable limits */ | ||
1906 | cmd = 0x0; | ||
1907 | |||
1908 | else if (value <= 50) | ||
1909 | cmd = 0x21; | ||
1910 | |||
1911 | else if (value <= 80) | ||
1912 | cmd = 0x11; | ||
1913 | |||
1914 | else if (value <= 100) | ||
1915 | cmd = 0x31; | ||
1916 | |||
1917 | else | ||
1918 | return -EINVAL; | ||
1919 | |||
1920 | if (sony_call_snc_handle(bcare_ctl->handle, (cmd << 0x10) | 0x0100, | ||
1921 | &result)) | ||
1922 | return -EIO; | ||
1923 | |||
1924 | return count; | ||
1925 | } | ||
1926 | |||
1927 | static ssize_t sony_nc_battery_care_limit_show(struct device *dev, | ||
1928 | struct device_attribute *attr, char *buffer) | ||
1929 | { | ||
1930 | unsigned int result, status; | ||
1931 | |||
1932 | if (sony_call_snc_handle(bcare_ctl->handle, 0x0000, &result)) | ||
1933 | return -EIO; | ||
1934 | |||
1935 | status = (result & 0x01) ? ((result & 0x30) >> 0x04) : 0; | ||
1936 | switch (status) { | ||
1937 | case 1: | ||
1938 | status = 80; | ||
1939 | break; | ||
1940 | case 2: | ||
1941 | status = 50; | ||
1942 | break; | ||
1943 | case 3: | ||
1944 | status = 100; | ||
1945 | break; | ||
1946 | default: | ||
1947 | status = 0; | ||
1948 | break; | ||
1949 | } | ||
1950 | |||
1951 | return snprintf(buffer, PAGE_SIZE, "%d\n", status); | ||
1952 | } | ||
1953 | |||
1954 | static ssize_t sony_nc_battery_care_health_show(struct device *dev, | ||
1955 | struct device_attribute *attr, char *buffer) | ||
1956 | { | ||
1957 | ssize_t count = 0; | ||
1958 | unsigned int health; | ||
1959 | |||
1960 | if (sony_call_snc_handle(bcare_ctl->handle, 0x0200, &health)) | ||
1961 | return -EIO; | ||
1962 | |||
1963 | count = snprintf(buffer, PAGE_SIZE, "%d\n", health & 0xff); | ||
1964 | |||
1965 | return count; | ||
1966 | } | ||
1967 | |||
1968 | static int sony_nc_battery_care_setup(struct platform_device *pd, | ||
1969 | unsigned int handle) | ||
1970 | { | ||
1971 | int ret = 0; | ||
1972 | |||
1973 | bcare_ctl = kzalloc(sizeof(struct battery_care_control), GFP_KERNEL); | ||
1974 | if (!bcare_ctl) | ||
1975 | return -ENOMEM; | ||
1976 | |||
1977 | bcare_ctl->handle = handle; | ||
1978 | |||
1979 | sysfs_attr_init(&bcare_ctl->attrs[0].attr); | ||
1980 | bcare_ctl->attrs[0].attr.name = "battery_care_limiter"; | ||
1981 | bcare_ctl->attrs[0].attr.mode = S_IRUGO | S_IWUSR; | ||
1982 | bcare_ctl->attrs[0].show = sony_nc_battery_care_limit_show; | ||
1983 | bcare_ctl->attrs[0].store = sony_nc_battery_care_limit_store; | ||
1984 | |||
1985 | ret = device_create_file(&pd->dev, &bcare_ctl->attrs[0]); | ||
1986 | if (ret) | ||
1987 | goto outkzalloc; | ||
1988 | |||
1989 | /* 0x0115 is for models with no health reporting capability */ | ||
1990 | if (handle == 0x0115) | ||
1991 | return 0; | ||
1992 | |||
1993 | sysfs_attr_init(&bcare_ctl->attrs[1].attr); | ||
1994 | bcare_ctl->attrs[1].attr.name = "battery_care_health"; | ||
1995 | bcare_ctl->attrs[1].attr.mode = S_IRUGO; | ||
1996 | bcare_ctl->attrs[1].show = sony_nc_battery_care_health_show; | ||
1997 | |||
1998 | ret = device_create_file(&pd->dev, &bcare_ctl->attrs[1]); | ||
1999 | if (ret) | ||
2000 | goto outlimiter; | ||
2001 | |||
2002 | return 0; | ||
2003 | |||
2004 | outlimiter: | ||
2005 | device_remove_file(&pd->dev, &bcare_ctl->attrs[0]); | ||
2006 | |||
2007 | outkzalloc: | ||
2008 | kfree(bcare_ctl); | ||
2009 | bcare_ctl = NULL; | ||
2010 | |||
2011 | return ret; | ||
2012 | } | ||
2013 | |||
2014 | static void sony_nc_battery_care_cleanup(struct platform_device *pd) | ||
2015 | { | ||
2016 | if (bcare_ctl) { | ||
2017 | device_remove_file(&pd->dev, &bcare_ctl->attrs[0]); | ||
2018 | if (bcare_ctl->handle != 0x0115) | ||
2019 | device_remove_file(&pd->dev, &bcare_ctl->attrs[1]); | ||
2020 | |||
2021 | kfree(bcare_ctl); | ||
2022 | bcare_ctl = NULL; | ||
2023 | } | ||
2024 | } | ||
2025 | |||
2026 | struct snc_thermal_ctrl { | ||
2027 | unsigned int mode; | ||
2028 | unsigned int profiles; | ||
2029 | struct device_attribute mode_attr; | ||
2030 | struct device_attribute profiles_attr; | ||
2031 | }; | ||
2032 | static struct snc_thermal_ctrl *th_handle; | ||
2033 | |||
2034 | #define THM_PROFILE_MAX 3 | ||
2035 | static const char * const snc_thermal_profiles[] = { | ||
2036 | "balanced", | ||
2037 | "silent", | ||
2038 | "performance" | ||
2039 | }; | ||
2040 | |||
2041 | static int sony_nc_thermal_mode_set(unsigned short mode) | ||
2042 | { | ||
2043 | unsigned int result; | ||
2044 | |||
2045 | /* the thermal profile seems to be a two bit bitmask: | ||
2046 | * lsb -> silent | ||
2047 | * msb -> performance | ||
2048 | * no bit set is the normal operation and is always valid | ||
2049 | * Some vaio models only have "balanced" and "performance" | ||
2050 | */ | ||
2051 | if ((mode && !(th_handle->profiles & mode)) || mode >= THM_PROFILE_MAX) | ||
2052 | return -EINVAL; | ||
2053 | |||
2054 | if (sony_call_snc_handle(0x0122, mode << 0x10 | 0x0200, &result)) | ||
2055 | return -EIO; | ||
2056 | |||
2057 | th_handle->mode = mode; | ||
2058 | |||
2059 | return 0; | ||
2060 | } | ||
2061 | |||
2062 | static int sony_nc_thermal_mode_get(void) | ||
2063 | { | ||
2064 | unsigned int result; | ||
2065 | |||
2066 | if (sony_call_snc_handle(0x0122, 0x0100, &result)) | ||
2067 | return -EIO; | ||
2068 | |||
2069 | return result & 0xff; | ||
2070 | } | ||
2071 | |||
2072 | static ssize_t sony_nc_thermal_profiles_show(struct device *dev, | ||
2073 | struct device_attribute *attr, char *buffer) | ||
2074 | { | ||
2075 | short cnt; | ||
2076 | size_t idx = 0; | ||
2077 | |||
2078 | for (cnt = 0; cnt < THM_PROFILE_MAX; cnt++) { | ||
2079 | if (!cnt || (th_handle->profiles & cnt)) | ||
2080 | idx += snprintf(buffer + idx, PAGE_SIZE - idx, "%s ", | ||
2081 | snc_thermal_profiles[cnt]); | ||
2082 | } | ||
2083 | idx += snprintf(buffer + idx, PAGE_SIZE - idx, "\n"); | ||
2084 | |||
2085 | return idx; | ||
2086 | } | ||
2087 | |||
2088 | static ssize_t sony_nc_thermal_mode_store(struct device *dev, | ||
2089 | struct device_attribute *attr, | ||
2090 | const char *buffer, size_t count) | ||
2091 | { | ||
2092 | unsigned short cmd; | ||
2093 | size_t len = count; | ||
2094 | |||
2095 | if (count == 0) | ||
2096 | return -EINVAL; | ||
2097 | |||
2098 | /* skip the newline if present */ | ||
2099 | if (buffer[len - 1] == '\n') | ||
2100 | len--; | ||
2101 | |||
2102 | for (cmd = 0; cmd < THM_PROFILE_MAX; cmd++) | ||
2103 | if (strncmp(buffer, snc_thermal_profiles[cmd], len) == 0) | ||
2104 | break; | ||
2105 | |||
2106 | if (sony_nc_thermal_mode_set(cmd)) | ||
2107 | return -EIO; | ||
2108 | |||
2109 | return count; | ||
2110 | } | ||
2111 | |||
2112 | static ssize_t sony_nc_thermal_mode_show(struct device *dev, | ||
2113 | struct device_attribute *attr, char *buffer) | ||
2114 | { | ||
2115 | ssize_t count = 0; | ||
2116 | unsigned int mode = sony_nc_thermal_mode_get(); | ||
2117 | |||
2118 | if (mode < 0) | ||
2119 | return mode; | ||
2120 | |||
2121 | count = snprintf(buffer, PAGE_SIZE, "%s\n", snc_thermal_profiles[mode]); | ||
2122 | |||
2123 | return count; | ||
2124 | } | ||
2125 | |||
2126 | static int sony_nc_thermal_setup(struct platform_device *pd) | ||
2127 | { | ||
2128 | int ret = 0; | ||
2129 | th_handle = kzalloc(sizeof(struct snc_thermal_ctrl), GFP_KERNEL); | ||
2130 | if (!th_handle) | ||
2131 | return -ENOMEM; | ||
2132 | |||
2133 | ret = sony_call_snc_handle(0x0122, 0x0000, &th_handle->profiles); | ||
2134 | if (ret) { | ||
2135 | pr_warn("couldn't to read the thermal profiles\n"); | ||
2136 | goto outkzalloc; | ||
2137 | } | ||
2138 | |||
2139 | ret = sony_nc_thermal_mode_get(); | ||
2140 | if (ret < 0) { | ||
2141 | pr_warn("couldn't to read the current thermal profile"); | ||
2142 | goto outkzalloc; | ||
2143 | } | ||
2144 | th_handle->mode = ret; | ||
2145 | |||
2146 | sysfs_attr_init(&th_handle->profiles_attr.attr); | ||
2147 | th_handle->profiles_attr.attr.name = "thermal_profiles"; | ||
2148 | th_handle->profiles_attr.attr.mode = S_IRUGO; | ||
2149 | th_handle->profiles_attr.show = sony_nc_thermal_profiles_show; | ||
2150 | |||
2151 | sysfs_attr_init(&th_handle->mode_attr.attr); | ||
2152 | th_handle->mode_attr.attr.name = "thermal_control"; | ||
2153 | th_handle->mode_attr.attr.mode = S_IRUGO | S_IWUSR; | ||
2154 | th_handle->mode_attr.show = sony_nc_thermal_mode_show; | ||
2155 | th_handle->mode_attr.store = sony_nc_thermal_mode_store; | ||
2156 | |||
2157 | ret = device_create_file(&pd->dev, &th_handle->profiles_attr); | ||
2158 | if (ret) | ||
2159 | goto outkzalloc; | ||
2160 | |||
2161 | ret = device_create_file(&pd->dev, &th_handle->mode_attr); | ||
2162 | if (ret) | ||
2163 | goto outprofiles; | ||
2164 | |||
2165 | return 0; | ||
2166 | |||
2167 | outprofiles: | ||
2168 | device_remove_file(&pd->dev, &th_handle->profiles_attr); | ||
2169 | outkzalloc: | ||
2170 | kfree(th_handle); | ||
2171 | th_handle = NULL; | ||
2172 | return ret; | ||
2173 | } | ||
2174 | |||
2175 | static void sony_nc_thermal_cleanup(struct platform_device *pd) | ||
2176 | { | ||
2177 | if (th_handle) { | ||
2178 | device_remove_file(&pd->dev, &th_handle->profiles_attr); | ||
2179 | device_remove_file(&pd->dev, &th_handle->mode_attr); | ||
2180 | kfree(th_handle); | ||
2181 | th_handle = NULL; | ||
2182 | } | ||
2183 | } | ||
2184 | |||
2185 | static void sony_nc_thermal_resume(void) | ||
2186 | { | ||
2187 | unsigned int status = sony_nc_thermal_mode_get(); | ||
2188 | |||
2189 | if (status != th_handle->mode) | ||
2190 | sony_nc_thermal_mode_set(th_handle->mode); | ||
2191 | } | ||
2192 | |||
2193 | /* resume on LID open */ | ||
2194 | struct snc_lid_resume_control { | ||
2195 | struct device_attribute attrs[3]; | ||
2196 | unsigned int status; | ||
2197 | }; | ||
2198 | static struct snc_lid_resume_control *lid_ctl; | ||
2199 | |||
2200 | static ssize_t sony_nc_lid_resume_store(struct device *dev, | ||
2201 | struct device_attribute *attr, | ||
2202 | const char *buffer, size_t count) | ||
2203 | { | ||
2204 | unsigned int result, pos; | ||
2205 | unsigned long value; | ||
2206 | if (count > 31) | ||
2207 | return -EINVAL; | ||
2208 | |||
2209 | if (kstrtoul(buffer, 10, &value) || value > 1) | ||
2210 | return -EINVAL; | ||
2211 | |||
2212 | /* the value we have to write to SNC is a bitmask: | ||
2213 | * +--------------+ | ||
2214 | * | S3 | S4 | S5 | | ||
2215 | * +--------------+ | ||
2216 | * 2 1 0 | ||
2217 | */ | ||
2218 | if (strcmp(attr->attr.name, "lid_resume_S3") == 0) | ||
2219 | pos = 2; | ||
2220 | else if (strcmp(attr->attr.name, "lid_resume_S4") == 0) | ||
2221 | pos = 1; | ||
2222 | else if (strcmp(attr->attr.name, "lid_resume_S5") == 0) | ||
2223 | pos = 0; | ||
2224 | else | ||
2225 | return -EINVAL; | ||
2226 | |||
2227 | if (value) | ||
2228 | value = lid_ctl->status | (1 << pos); | ||
2229 | else | ||
2230 | value = lid_ctl->status & ~(1 << pos); | ||
2231 | |||
2232 | if (sony_call_snc_handle(0x0119, value << 0x10 | 0x0100, &result)) | ||
2233 | return -EIO; | ||
2234 | |||
2235 | lid_ctl->status = value; | ||
2236 | |||
2237 | return count; | ||
2238 | } | ||
2239 | |||
2240 | static ssize_t sony_nc_lid_resume_show(struct device *dev, | ||
2241 | struct device_attribute *attr, char *buffer) | ||
2242 | { | ||
2243 | unsigned int pos; | ||
2244 | |||
2245 | if (strcmp(attr->attr.name, "lid_resume_S3") == 0) | ||
2246 | pos = 2; | ||
2247 | else if (strcmp(attr->attr.name, "lid_resume_S4") == 0) | ||
2248 | pos = 1; | ||
2249 | else if (strcmp(attr->attr.name, "lid_resume_S5") == 0) | ||
2250 | pos = 0; | ||
2251 | else | ||
2252 | return -EINVAL; | ||
2253 | |||
2254 | return snprintf(buffer, PAGE_SIZE, "%d\n", | ||
2255 | (lid_ctl->status >> pos) & 0x01); | ||
2256 | } | ||
2257 | |||
2258 | static int sony_nc_lid_resume_setup(struct platform_device *pd) | ||
2259 | { | ||
2260 | unsigned int result; | ||
2261 | int i; | ||
2262 | |||
2263 | if (sony_call_snc_handle(0x0119, 0x0000, &result)) | ||
2264 | return -EIO; | ||
2265 | |||
2266 | lid_ctl = kzalloc(sizeof(struct snc_lid_resume_control), GFP_KERNEL); | ||
2267 | if (!lid_ctl) | ||
2268 | return -ENOMEM; | ||
2269 | |||
2270 | lid_ctl->status = result & 0x7; | ||
2271 | |||
2272 | sysfs_attr_init(&lid_ctl->attrs[0].attr); | ||
2273 | lid_ctl->attrs[0].attr.name = "lid_resume_S3"; | ||
2274 | lid_ctl->attrs[0].attr.mode = S_IRUGO | S_IWUSR; | ||
2275 | lid_ctl->attrs[0].show = sony_nc_lid_resume_show; | ||
2276 | lid_ctl->attrs[0].store = sony_nc_lid_resume_store; | ||
2277 | |||
2278 | sysfs_attr_init(&lid_ctl->attrs[1].attr); | ||
2279 | lid_ctl->attrs[1].attr.name = "lid_resume_S4"; | ||
2280 | lid_ctl->attrs[1].attr.mode = S_IRUGO | S_IWUSR; | ||
2281 | lid_ctl->attrs[1].show = sony_nc_lid_resume_show; | ||
2282 | lid_ctl->attrs[1].store = sony_nc_lid_resume_store; | ||
2283 | |||
2284 | sysfs_attr_init(&lid_ctl->attrs[2].attr); | ||
2285 | lid_ctl->attrs[2].attr.name = "lid_resume_S5"; | ||
2286 | lid_ctl->attrs[2].attr.mode = S_IRUGO | S_IWUSR; | ||
2287 | lid_ctl->attrs[2].show = sony_nc_lid_resume_show; | ||
2288 | lid_ctl->attrs[2].store = sony_nc_lid_resume_store; | ||
2289 | |||
2290 | for (i = 0; i < 3; i++) { | ||
2291 | result = device_create_file(&pd->dev, &lid_ctl->attrs[i]); | ||
2292 | if (result) | ||
2293 | goto liderror; | ||
2294 | } | ||
2295 | |||
2296 | return 0; | ||
2297 | |||
2298 | liderror: | ||
2299 | for (; i > 0; i--) | ||
2300 | device_remove_file(&pd->dev, &lid_ctl->attrs[i]); | ||
2301 | |||
2302 | kfree(lid_ctl); | ||
2303 | lid_ctl = NULL; | ||
2304 | |||
2305 | return result; | ||
2306 | } | ||
2307 | |||
2308 | static void sony_nc_lid_resume_cleanup(struct platform_device *pd) | ||
2309 | { | ||
2310 | int i; | ||
2311 | |||
2312 | if (lid_ctl) { | ||
2313 | for (i = 0; i < 3; i++) | ||
2314 | device_remove_file(&pd->dev, &lid_ctl->attrs[i]); | ||
2315 | |||
2316 | kfree(lid_ctl); | ||
2317 | lid_ctl = NULL; | ||
2318 | } | ||
2319 | } | ||
2320 | |||
2321 | /* High speed charging function */ | ||
2322 | static struct device_attribute *hsc_handle; | ||
2323 | |||
2324 | static ssize_t sony_nc_highspeed_charging_store(struct device *dev, | ||
2325 | struct device_attribute *attr, | ||
2326 | const char *buffer, size_t count) | ||
2327 | { | ||
2328 | unsigned int result; | ||
2329 | unsigned long value; | ||
2330 | |||
2331 | if (count > 31) | ||
2332 | return -EINVAL; | ||
2333 | |||
2334 | if (kstrtoul(buffer, 10, &value) || value > 1) | ||
2335 | return -EINVAL; | ||
2336 | |||
2337 | if (sony_call_snc_handle(0x0131, value << 0x10 | 0x0200, &result)) | ||
2338 | return -EIO; | ||
2339 | |||
2340 | return count; | ||
2341 | } | ||
2342 | |||
2343 | static ssize_t sony_nc_highspeed_charging_show(struct device *dev, | ||
2344 | struct device_attribute *attr, char *buffer) | ||
2345 | { | ||
2346 | unsigned int result; | ||
2347 | |||
2348 | if (sony_call_snc_handle(0x0131, 0x0100, &result)) | ||
2349 | return -EIO; | ||
2350 | |||
2351 | return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x01); | ||
2352 | } | ||
2353 | |||
2354 | static int sony_nc_highspeed_charging_setup(struct platform_device *pd) | ||
2355 | { | ||
2356 | unsigned int result; | ||
2357 | |||
2358 | if (sony_call_snc_handle(0x0131, 0x0000, &result) || !(result & 0x01)) { | ||
2359 | /* some models advertise the handle but have no implementation | ||
2360 | * for it | ||
2361 | */ | ||
2362 | pr_info("No High Speed Charging capability found\n"); | ||
2363 | return 0; | ||
2364 | } | ||
2365 | |||
2366 | hsc_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); | ||
2367 | if (!hsc_handle) | ||
2368 | return -ENOMEM; | ||
2369 | |||
2370 | sysfs_attr_init(&hsc_handle->attr); | ||
2371 | hsc_handle->attr.name = "battery_highspeed_charging"; | ||
2372 | hsc_handle->attr.mode = S_IRUGO | S_IWUSR; | ||
2373 | hsc_handle->show = sony_nc_highspeed_charging_show; | ||
2374 | hsc_handle->store = sony_nc_highspeed_charging_store; | ||
2375 | |||
2376 | result = device_create_file(&pd->dev, hsc_handle); | ||
2377 | if (result) { | ||
2378 | kfree(hsc_handle); | ||
2379 | hsc_handle = NULL; | ||
2380 | return result; | ||
2381 | } | ||
2382 | |||
2383 | return 0; | ||
2384 | } | ||
2385 | |||
2386 | static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd) | ||
2387 | { | ||
2388 | if (hsc_handle) { | ||
2389 | device_remove_file(&pd->dev, hsc_handle); | ||
2390 | kfree(hsc_handle); | ||
2391 | hsc_handle = NULL; | ||
2392 | } | ||
2393 | } | ||
2394 | |||
2395 | /* Touchpad enable/disable */ | ||
2396 | struct touchpad_control { | ||
2397 | struct device_attribute attr; | ||
2398 | int handle; | ||
2399 | }; | ||
2400 | static struct touchpad_control *tp_ctl; | ||
2401 | |||
2402 | static ssize_t sony_nc_touchpad_store(struct device *dev, | ||
2403 | struct device_attribute *attr, const char *buffer, size_t count) | ||
2404 | { | ||
2405 | unsigned int result; | ||
2406 | unsigned long value; | ||
2407 | |||
2408 | if (count > 31) | ||
2409 | return -EINVAL; | ||
2410 | |||
2411 | if (kstrtoul(buffer, 10, &value) || value > 1) | ||
2412 | return -EINVAL; | ||
2413 | |||
2414 | /* sysfs: 0 disabled, 1 enabled | ||
2415 | * EC: 0 enabled, 1 disabled | ||
2416 | */ | ||
2417 | if (sony_call_snc_handle(tp_ctl->handle, | ||
2418 | (!value << 0x10) | 0x100, &result)) | ||
2419 | return -EIO; | ||
2420 | |||
2421 | return count; | ||
2422 | } | ||
2423 | |||
2424 | static ssize_t sony_nc_touchpad_show(struct device *dev, | ||
2425 | struct device_attribute *attr, char *buffer) | ||
2426 | { | ||
2427 | unsigned int result; | ||
2428 | |||
2429 | if (sony_call_snc_handle(tp_ctl->handle, 0x000, &result)) | ||
2430 | return -EINVAL; | ||
2431 | |||
2432 | return snprintf(buffer, PAGE_SIZE, "%d\n", !(result & 0x01)); | ||
2433 | } | ||
2434 | |||
2435 | static int sony_nc_touchpad_setup(struct platform_device *pd, | ||
2436 | unsigned int handle) | ||
2437 | { | ||
2438 | int ret = 0; | ||
2439 | |||
2440 | tp_ctl = kzalloc(sizeof(struct touchpad_control), GFP_KERNEL); | ||
2441 | if (!tp_ctl) | ||
2442 | return -ENOMEM; | ||
2443 | |||
2444 | tp_ctl->handle = handle; | ||
2445 | |||
2446 | sysfs_attr_init(&tp_ctl->attr.attr); | ||
2447 | tp_ctl->attr.attr.name = "touchpad"; | ||
2448 | tp_ctl->attr.attr.mode = S_IRUGO | S_IWUSR; | ||
2449 | tp_ctl->attr.show = sony_nc_touchpad_show; | ||
2450 | tp_ctl->attr.store = sony_nc_touchpad_store; | ||
2451 | |||
2452 | ret = device_create_file(&pd->dev, &tp_ctl->attr); | ||
2453 | if (ret) { | ||
2454 | kfree(tp_ctl); | ||
2455 | tp_ctl = NULL; | ||
2456 | } | ||
2457 | |||
2458 | return ret; | ||
2459 | } | ||
2460 | |||
2461 | static void sony_nc_touchpad_cleanup(struct platform_device *pd) | ||
2462 | { | ||
2463 | if (tp_ctl) { | ||
2464 | device_remove_file(&pd->dev, &tp_ctl->attr); | ||
2465 | kfree(tp_ctl); | ||
2466 | tp_ctl = NULL; | ||
2467 | } | ||
1561 | } | 2468 | } |
1562 | 2469 | ||
1563 | static void sony_nc_backlight_ng_read_limits(int handle, | 2470 | static void sony_nc_backlight_ng_read_limits(int handle, |
1564 | struct sony_backlight_props *props) | 2471 | struct sony_backlight_props *props) |
1565 | { | 2472 | { |
1566 | int offset; | 2473 | u64 offset; |
1567 | acpi_status status; | 2474 | int i; |
1568 | u8 brlvl, i; | ||
1569 | u8 min = 0xff, max = 0x00; | 2475 | u8 min = 0xff, max = 0x00; |
1570 | struct acpi_object_list params; | 2476 | unsigned char buffer[32] = { 0 }; |
1571 | union acpi_object in_obj; | ||
1572 | union acpi_object *lvl_enum; | ||
1573 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
1574 | 2477 | ||
1575 | props->handle = handle; | 2478 | props->handle = handle; |
1576 | props->offset = 0; | 2479 | props->offset = 0; |
@@ -1583,50 +2486,31 @@ static void sony_nc_backlight_ng_read_limits(int handle, | |||
1583 | /* try to read the boundaries from ACPI tables, if we fail the above | 2486 | /* try to read the boundaries from ACPI tables, if we fail the above |
1584 | * defaults should be reasonable | 2487 | * defaults should be reasonable |
1585 | */ | 2488 | */ |
1586 | params.count = 1; | 2489 | i = sony_nc_buffer_call(sony_nc_acpi_handle, "SN06", &offset, buffer, |
1587 | params.pointer = &in_obj; | 2490 | 32); |
1588 | in_obj.type = ACPI_TYPE_INTEGER; | 2491 | if (i < 0) |
1589 | in_obj.integer.value = offset; | ||
1590 | status = acpi_evaluate_object(sony_nc_acpi_handle, "SN06", ¶ms, | ||
1591 | &buffer); | ||
1592 | if (ACPI_FAILURE(status)) | ||
1593 | return; | 2492 | return; |
1594 | 2493 | ||
1595 | lvl_enum = (union acpi_object *) buffer.pointer; | ||
1596 | if (!lvl_enum) { | ||
1597 | pr_err("No SN06 return object."); | ||
1598 | return; | ||
1599 | } | ||
1600 | if (lvl_enum->type != ACPI_TYPE_BUFFER) { | ||
1601 | pr_err("Invalid SN06 return object 0x%.2x\n", | ||
1602 | lvl_enum->type); | ||
1603 | goto out_invalid; | ||
1604 | } | ||
1605 | |||
1606 | /* the buffer lists brightness levels available, brightness levels are | 2494 | /* the buffer lists brightness levels available, brightness levels are |
1607 | * from 0 to 8 in the array, other values are used by ALS control. | 2495 | * from position 0 to 8 in the array, other values are used by ALS |
2496 | * control. | ||
1608 | */ | 2497 | */ |
1609 | for (i = 0; i < 9 && i < lvl_enum->buffer.length; i++) { | 2498 | for (i = 0; i < 9 && i < ARRAY_SIZE(buffer); i++) { |
1610 | 2499 | ||
1611 | brlvl = *(lvl_enum->buffer.pointer + i); | 2500 | dprintk("Brightness level: %d\n", buffer[i]); |
1612 | dprintk("Brightness level: %d\n", brlvl); | ||
1613 | 2501 | ||
1614 | if (!brlvl) | 2502 | if (!buffer[i]) |
1615 | break; | 2503 | break; |
1616 | 2504 | ||
1617 | if (brlvl > max) | 2505 | if (buffer[i] > max) |
1618 | max = brlvl; | 2506 | max = buffer[i]; |
1619 | if (brlvl < min) | 2507 | if (buffer[i] < min) |
1620 | min = brlvl; | 2508 | min = buffer[i]; |
1621 | } | 2509 | } |
1622 | props->offset = min; | 2510 | props->offset = min; |
1623 | props->maxlvl = max; | 2511 | props->maxlvl = max; |
1624 | dprintk("Brightness levels: min=%d max=%d\n", props->offset, | 2512 | dprintk("Brightness levels: min=%d max=%d\n", props->offset, |
1625 | props->maxlvl); | 2513 | props->maxlvl); |
1626 | |||
1627 | out_invalid: | ||
1628 | kfree(buffer.pointer); | ||
1629 | return; | ||
1630 | } | 2514 | } |
1631 | 2515 | ||
1632 | static void sony_nc_backlight_setup(void) | 2516 | static void sony_nc_backlight_setup(void) |
@@ -1715,28 +2599,25 @@ static int sony_nc_add(struct acpi_device *device) | |||
1715 | 2599 | ||
1716 | if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON", | 2600 | if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON", |
1717 | &handle))) { | 2601 | &handle))) { |
1718 | if (acpi_callsetfunc(sony_nc_acpi_handle, "ECON", 1, NULL)) | 2602 | int arg = 1; |
2603 | if (sony_nc_int_call(sony_nc_acpi_handle, "ECON", &arg, NULL)) | ||
1719 | dprintk("ECON Method failed\n"); | 2604 | dprintk("ECON Method failed\n"); |
1720 | } | 2605 | } |
1721 | 2606 | ||
1722 | if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00", | 2607 | if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00", |
1723 | &handle))) { | 2608 | &handle))) { |
1724 | dprintk("Doing SNC setup\n"); | 2609 | dprintk("Doing SNC setup\n"); |
2610 | /* retrieve the available handles */ | ||
1725 | result = sony_nc_handles_setup(sony_pf_device); | 2611 | result = sony_nc_handles_setup(sony_pf_device); |
1726 | if (result) | 2612 | if (!result) |
1727 | goto outpresent; | 2613 | sony_nc_function_setup(device, sony_pf_device); |
1728 | result = sony_nc_kbd_backlight_setup(sony_pf_device); | ||
1729 | if (result) | ||
1730 | goto outsnc; | ||
1731 | sony_nc_function_setup(device); | ||
1732 | sony_nc_rfkill_setup(device); | ||
1733 | } | 2614 | } |
1734 | 2615 | ||
1735 | /* setup input devices and helper fifo */ | 2616 | /* setup input devices and helper fifo */ |
1736 | result = sony_laptop_setup_input(device); | 2617 | result = sony_laptop_setup_input(device); |
1737 | if (result) { | 2618 | if (result) { |
1738 | pr_err("Unable to create input devices\n"); | 2619 | pr_err("Unable to create input devices\n"); |
1739 | goto outkbdbacklight; | 2620 | goto outsnc; |
1740 | } | 2621 | } |
1741 | 2622 | ||
1742 | if (acpi_video_backlight_support()) { | 2623 | if (acpi_video_backlight_support()) { |
@@ -1794,10 +2675,8 @@ static int sony_nc_add(struct acpi_device *device) | |||
1794 | 2675 | ||
1795 | sony_laptop_remove_input(); | 2676 | sony_laptop_remove_input(); |
1796 | 2677 | ||
1797 | outkbdbacklight: | ||
1798 | sony_nc_kbd_backlight_cleanup(sony_pf_device); | ||
1799 | |||
1800 | outsnc: | 2678 | outsnc: |
2679 | sony_nc_function_cleanup(sony_pf_device); | ||
1801 | sony_nc_handles_cleanup(sony_pf_device); | 2680 | sony_nc_handles_cleanup(sony_pf_device); |
1802 | 2681 | ||
1803 | outpresent: | 2682 | outpresent: |
@@ -1820,11 +2699,10 @@ static int sony_nc_remove(struct acpi_device *device, int type) | |||
1820 | device_remove_file(&sony_pf_device->dev, &item->devattr); | 2699 | device_remove_file(&sony_pf_device->dev, &item->devattr); |
1821 | } | 2700 | } |
1822 | 2701 | ||
1823 | sony_nc_kbd_backlight_cleanup(sony_pf_device); | 2702 | sony_nc_function_cleanup(sony_pf_device); |
1824 | sony_nc_handles_cleanup(sony_pf_device); | 2703 | sony_nc_handles_cleanup(sony_pf_device); |
1825 | sony_pf_remove(); | 2704 | sony_pf_remove(); |
1826 | sony_laptop_remove_input(); | 2705 | sony_laptop_remove_input(); |
1827 | sony_nc_rfkill_cleanup(); | ||
1828 | dprintk(SONY_NC_DRIVER_NAME " removed.\n"); | 2706 | dprintk(SONY_NC_DRIVER_NAME " removed.\n"); |
1829 | 2707 | ||
1830 | return 0; | 2708 | return 0; |
@@ -2437,7 +3315,9 @@ static ssize_t sony_pic_wwanpower_store(struct device *dev, | |||
2437 | if (count > 31) | 3315 | if (count > 31) |
2438 | return -EINVAL; | 3316 | return -EINVAL; |
2439 | 3317 | ||
2440 | value = simple_strtoul(buffer, NULL, 10); | 3318 | if (kstrtoul(buffer, 10, &value)) |
3319 | return -EINVAL; | ||
3320 | |||
2441 | mutex_lock(&spic_dev.lock); | 3321 | mutex_lock(&spic_dev.lock); |
2442 | __sony_pic_set_wwanpower(value); | 3322 | __sony_pic_set_wwanpower(value); |
2443 | mutex_unlock(&spic_dev.lock); | 3323 | mutex_unlock(&spic_dev.lock); |
@@ -2474,7 +3354,9 @@ static ssize_t sony_pic_bluetoothpower_store(struct device *dev, | |||
2474 | if (count > 31) | 3354 | if (count > 31) |
2475 | return -EINVAL; | 3355 | return -EINVAL; |
2476 | 3356 | ||
2477 | value = simple_strtoul(buffer, NULL, 10); | 3357 | if (kstrtoul(buffer, 10, &value)) |
3358 | return -EINVAL; | ||
3359 | |||
2478 | mutex_lock(&spic_dev.lock); | 3360 | mutex_lock(&spic_dev.lock); |
2479 | __sony_pic_set_bluetoothpower(value); | 3361 | __sony_pic_set_bluetoothpower(value); |
2480 | mutex_unlock(&spic_dev.lock); | 3362 | mutex_unlock(&spic_dev.lock); |
@@ -2513,7 +3395,9 @@ static ssize_t sony_pic_fanspeed_store(struct device *dev, | |||
2513 | if (count > 31) | 3395 | if (count > 31) |
2514 | return -EINVAL; | 3396 | return -EINVAL; |
2515 | 3397 | ||
2516 | value = simple_strtoul(buffer, NULL, 10); | 3398 | if (kstrtoul(buffer, 10, &value)) |
3399 | return -EINVAL; | ||
3400 | |||
2517 | if (sony_pic_set_fanspeed(value)) | 3401 | if (sony_pic_set_fanspeed(value)) |
2518 | return -EIO; | 3402 | return -EIO; |
2519 | 3403 | ||
@@ -2671,7 +3555,8 @@ static long sonypi_misc_ioctl(struct file *fp, unsigned int cmd, | |||
2671 | ret = -EIO; | 3555 | ret = -EIO; |
2672 | break; | 3556 | break; |
2673 | } | 3557 | } |
2674 | if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value)) { | 3558 | if (sony_nc_int_call(sony_nc_acpi_handle, "GBRT", NULL, |
3559 | &value)) { | ||
2675 | ret = -EIO; | 3560 | ret = -EIO; |
2676 | break; | 3561 | break; |
2677 | } | 3562 | } |
@@ -2688,8 +3573,9 @@ static long sonypi_misc_ioctl(struct file *fp, unsigned int cmd, | |||
2688 | ret = -EFAULT; | 3573 | ret = -EFAULT; |
2689 | break; | 3574 | break; |
2690 | } | 3575 | } |
2691 | if (acpi_callsetfunc(sony_nc_acpi_handle, "SBRT", | 3576 | value = (val8 >> 5) + 1; |
2692 | (val8 >> 5) + 1, NULL)) { | 3577 | if (sony_nc_int_call(sony_nc_acpi_handle, "SBRT", &value, |
3578 | NULL)) { | ||
2693 | ret = -EIO; | 3579 | ret = -EIO; |
2694 | break; | 3580 | break; |
2695 | } | 3581 | } |
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index d68c0002f4a2..8b5610d88418 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c | |||
@@ -3402,7 +3402,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3402 | /* Do not issue duplicate brightness change events to | 3402 | /* Do not issue duplicate brightness change events to |
3403 | * userspace. tpacpi_detect_brightness_capabilities() must have | 3403 | * userspace. tpacpi_detect_brightness_capabilities() must have |
3404 | * been called before this point */ | 3404 | * been called before this point */ |
3405 | if (tp_features.bright_acpimode && acpi_video_backlight_support()) { | 3405 | if (acpi_video_backlight_support()) { |
3406 | pr_info("This ThinkPad has standard ACPI backlight " | 3406 | pr_info("This ThinkPad has standard ACPI backlight " |
3407 | "brightness control, supported by the ACPI " | 3407 | "brightness control, supported by the ACPI " |
3408 | "video driver\n"); | 3408 | "video driver\n"); |
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 57787d87d9a4..dab10f6edcd4 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c | |||
@@ -95,6 +95,7 @@ MODULE_LICENSE("GPL"); | |||
95 | 95 | ||
96 | /* registers */ | 96 | /* registers */ |
97 | #define HCI_FAN 0x0004 | 97 | #define HCI_FAN 0x0004 |
98 | #define HCI_TR_BACKLIGHT 0x0005 | ||
98 | #define HCI_SYSTEM_EVENT 0x0016 | 99 | #define HCI_SYSTEM_EVENT 0x0016 |
99 | #define HCI_VIDEO_OUT 0x001c | 100 | #define HCI_VIDEO_OUT 0x001c |
100 | #define HCI_HOTKEY_EVENT 0x001e | 101 | #define HCI_HOTKEY_EVENT 0x001e |
@@ -134,6 +135,7 @@ struct toshiba_acpi_dev { | |||
134 | unsigned int system_event_supported:1; | 135 | unsigned int system_event_supported:1; |
135 | unsigned int ntfy_supported:1; | 136 | unsigned int ntfy_supported:1; |
136 | unsigned int info_supported:1; | 137 | unsigned int info_supported:1; |
138 | unsigned int tr_backlight_supported:1; | ||
137 | 139 | ||
138 | struct mutex mutex; | 140 | struct mutex mutex; |
139 | }; | 141 | }; |
@@ -478,34 +480,70 @@ static const struct rfkill_ops toshiba_rfk_ops = { | |||
478 | .poll = bt_rfkill_poll, | 480 | .poll = bt_rfkill_poll, |
479 | }; | 481 | }; |
480 | 482 | ||
483 | static int get_tr_backlight_status(struct toshiba_acpi_dev *dev, bool *enabled) | ||
484 | { | ||
485 | u32 hci_result; | ||
486 | u32 status; | ||
487 | |||
488 | hci_read1(dev, HCI_TR_BACKLIGHT, &status, &hci_result); | ||
489 | *enabled = !status; | ||
490 | return hci_result == HCI_SUCCESS ? 0 : -EIO; | ||
491 | } | ||
492 | |||
493 | static int set_tr_backlight_status(struct toshiba_acpi_dev *dev, bool enable) | ||
494 | { | ||
495 | u32 hci_result; | ||
496 | u32 value = !enable; | ||
497 | |||
498 | hci_write1(dev, HCI_TR_BACKLIGHT, value, &hci_result); | ||
499 | return hci_result == HCI_SUCCESS ? 0 : -EIO; | ||
500 | } | ||
501 | |||
481 | static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ; | 502 | static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ; |
482 | 503 | ||
483 | static int get_lcd(struct backlight_device *bd) | 504 | static int __get_lcd_brightness(struct toshiba_acpi_dev *dev) |
484 | { | 505 | { |
485 | struct toshiba_acpi_dev *dev = bl_get_data(bd); | ||
486 | u32 hci_result; | 506 | u32 hci_result; |
487 | u32 value; | 507 | u32 value; |
508 | int brightness = 0; | ||
509 | |||
510 | if (dev->tr_backlight_supported) { | ||
511 | bool enabled; | ||
512 | int ret = get_tr_backlight_status(dev, &enabled); | ||
513 | if (ret) | ||
514 | return ret; | ||
515 | if (enabled) | ||
516 | return 0; | ||
517 | brightness++; | ||
518 | } | ||
488 | 519 | ||
489 | hci_read1(dev, HCI_LCD_BRIGHTNESS, &value, &hci_result); | 520 | hci_read1(dev, HCI_LCD_BRIGHTNESS, &value, &hci_result); |
490 | if (hci_result == HCI_SUCCESS) | 521 | if (hci_result == HCI_SUCCESS) |
491 | return (value >> HCI_LCD_BRIGHTNESS_SHIFT); | 522 | return brightness + (value >> HCI_LCD_BRIGHTNESS_SHIFT); |
492 | 523 | ||
493 | return -EIO; | 524 | return -EIO; |
494 | } | 525 | } |
495 | 526 | ||
527 | static int get_lcd_brightness(struct backlight_device *bd) | ||
528 | { | ||
529 | struct toshiba_acpi_dev *dev = bl_get_data(bd); | ||
530 | return __get_lcd_brightness(dev); | ||
531 | } | ||
532 | |||
496 | static int lcd_proc_show(struct seq_file *m, void *v) | 533 | static int lcd_proc_show(struct seq_file *m, void *v) |
497 | { | 534 | { |
498 | struct toshiba_acpi_dev *dev = m->private; | 535 | struct toshiba_acpi_dev *dev = m->private; |
499 | int value; | 536 | int value; |
537 | int levels; | ||
500 | 538 | ||
501 | if (!dev->backlight_dev) | 539 | if (!dev->backlight_dev) |
502 | return -ENODEV; | 540 | return -ENODEV; |
503 | 541 | ||
504 | value = get_lcd(dev->backlight_dev); | 542 | levels = dev->backlight_dev->props.max_brightness + 1; |
543 | value = get_lcd_brightness(dev->backlight_dev); | ||
505 | if (value >= 0) { | 544 | if (value >= 0) { |
506 | seq_printf(m, "brightness: %d\n", value); | 545 | seq_printf(m, "brightness: %d\n", value); |
507 | seq_printf(m, "brightness_levels: %d\n", | 546 | seq_printf(m, "brightness_levels: %d\n", levels); |
508 | HCI_LCD_BRIGHTNESS_LEVELS); | ||
509 | return 0; | 547 | return 0; |
510 | } | 548 | } |
511 | 549 | ||
@@ -518,10 +556,19 @@ static int lcd_proc_open(struct inode *inode, struct file *file) | |||
518 | return single_open(file, lcd_proc_show, PDE(inode)->data); | 556 | return single_open(file, lcd_proc_show, PDE(inode)->data); |
519 | } | 557 | } |
520 | 558 | ||
521 | static int set_lcd(struct toshiba_acpi_dev *dev, int value) | 559 | static int set_lcd_brightness(struct toshiba_acpi_dev *dev, int value) |
522 | { | 560 | { |
523 | u32 hci_result; | 561 | u32 hci_result; |
524 | 562 | ||
563 | if (dev->tr_backlight_supported) { | ||
564 | bool enable = !value; | ||
565 | int ret = set_tr_backlight_status(dev, enable); | ||
566 | if (ret) | ||
567 | return ret; | ||
568 | if (value) | ||
569 | value--; | ||
570 | } | ||
571 | |||
525 | value = value << HCI_LCD_BRIGHTNESS_SHIFT; | 572 | value = value << HCI_LCD_BRIGHTNESS_SHIFT; |
526 | hci_write1(dev, HCI_LCD_BRIGHTNESS, value, &hci_result); | 573 | hci_write1(dev, HCI_LCD_BRIGHTNESS, value, &hci_result); |
527 | return hci_result == HCI_SUCCESS ? 0 : -EIO; | 574 | return hci_result == HCI_SUCCESS ? 0 : -EIO; |
@@ -530,7 +577,7 @@ static int set_lcd(struct toshiba_acpi_dev *dev, int value) | |||
530 | static int set_lcd_status(struct backlight_device *bd) | 577 | static int set_lcd_status(struct backlight_device *bd) |
531 | { | 578 | { |
532 | struct toshiba_acpi_dev *dev = bl_get_data(bd); | 579 | struct toshiba_acpi_dev *dev = bl_get_data(bd); |
533 | return set_lcd(dev, bd->props.brightness); | 580 | return set_lcd_brightness(dev, bd->props.brightness); |
534 | } | 581 | } |
535 | 582 | ||
536 | static ssize_t lcd_proc_write(struct file *file, const char __user *buf, | 583 | static ssize_t lcd_proc_write(struct file *file, const char __user *buf, |
@@ -541,6 +588,7 @@ static ssize_t lcd_proc_write(struct file *file, const char __user *buf, | |||
541 | size_t len; | 588 | size_t len; |
542 | int value; | 589 | int value; |
543 | int ret; | 590 | int ret; |
591 | int levels = dev->backlight_dev->props.max_brightness + 1; | ||
544 | 592 | ||
545 | len = min(count, sizeof(cmd) - 1); | 593 | len = min(count, sizeof(cmd) - 1); |
546 | if (copy_from_user(cmd, buf, len)) | 594 | if (copy_from_user(cmd, buf, len)) |
@@ -548,8 +596,8 @@ static ssize_t lcd_proc_write(struct file *file, const char __user *buf, | |||
548 | cmd[len] = '\0'; | 596 | cmd[len] = '\0'; |
549 | 597 | ||
550 | if (sscanf(cmd, " brightness : %i", &value) == 1 && | 598 | if (sscanf(cmd, " brightness : %i", &value) == 1 && |
551 | value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) { | 599 | value >= 0 && value < levels) { |
552 | ret = set_lcd(dev, value); | 600 | ret = set_lcd_brightness(dev, value); |
553 | if (ret == 0) | 601 | if (ret == 0) |
554 | ret = count; | 602 | ret = count; |
555 | } else { | 603 | } else { |
@@ -860,8 +908,9 @@ static void remove_toshiba_proc_entries(struct toshiba_acpi_dev *dev) | |||
860 | } | 908 | } |
861 | 909 | ||
862 | static const struct backlight_ops toshiba_backlight_data = { | 910 | static const struct backlight_ops toshiba_backlight_data = { |
863 | .get_brightness = get_lcd, | 911 | .options = BL_CORE_SUSPENDRESUME, |
864 | .update_status = set_lcd_status, | 912 | .get_brightness = get_lcd_brightness, |
913 | .update_status = set_lcd_status, | ||
865 | }; | 914 | }; |
866 | 915 | ||
867 | static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str, | 916 | static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str, |
@@ -1020,6 +1069,56 @@ static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) | |||
1020 | return error; | 1069 | return error; |
1021 | } | 1070 | } |
1022 | 1071 | ||
1072 | static int __devinit toshiba_acpi_setup_backlight(struct toshiba_acpi_dev *dev) | ||
1073 | { | ||
1074 | struct backlight_properties props; | ||
1075 | int brightness; | ||
1076 | int ret; | ||
1077 | bool enabled; | ||
1078 | |||
1079 | /* | ||
1080 | * Some machines don't support the backlight methods at all, and | ||
1081 | * others support it read-only. Either of these is pretty useless, | ||
1082 | * so only register the backlight device if the backlight method | ||
1083 | * supports both reads and writes. | ||
1084 | */ | ||
1085 | brightness = __get_lcd_brightness(dev); | ||
1086 | if (brightness < 0) | ||
1087 | return 0; | ||
1088 | ret = set_lcd_brightness(dev, brightness); | ||
1089 | if (ret) { | ||
1090 | pr_debug("Backlight method is read-only, disabling backlight support\n"); | ||
1091 | return 0; | ||
1092 | } | ||
1093 | |||
1094 | /* Determine whether or not BIOS supports transflective backlight */ | ||
1095 | ret = get_tr_backlight_status(dev, &enabled); | ||
1096 | dev->tr_backlight_supported = !ret; | ||
1097 | |||
1098 | memset(&props, 0, sizeof(props)); | ||
1099 | props.type = BACKLIGHT_PLATFORM; | ||
1100 | props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; | ||
1101 | |||
1102 | /* adding an extra level and having 0 change to transflective mode */ | ||
1103 | if (dev->tr_backlight_supported) | ||
1104 | props.max_brightness++; | ||
1105 | |||
1106 | dev->backlight_dev = backlight_device_register("toshiba", | ||
1107 | &dev->acpi_dev->dev, | ||
1108 | dev, | ||
1109 | &toshiba_backlight_data, | ||
1110 | &props); | ||
1111 | if (IS_ERR(dev->backlight_dev)) { | ||
1112 | ret = PTR_ERR(dev->backlight_dev); | ||
1113 | pr_err("Could not register toshiba backlight device\n"); | ||
1114 | dev->backlight_dev = NULL; | ||
1115 | return ret; | ||
1116 | } | ||
1117 | |||
1118 | dev->backlight_dev->props.brightness = brightness; | ||
1119 | return 0; | ||
1120 | } | ||
1121 | |||
1023 | static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type) | 1122 | static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type) |
1024 | { | 1123 | { |
1025 | struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); | 1124 | struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); |
@@ -1078,7 +1177,6 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) | |||
1078 | u32 dummy; | 1177 | u32 dummy; |
1079 | bool bt_present; | 1178 | bool bt_present; |
1080 | int ret = 0; | 1179 | int ret = 0; |
1081 | struct backlight_properties props; | ||
1082 | 1180 | ||
1083 | if (toshiba_acpi) | 1181 | if (toshiba_acpi) |
1084 | return -EBUSY; | 1182 | return -EBUSY; |
@@ -1104,22 +1202,9 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) | |||
1104 | 1202 | ||
1105 | mutex_init(&dev->mutex); | 1203 | mutex_init(&dev->mutex); |
1106 | 1204 | ||
1107 | memset(&props, 0, sizeof(props)); | 1205 | ret = toshiba_acpi_setup_backlight(dev); |
1108 | props.type = BACKLIGHT_PLATFORM; | 1206 | if (ret) |
1109 | props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; | ||
1110 | dev->backlight_dev = backlight_device_register("toshiba", | ||
1111 | &acpi_dev->dev, | ||
1112 | dev, | ||
1113 | &toshiba_backlight_data, | ||
1114 | &props); | ||
1115 | if (IS_ERR(dev->backlight_dev)) { | ||
1116 | ret = PTR_ERR(dev->backlight_dev); | ||
1117 | |||
1118 | pr_err("Could not register toshiba backlight device\n"); | ||
1119 | dev->backlight_dev = NULL; | ||
1120 | goto error; | 1207 | goto error; |
1121 | } | ||
1122 | dev->backlight_dev->props.brightness = get_lcd(dev->backlight_dev); | ||
1123 | 1208 | ||
1124 | /* Register rfkill switch for Bluetooth */ | 1209 | /* Register rfkill switch for Bluetooth */ |
1125 | if (hci_get_bt_present(dev, &bt_present) == HCI_SUCCESS && bt_present) { | 1210 | if (hci_get_bt_present(dev, &bt_present) == HCI_SUCCESS && bt_present) { |
diff --git a/drivers/platform/x86/xo1-rfkill.c b/drivers/platform/x86/xo1-rfkill.c index 41781ed8301c..b57ad8641480 100644 --- a/drivers/platform/x86/xo1-rfkill.c +++ b/drivers/platform/x86/xo1-rfkill.c | |||
@@ -15,15 +15,26 @@ | |||
15 | 15 | ||
16 | #include <asm/olpc.h> | 16 | #include <asm/olpc.h> |
17 | 17 | ||
18 | static bool card_blocked; | ||
19 | |||
18 | static int rfkill_set_block(void *data, bool blocked) | 20 | static int rfkill_set_block(void *data, bool blocked) |
19 | { | 21 | { |
20 | unsigned char cmd; | 22 | unsigned char cmd; |
23 | int r; | ||
24 | |||
25 | if (blocked == card_blocked) | ||
26 | return 0; | ||
27 | |||
21 | if (blocked) | 28 | if (blocked) |
22 | cmd = EC_WLAN_ENTER_RESET; | 29 | cmd = EC_WLAN_ENTER_RESET; |
23 | else | 30 | else |
24 | cmd = EC_WLAN_LEAVE_RESET; | 31 | cmd = EC_WLAN_LEAVE_RESET; |
25 | 32 | ||
26 | return olpc_ec_cmd(cmd, NULL, 0, NULL, 0); | 33 | r = olpc_ec_cmd(cmd, NULL, 0, NULL, 0); |
34 | if (r == 0) | ||
35 | card_blocked = blocked; | ||
36 | |||
37 | return r; | ||
27 | } | 38 | } |
28 | 39 | ||
29 | static const struct rfkill_ops rfkill_ops = { | 40 | static const struct rfkill_ops rfkill_ops = { |