summaryrefslogtreecommitdiff
path: root/drivers/leds/dell-led.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-06-13 00:08:09 +0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-06-13 00:08:09 +0400
commit7c574cf6aeb75920ba4d3af937bb1b3c42785ac4 (patch)
tree336586614fcbba9fb8fc6095d23f87d8ce11b559 /drivers/leds/dell-led.c
parentaf76004cf8b4f368583bda22d7e348e40a338b91 (diff)
parent0c9a03b68511daf078256367e7a98d7ff3b7dfcb (diff)
downloadlinux-7c574cf6aeb75920ba4d3af937bb1b3c42785ac4.tar.xz
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/linux-leds
Pull LED updates from Bryan Wu: "I just found merge window is open and I'm quite busy and almost forget to send out this pull request. Thanks Russell and Alexandre ping me about this. So basically we got some clean up and leds-pwm fixing patches from Russell" * 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/linux-leds: leds: Remove duplicated OOM message for individual driver drivers/leds: Replace __get_cpu_var use through this_cpu_ptr leds: lp55xx: add DT bindings for LP55231 leds: 88pm860x: Fix missing refcount decrement for parent of_node leds: 88pm860x: Use of_get_child_by_name leds: leds-pwm: add DT support for LEDs wired to supply leds: leds-pwm: implement PWM inversion leds: leds-pwm: convert OF parsing code to use led_pwm_add() leds: leds-pwm: provide a common function to setup a single led-pwm device leds: pca9685: Remove leds-pca9685 driver dell-led: add mic mute led interface
Diffstat (limited to 'drivers/leds/dell-led.c')
-rw-r--r--drivers/leds/dell-led.c171
1 files changed, 164 insertions, 7 deletions
diff --git a/drivers/leds/dell-led.c b/drivers/leds/dell-led.c
index e5c57389efd6..c36acaf566a6 100644
--- a/drivers/leds/dell-led.c
+++ b/drivers/leds/dell-led.c
@@ -15,12 +15,15 @@
#include <linux/leds.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/dmi.h>
+#include <linux/dell-led.h>
MODULE_AUTHOR("Louis Davis/Jim Dailey");
MODULE_DESCRIPTION("Dell LED Control Driver");
MODULE_LICENSE("GPL");
#define DELL_LED_BIOS_GUID "F6E4FE6E-909D-47cb-8BAB-C9F6F2F8D396"
+#define DELL_APP_GUID "A80593CE-A997-11DA-B012-B622A1EF5492"
MODULE_ALIAS("wmi:" DELL_LED_BIOS_GUID);
/* Error Result Codes: */
@@ -39,6 +42,149 @@ MODULE_ALIAS("wmi:" DELL_LED_BIOS_GUID);
#define CMD_LED_OFF 17
#define CMD_LED_BLINK 18
+struct app_wmi_args {
+ u16 class;
+ u16 selector;
+ u32 arg1;
+ u32 arg2;
+ u32 arg3;
+ u32 arg4;
+ u32 res1;
+ u32 res2;
+ u32 res3;
+ u32 res4;
+ char dummy[92];
+};
+
+#define GLOBAL_MIC_MUTE_ENABLE 0x364
+#define GLOBAL_MIC_MUTE_DISABLE 0x365
+
+struct dell_bios_data_token {
+ u16 tokenid;
+ u16 location;
+ u16 value;
+};
+
+struct __attribute__ ((__packed__)) dell_bios_calling_interface {
+ struct dmi_header header;
+ u16 cmd_io_addr;
+ u8 cmd_io_code;
+ u32 supported_cmds;
+ struct dell_bios_data_token damap[];
+};
+
+static struct dell_bios_data_token dell_mic_tokens[2];
+
+static int dell_wmi_perform_query(struct app_wmi_args *args)
+{
+ struct app_wmi_args *bios_return;
+ union acpi_object *obj;
+ struct acpi_buffer input;
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ acpi_status status;
+ u32 rc = -EINVAL;
+
+ input.length = 128;
+ input.pointer = args;
+
+ status = wmi_evaluate_method(DELL_APP_GUID, 0, 1, &input, &output);
+ if (!ACPI_SUCCESS(status))
+ goto err_out0;
+
+ obj = output.pointer;
+ if (!obj)
+ goto err_out0;
+
+ if (obj->type != ACPI_TYPE_BUFFER)
+ goto err_out1;
+
+ bios_return = (struct app_wmi_args *)obj->buffer.pointer;
+ rc = bios_return->res1;
+ if (rc)
+ goto err_out1;
+
+ memcpy(args, bios_return, sizeof(struct app_wmi_args));
+ rc = 0;
+
+ err_out1:
+ kfree(obj);
+ err_out0:
+ return rc;
+}
+
+static void __init find_micmute_tokens(const struct dmi_header *dm, void *dummy)
+{
+ struct dell_bios_calling_interface *calling_interface;
+ struct dell_bios_data_token *token;
+ int token_size = sizeof(struct dell_bios_data_token);
+ int i = 0;
+
+ if (dm->type == 0xda && dm->length > 17) {
+ calling_interface = container_of(dm,
+ struct dell_bios_calling_interface, header);
+
+ token = &calling_interface->damap[i];
+ while (token->tokenid != 0xffff) {
+ if (token->tokenid == GLOBAL_MIC_MUTE_DISABLE)
+ memcpy(&dell_mic_tokens[0], token, token_size);
+ else if (token->tokenid == GLOBAL_MIC_MUTE_ENABLE)
+ memcpy(&dell_mic_tokens[1], token, token_size);
+
+ i++;
+ token = &calling_interface->damap[i];
+ }
+ }
+}
+
+static int dell_micmute_led_set(int state)
+{
+ struct app_wmi_args args;
+ struct dell_bios_data_token *token;
+
+ if (!wmi_has_guid(DELL_APP_GUID))
+ return -ENODEV;
+
+ if (state == 0 || state == 1)
+ token = &dell_mic_tokens[state];
+ else
+ return -EINVAL;
+
+ memset(&args, 0, sizeof(struct app_wmi_args));
+
+ args.class = 1;
+ args.arg1 = token->location;
+ args.arg2 = token->value;
+
+ dell_wmi_perform_query(&args);
+
+ return state;
+}
+
+int dell_app_wmi_led_set(int whichled, int on)
+{
+ int state = 0;
+
+ switch (whichled) {
+ case DELL_LED_MICMUTE:
+ state = dell_micmute_led_set(on);
+ break;
+ default:
+ pr_warn("led type %x is not supported\n", whichled);
+ break;
+ }
+
+ return state;
+}
+EXPORT_SYMBOL_GPL(dell_app_wmi_led_set);
+
+static int __init dell_micmute_led_init(void)
+{
+ memset(dell_mic_tokens, 0, sizeof(struct dell_bios_data_token) * 2);
+ dmi_walk(find_micmute_tokens, NULL);
+
+ return 0;
+}
+
struct bios_args {
unsigned char length;
unsigned char result_code;
@@ -181,21 +327,32 @@ static int __init dell_led_init(void)
{
int error = 0;
- if (!wmi_has_guid(DELL_LED_BIOS_GUID))
+ if (!wmi_has_guid(DELL_LED_BIOS_GUID) && !wmi_has_guid(DELL_APP_GUID))
return -ENODEV;
- error = led_off();
- if (error != 0)
- return -ENODEV;
+ if (wmi_has_guid(DELL_APP_GUID))
+ error = dell_micmute_led_init();
- return led_classdev_register(NULL, &dell_led);
+ if (wmi_has_guid(DELL_LED_BIOS_GUID)) {
+ error = led_off();
+ if (error != 0)
+ return -ENODEV;
+
+ error = led_classdev_register(NULL, &dell_led);
+ }
+
+ return error;
}
static void __exit dell_led_exit(void)
{
- led_classdev_unregister(&dell_led);
+ int error = 0;
- led_off();
+ if (wmi_has_guid(DELL_LED_BIOS_GUID)) {
+ error = led_off();
+ if (error == 0)
+ led_classdev_unregister(&dell_led);
+ }
}
module_init(dell_led_init);