Commit 6c3f6e6c575a0a992429427d4978c6091756a526
Committed by
Matthew Garrett
1 parent
5ca5671891
Exists in
master
and in
7 other branches
toshiba-acpi: Add support for Toshiba Illumination.
Add support for Toshiba Illumination. This is a set of LEDs installed on some Toshiba laptops. It is controlled through ACPI, the commands has been found through reverse engineering. It has been tested on a Toshiba Qosmio G50-122. Signed-off-by: Pierre Ducroquet <pinaraf@pinaraf.info> Signed-off-by: Matthew Garrett <mjg@redhat.com>
Showing 1 changed file with 117 additions and 0 deletions Side-by-side Diff
drivers/platform/x86/toshiba_acpi.c
... | ... | @@ -4,6 +4,7 @@ |
4 | 4 | * |
5 | 5 | * Copyright (C) 2002-2004 John Belmonte |
6 | 6 | * Copyright (C) 2008 Philip Langdale |
7 | + * Copyright (C) 2010 Pierre Ducroquet | |
7 | 8 | * |
8 | 9 | * This program is free software; you can redistribute it and/or modify |
9 | 10 | * it under the terms of the GNU General Public License as published by |
... | ... | @@ -47,6 +48,7 @@ |
47 | 48 | #include <linux/platform_device.h> |
48 | 49 | #include <linux/rfkill.h> |
49 | 50 | #include <linux/input.h> |
51 | +#include <linux/leds.h> | |
50 | 52 | #include <linux/slab.h> |
51 | 53 | |
52 | 54 | #include <asm/uaccess.h> |
... | ... | @@ -287,6 +289,7 @@ |
287 | 289 | struct platform_device *p_dev; |
288 | 290 | struct rfkill *bt_rfk; |
289 | 291 | struct input_dev *hotkey_dev; |
292 | + int illumination_installed; | |
290 | 293 | acpi_handle handle; |
291 | 294 | |
292 | 295 | const char *bt_name; |
... | ... | @@ -294,6 +297,110 @@ |
294 | 297 | struct mutex mutex; |
295 | 298 | }; |
296 | 299 | |
300 | +/* Illumination support */ | |
301 | +static int toshiba_illumination_available(void) | |
302 | +{ | |
303 | + u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; | |
304 | + u32 out[HCI_WORDS]; | |
305 | + acpi_status status; | |
306 | + | |
307 | + in[0] = 0xf100; | |
308 | + status = hci_raw(in, out); | |
309 | + if (ACPI_FAILURE(status)) { | |
310 | + printk(MY_INFO "Illumination device not available\n"); | |
311 | + return 0; | |
312 | + } | |
313 | + in[0] = 0xf400; | |
314 | + status = hci_raw(in, out); | |
315 | + return 1; | |
316 | +} | |
317 | + | |
318 | +static void toshiba_illumination_set(struct led_classdev *cdev, | |
319 | + enum led_brightness brightness) | |
320 | +{ | |
321 | + u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; | |
322 | + u32 out[HCI_WORDS]; | |
323 | + acpi_status status; | |
324 | + | |
325 | + /* First request : initialize communication. */ | |
326 | + in[0] = 0xf100; | |
327 | + status = hci_raw(in, out); | |
328 | + if (ACPI_FAILURE(status)) { | |
329 | + printk(MY_INFO "Illumination device not available\n"); | |
330 | + return; | |
331 | + } | |
332 | + | |
333 | + if (brightness) { | |
334 | + /* Switch the illumination on */ | |
335 | + in[0] = 0xf400; | |
336 | + in[1] = 0x14e; | |
337 | + in[2] = 1; | |
338 | + status = hci_raw(in, out); | |
339 | + if (ACPI_FAILURE(status)) { | |
340 | + printk(MY_INFO "ACPI call for illumination failed.\n"); | |
341 | + return; | |
342 | + } | |
343 | + } else { | |
344 | + /* Switch the illumination off */ | |
345 | + in[0] = 0xf400; | |
346 | + in[1] = 0x14e; | |
347 | + in[2] = 0; | |
348 | + status = hci_raw(in, out); | |
349 | + if (ACPI_FAILURE(status)) { | |
350 | + printk(MY_INFO "ACPI call for illumination failed.\n"); | |
351 | + return; | |
352 | + } | |
353 | + } | |
354 | + | |
355 | + /* Last request : close communication. */ | |
356 | + in[0] = 0xf200; | |
357 | + in[1] = 0; | |
358 | + in[2] = 0; | |
359 | + hci_raw(in, out); | |
360 | +} | |
361 | + | |
362 | +static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) | |
363 | +{ | |
364 | + u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; | |
365 | + u32 out[HCI_WORDS]; | |
366 | + acpi_status status; | |
367 | + enum led_brightness result; | |
368 | + | |
369 | + /* First request : initialize communication. */ | |
370 | + in[0] = 0xf100; | |
371 | + status = hci_raw(in, out); | |
372 | + if (ACPI_FAILURE(status)) { | |
373 | + printk(MY_INFO "Illumination device not available\n"); | |
374 | + return LED_OFF; | |
375 | + } | |
376 | + | |
377 | + /* Check the illumination */ | |
378 | + in[0] = 0xf300; | |
379 | + in[1] = 0x14e; | |
380 | + status = hci_raw(in, out); | |
381 | + if (ACPI_FAILURE(status)) { | |
382 | + printk(MY_INFO "ACPI call for illumination failed.\n"); | |
383 | + return LED_OFF; | |
384 | + } | |
385 | + | |
386 | + result = out[2] ? LED_FULL : LED_OFF; | |
387 | + | |
388 | + /* Last request : close communication. */ | |
389 | + in[0] = 0xf200; | |
390 | + in[1] = 0; | |
391 | + in[2] = 0; | |
392 | + hci_raw(in, out); | |
393 | + | |
394 | + return result; | |
395 | +} | |
396 | + | |
397 | +static struct led_classdev toshiba_led = { | |
398 | + .name = "toshiba::illumination", | |
399 | + .max_brightness = 1, | |
400 | + .brightness_set = toshiba_illumination_set, | |
401 | + .brightness_get = toshiba_illumination_get, | |
402 | +}; | |
403 | + | |
297 | 404 | static struct toshiba_acpi_dev toshiba_acpi = { |
298 | 405 | .bt_name = "Toshiba Bluetooth", |
299 | 406 | }; |
... | ... | @@ -913,6 +1020,9 @@ |
913 | 1020 | acpi_remove_notify_handler(toshiba_acpi.handle, ACPI_DEVICE_NOTIFY, |
914 | 1021 | toshiba_acpi_notify); |
915 | 1022 | |
1023 | + if (toshiba_acpi.illumination_installed) | |
1024 | + led_classdev_unregister(&toshiba_led); | |
1025 | + | |
916 | 1026 | platform_device_unregister(toshiba_acpi.p_dev); |
917 | 1027 | |
918 | 1028 | return; |
... | ... | @@ -1005,6 +1115,13 @@ |
1005 | 1115 | toshiba_acpi_exit(); |
1006 | 1116 | return ret; |
1007 | 1117 | } |
1118 | + } | |
1119 | + | |
1120 | + toshiba_acpi.illumination_installed = 0; | |
1121 | + if (toshiba_illumination_available()) { | |
1122 | + if (!led_classdev_register(&(toshiba_acpi.p_dev->dev), | |
1123 | + &toshiba_led)) | |
1124 | + toshiba_acpi.illumination_installed = 1; | |
1008 | 1125 | } |
1009 | 1126 | |
1010 | 1127 | return 0; |