From 58c688657c73a6a87aa9ea414d510b5827cec298 Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 2 Mar 2014 18:01:19 +0100 Subject: Fix sleep / suspend keys for Toughbook CF-51 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hi all, my panasonic cf-51 does no longer react to the suspend and hibernate keys. I cannot tell when this started since I no longer use the machine on a daily basis, but I suspect it started when userspace switched from using /proc/acpi/event to the input layer, wich was quite some time ago ;-) Today I investigated the issue and found that the firmware simply does not generate any event on "key down" for those keys, but only on "key up". The attached patch works around the problem. Best regards, Stefan -- Stefan Seyfried Linux Consultant & Developer -- GPG Key: 0x731B665B B1 Systems GmbH Osterfeldstraße 7 / 85088 Vohburg / http://www.b1-systems.de GF: Ralph Dehner / Unternehmenssitz: Vohburg / AG: Ingolstadt,HRB 3537 >From 7c96fee748cfd3e64732a7ac142f5dea07d7379f Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Sun, 2 Mar 2014 17:50:01 +0100 Subject: [PATCH] panasonic-laptop: fix sleep keys on CF-51 At least on my CF-51, both sleep and hibernate keys do not generate "key down" events, only "key up". Because of this, the input layer does ignore both keys. The work around is to generate a key down event before the key up. To avoid double events on non-broken firmware, this is only done if no key down is ever seen for those keys. Signed-off-by: Stefan Seyfried Signed-off-by: Matthew Garrett --- drivers/platform/x86/panasonic-laptop.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index 609d38779b26..3f870972247c 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -449,6 +449,7 @@ static struct attribute_group pcc_attr_group = { /* hotkey input device driver */ +static int sleep_keydown_seen; static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) { struct input_dev *hotk_input_dev = pcc->input_dev; @@ -462,6 +463,16 @@ static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) "error getting hotkey status\n")); return; } + + /* hack: some firmware sends no key down for sleep / hibernate */ + if ((result & 0xf) == 0x7 || (result & 0xf) == 0xa) { + if (result & 0x80) + sleep_keydown_seen = 1; + if (!sleep_keydown_seen) + sparse_keymap_report_event(hotk_input_dev, + result & 0xf, 0x80, false); + } + if (!sparse_keymap_report_event(hotk_input_dev, result & 0xf, result & 0x80, false)) ACPI_DEBUG_PRINT((ACPI_DB_ERROR, -- cgit v1.2.3-70-g09d2 From 94d164dc411ffe8d59c7201ade73126297380733 Mon Sep 17 00:00:00 2001 From: Javier Achirica Date: Fri, 21 Mar 2014 08:01:12 +0900 Subject: sony-laptop: add support as Fn+1 as a hot key Signed-off-by: Javier Achirica Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 8f8551a63cc0..fe5424c3b9af 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -1122,6 +1122,8 @@ static struct sony_nc_event sony_100_events[] = { { 0x25, SONYPI_EVENT_ANYBUTTON_RELEASED }, { 0xa6, SONYPI_EVENT_HELP_PRESSED }, { 0x26, SONYPI_EVENT_ANYBUTTON_RELEASED }, + { 0xa8, SONYPI_EVENT_FNKEY_1 }, + { 0x28, SONYPI_EVENT_ANYBUTTON_RELEASED }, { 0, 0 }, }; -- cgit v1.2.3-70-g09d2 From 353120760599c565e2d0a4174a0f3faeeb2e56ae Mon Sep 17 00:00:00 2001 From: Javier Achirica Date: Fri, 21 Mar 2014 08:01:13 +0900 Subject: sony-laptop: Add support for lid resume settings on Vaio Pro Vaio Pro uses a different handle and doesn't support all the options as other models (only S5 setting v/s S3/4/5). Minor code rework to generalize functions by Mattia Dongili. Signed-off-by: Javier Achirica Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 110 +++++++++++++++++++++---------------- 1 file changed, 62 insertions(+), 48 deletions(-) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index fe5424c3b9af..de173909257a 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -152,7 +152,8 @@ static void sony_nc_battery_care_cleanup(struct platform_device *pd); static int sony_nc_thermal_setup(struct platform_device *pd); static void sony_nc_thermal_cleanup(struct platform_device *pd); -static int sony_nc_lid_resume_setup(struct platform_device *pd); +static int sony_nc_lid_resume_setup(struct platform_device *pd, + unsigned int handle); static void sony_nc_lid_resume_cleanup(struct platform_device *pd); static int sony_nc_gfx_switch_setup(struct platform_device *pd, @@ -1341,7 +1342,8 @@ static void sony_nc_function_setup(struct acpi_device *device, result); break; case 0x0119: - result = sony_nc_lid_resume_setup(pf_device); + case 0x015D: + result = sony_nc_lid_resume_setup(pf_device, handle); if (result) pr_err("couldn't set up lid resume function (%d)\n", result); @@ -1422,6 +1424,7 @@ static void sony_nc_function_cleanup(struct platform_device *pd) sony_nc_battery_care_cleanup(pd); break; case 0x0119: + case 0x015D: sony_nc_lid_resume_cleanup(pd); break; case 0x0122: @@ -2223,9 +2226,14 @@ static void sony_nc_thermal_resume(void) #endif /* resume on LID open */ +#define LID_RESUME_S5 0 +#define LID_RESUME_S4 1 +#define LID_RESUME_S3 2 +#define LID_RESUME_MAX 3 struct snc_lid_resume_control { - struct device_attribute attrs[3]; + struct device_attribute attrs[LID_RESUME_MAX]; unsigned int status; + int handle; }; static struct snc_lid_resume_control *lid_ctl; @@ -2233,8 +2241,9 @@ static ssize_t sony_nc_lid_resume_store(struct device *dev, struct device_attribute *attr, const char *buffer, size_t count) { - unsigned int result, pos; + unsigned int result; unsigned long value; + unsigned int pos = LID_RESUME_S5; if (count > 31) return -EINVAL; @@ -2247,21 +2256,21 @@ static ssize_t sony_nc_lid_resume_store(struct device *dev, * +--------------+ * 2 1 0 */ - if (strcmp(attr->attr.name, "lid_resume_S3") == 0) - pos = 2; - else if (strcmp(attr->attr.name, "lid_resume_S4") == 0) - pos = 1; - else if (strcmp(attr->attr.name, "lid_resume_S5") == 0) - pos = 0; - else - return -EINVAL; + while (pos < LID_RESUME_MAX) { + if (&lid_ctl->attrs[pos].attr == &attr->attr) + break; + pos++; + } + if (pos == LID_RESUME_MAX) + return -EINVAL; if (value) value = lid_ctl->status | (1 << pos); else value = lid_ctl->status & ~(1 << pos); - if (sony_call_snc_handle(0x0119, value << 0x10 | 0x0100, &result)) + if (sony_call_snc_handle(lid_ctl->handle, value << 0x10 | 0x0100, + &result)) return -EIO; lid_ctl->status = value; @@ -2270,29 +2279,27 @@ static ssize_t sony_nc_lid_resume_store(struct device *dev, } static ssize_t sony_nc_lid_resume_show(struct device *dev, - struct device_attribute *attr, char *buffer) + struct device_attribute *attr, + char *buffer) { - unsigned int pos; - - if (strcmp(attr->attr.name, "lid_resume_S3") == 0) - pos = 2; - else if (strcmp(attr->attr.name, "lid_resume_S4") == 0) - pos = 1; - else if (strcmp(attr->attr.name, "lid_resume_S5") == 0) - pos = 0; - else - return -EINVAL; - - return snprintf(buffer, PAGE_SIZE, "%d\n", - (lid_ctl->status >> pos) & 0x01); + unsigned int pos = LID_RESUME_S5; + + while (pos < LID_RESUME_MAX) { + if (&lid_ctl->attrs[pos].attr == &attr->attr) + return snprintf(buffer, PAGE_SIZE, "%d\n", + (lid_ctl->status >> pos) & 0x01); + pos++; + } + return -EINVAL; } -static int sony_nc_lid_resume_setup(struct platform_device *pd) +static int sony_nc_lid_resume_setup(struct platform_device *pd, + unsigned int handle) { unsigned int result; int i; - if (sony_call_snc_handle(0x0119, 0x0000, &result)) + if (sony_call_snc_handle(handle, 0x0000, &result)) return -EIO; lid_ctl = kzalloc(sizeof(struct snc_lid_resume_control), GFP_KERNEL); @@ -2300,26 +2307,29 @@ static int sony_nc_lid_resume_setup(struct platform_device *pd) return -ENOMEM; lid_ctl->status = result & 0x7; + lid_ctl->handle = handle; sysfs_attr_init(&lid_ctl->attrs[0].attr); - lid_ctl->attrs[0].attr.name = "lid_resume_S3"; - lid_ctl->attrs[0].attr.mode = S_IRUGO | S_IWUSR; - lid_ctl->attrs[0].show = sony_nc_lid_resume_show; - lid_ctl->attrs[0].store = sony_nc_lid_resume_store; - - sysfs_attr_init(&lid_ctl->attrs[1].attr); - lid_ctl->attrs[1].attr.name = "lid_resume_S4"; - lid_ctl->attrs[1].attr.mode = S_IRUGO | S_IWUSR; - lid_ctl->attrs[1].show = sony_nc_lid_resume_show; - lid_ctl->attrs[1].store = sony_nc_lid_resume_store; - - sysfs_attr_init(&lid_ctl->attrs[2].attr); - lid_ctl->attrs[2].attr.name = "lid_resume_S5"; - lid_ctl->attrs[2].attr.mode = S_IRUGO | S_IWUSR; - lid_ctl->attrs[2].show = sony_nc_lid_resume_show; - lid_ctl->attrs[2].store = sony_nc_lid_resume_store; - - for (i = 0; i < 3; i++) { + lid_ctl->attrs[LID_RESUME_S5].attr.name = "lid_resume_S5"; + lid_ctl->attrs[LID_RESUME_S5].attr.mode = S_IRUGO | S_IWUSR; + lid_ctl->attrs[LID_RESUME_S5].show = sony_nc_lid_resume_show; + lid_ctl->attrs[LID_RESUME_S5].store = sony_nc_lid_resume_store; + + if (handle == 0x0119) { + sysfs_attr_init(&lid_ctl->attrs[1].attr); + lid_ctl->attrs[LID_RESUME_S4].attr.name = "lid_resume_S4"; + lid_ctl->attrs[LID_RESUME_S4].attr.mode = S_IRUGO | S_IWUSR; + lid_ctl->attrs[LID_RESUME_S4].show = sony_nc_lid_resume_show; + lid_ctl->attrs[LID_RESUME_S4].store = sony_nc_lid_resume_store; + + sysfs_attr_init(&lid_ctl->attrs[2].attr); + lid_ctl->attrs[LID_RESUME_S3].attr.name = "lid_resume_S3"; + lid_ctl->attrs[LID_RESUME_S3].attr.mode = S_IRUGO | S_IWUSR; + lid_ctl->attrs[LID_RESUME_S3].show = sony_nc_lid_resume_show; + lid_ctl->attrs[LID_RESUME_S3].store = sony_nc_lid_resume_store; + } + for (i = 0; i < LID_RESUME_MAX && + lid_ctl->attrs[LID_RESUME_S3].attr.name; i++) { result = device_create_file(&pd->dev, &lid_ctl->attrs[i]); if (result) goto liderror; @@ -2342,8 +2352,12 @@ static void sony_nc_lid_resume_cleanup(struct platform_device *pd) int i; if (lid_ctl) { - for (i = 0; i < 3; i++) + for (i = 0; i < LID_RESUME_MAX; i++) { + if (!lid_ctl->attrs[i].attr.name) + break; + device_remove_file(&pd->dev, &lid_ctl->attrs[i]); + } kfree(lid_ctl); lid_ctl = NULL; -- cgit v1.2.3-70-g09d2 From 9e04c9080dfadd9bc254ffaa738f39e4796d0ab7 Mon Sep 17 00:00:00 2001 From: Javier Achirica Date: Fri, 21 Mar 2014 08:01:14 +0900 Subject: sony-laptop: add panel_id function Signed-off-by: Javier Achirica Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 59 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index de173909257a..d1f712e368ce 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -164,6 +164,9 @@ static int __sony_nc_gfx_switch_status_get(void); static int sony_nc_highspeed_charging_setup(struct platform_device *pd); static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd); +static int sony_nc_panelid_setup(struct platform_device *pd); +static void sony_nc_panelid_cleanup(struct platform_device *pd); + static int sony_nc_touchpad_setup(struct platform_device *pd, unsigned int handle); static void sony_nc_touchpad_cleanup(struct platform_device *pd); @@ -1385,6 +1388,12 @@ static void sony_nc_function_setup(struct acpi_device *device, pr_err("couldn't set up keyboard backlight function (%d)\n", result); break; + case 0x011D: + result = sony_nc_panelid_setup(pf_device); + if (result) + pr_err("couldn't set up panel ID function (%d)\n", + result); + break; default: continue; } @@ -1449,6 +1458,9 @@ static void sony_nc_function_cleanup(struct platform_device *pd) case 0x0163: sony_nc_kbd_backlight_cleanup(pd, handle); break; + case 0x011D: + sony_nc_panelid_cleanup(pd); + break; default: continue; } @@ -2540,6 +2552,53 @@ static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd) } } +/* Panel ID function */ +static struct device_attribute *panel_handle; + +static ssize_t sony_nc_panelid_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + unsigned int result; + + if (sony_call_snc_handle(0x011D, 0x0000, &result)) + return -EIO; + + return snprintf(buffer, PAGE_SIZE, "%d\n", result); +} + +static int sony_nc_panelid_setup(struct platform_device *pd) +{ + unsigned int result; + + panel_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); + if (!panel_handle) + return -ENOMEM; + + sysfs_attr_init(&panel_handle->attr); + panel_handle->attr.name = "panel_id"; + panel_handle->attr.mode = S_IRUGO; + panel_handle->show = sony_nc_panelid_show; + panel_handle->store = NULL; + + result = device_create_file(&pd->dev, panel_handle); + if (result) { + kfree(panel_handle); + panel_handle = NULL; + return result; + } + + return 0; +} + +static void sony_nc_panelid_cleanup(struct platform_device *pd) +{ + if (panel_handle) { + device_remove_file(&pd->dev, panel_handle); + kfree(panel_handle); + panel_handle = NULL; + } +} + /* Touchpad enable/disable */ struct touchpad_control { struct device_attribute attr; -- cgit v1.2.3-70-g09d2 From 2a26f3415865a9540b0b8a93455eeeea90a98983 Mon Sep 17 00:00:00 2001 From: Javier Achirica Date: Fri, 21 Mar 2014 08:01:15 +0900 Subject: sony-laptop: add usb charge function Allows to specify if the USB socket should charge attached devices while the laptop is suspended to ram. Signed-off-by: Javier Achirica Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 86 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index d1f712e368ce..9620285cb9af 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -164,6 +164,9 @@ static int __sony_nc_gfx_switch_status_get(void); static int sony_nc_highspeed_charging_setup(struct platform_device *pd); static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd); +static int sony_nc_usb_charge_setup(struct platform_device *pd); +static void sony_nc_usb_charge_cleanup(struct platform_device *pd); + static int sony_nc_panelid_setup(struct platform_device *pd); static void sony_nc_panelid_cleanup(struct platform_device *pd); @@ -1388,6 +1391,12 @@ static void sony_nc_function_setup(struct acpi_device *device, pr_err("couldn't set up keyboard backlight function (%d)\n", result); break; + case 0x0155: + result = sony_nc_usb_charge_setup(pf_device); + if (result) + pr_err("couldn't set up USB charge support (%d)\n", + result); + break; case 0x011D: result = sony_nc_panelid_setup(pf_device); if (result) @@ -1458,6 +1467,9 @@ static void sony_nc_function_cleanup(struct platform_device *pd) case 0x0163: sony_nc_kbd_backlight_cleanup(pd, handle); break; + case 0x0155: + sony_nc_usb_charge_cleanup(pd); + break; case 0x011D: sony_nc_panelid_cleanup(pd); break; @@ -2552,6 +2564,80 @@ static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd) } } +/* USB charge function */ +static struct device_attribute *uc_handle; + +static ssize_t sony_nc_usb_charge_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + unsigned int result; + unsigned long value; + + if (count > 31) + return -EINVAL; + + if (kstrtoul(buffer, 10, &value) || value > 1) + return -EINVAL; + + if (sony_call_snc_handle(0x0155, value << 0x10 | 0x0100, &result)) + return -EIO; + + return count; +} + +static ssize_t sony_nc_usb_charge_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + unsigned int result; + + if (sony_call_snc_handle(0x0155, 0x0000, &result)) + return -EIO; + + return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x01); +} + +static int sony_nc_usb_charge_setup(struct platform_device *pd) +{ + unsigned int result; + + if (sony_call_snc_handle(0x0155, 0x0000, &result) || !(result & 0x01)) { + /* some models advertise the handle but have no implementation + * for it + */ + pr_info("No USB Charge capability found\n"); + return 0; + } + + uc_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); + if (!uc_handle) + return -ENOMEM; + + sysfs_attr_init(&uc_handle->attr); + uc_handle->attr.name = "usb_charge"; + uc_handle->attr.mode = S_IRUGO | S_IWUSR; + uc_handle->show = sony_nc_usb_charge_show; + uc_handle->store = sony_nc_usb_charge_store; + + result = device_create_file(&pd->dev, uc_handle); + if (result) { + kfree(uc_handle); + uc_handle = NULL; + return result; + } + + return 0; +} + +static void sony_nc_usb_charge_cleanup(struct platform_device *pd) +{ + if (uc_handle) { + device_remove_file(&pd->dev, uc_handle); + kfree(uc_handle); + uc_handle = NULL; + } +} + /* Panel ID function */ static struct device_attribute *panel_handle; -- cgit v1.2.3-70-g09d2 From c62f15395c780fff606cfec23d66417a80a5ad81 Mon Sep 17 00:00:00 2001 From: Javier Achirica Date: Fri, 21 Mar 2014 08:01:16 +0900 Subject: sony-laptop: add fan speed regulation function Rework error exit logic by Mattia Dongili. Signed-off-by: Javier Achirica Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 119 +++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 9620285cb9af..7b5a56d59bac 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -164,6 +164,9 @@ static int __sony_nc_gfx_switch_status_get(void); static int sony_nc_highspeed_charging_setup(struct platform_device *pd); static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd); +static int sony_nc_fanspeed_setup(struct platform_device *pd); +static void sony_nc_fanspeed_cleanup(struct platform_device *pd); + static int sony_nc_usb_charge_setup(struct platform_device *pd); static void sony_nc_usb_charge_cleanup(struct platform_device *pd); @@ -1391,6 +1394,12 @@ static void sony_nc_function_setup(struct acpi_device *device, pr_err("couldn't set up keyboard backlight function (%d)\n", result); break; + case 0x0149: + result = sony_nc_fanspeed_setup(pf_device); + if (result) + pr_err("couldn't set up fan speed function (%d)\n", + result); + break; case 0x0155: result = sony_nc_usb_charge_setup(pf_device); if (result) @@ -1467,6 +1476,9 @@ static void sony_nc_function_cleanup(struct platform_device *pd) case 0x0163: sony_nc_kbd_backlight_cleanup(pd, handle); break; + case 0x0149: + sony_nc_fanspeed_cleanup(pd); + break; case 0x0155: sony_nc_usb_charge_cleanup(pd); break; @@ -2564,6 +2576,113 @@ static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd) } } +/* fan speed function */ +static struct device_attribute *fan_handle, *hsf_handle; + +static ssize_t sony_nc_hsfan_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + unsigned int result; + unsigned long value; + + if (count > 31) + return -EINVAL; + + if (kstrtoul(buffer, 10, &value) || value > 1) + return -EINVAL; + + if (sony_call_snc_handle(0x0149, value << 0x10 | 0x0200, &result)) + return -EIO; + + return count; +} + +static ssize_t sony_nc_hsfan_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + unsigned int result; + + if (sony_call_snc_handle(0x0149, 0x0100, &result)) + return -EIO; + + return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x01); +} + +static ssize_t sony_nc_fanspeed_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + unsigned int result; + + if (sony_call_snc_handle(0x0149, 0x0300, &result)) + return -EIO; + + return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0xff); +} + +static int sony_nc_fanspeed_setup(struct platform_device *pd) +{ + unsigned int result; + + fan_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); + if (!fan_handle) + return -ENOMEM; + + hsf_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); + if (!hsf_handle) { + result = -ENOMEM; + goto out_hsf_handle_alloc; + } + + sysfs_attr_init(&fan_handle->attr); + fan_handle->attr.name = "fanspeed"; + fan_handle->attr.mode = S_IRUGO; + fan_handle->show = sony_nc_fanspeed_show; + fan_handle->store = NULL; + + sysfs_attr_init(&hsf_handle->attr); + hsf_handle->attr.name = "fan_forced"; + hsf_handle->attr.mode = S_IRUGO | S_IWUSR; + hsf_handle->show = sony_nc_hsfan_show; + hsf_handle->store = sony_nc_hsfan_store; + + result = device_create_file(&pd->dev, fan_handle); + if (result) + goto out_fan_handle; + + result = device_create_file(&pd->dev, hsf_handle); + if (result) + goto out_hsf_handle; + + return 0; + +out_hsf_handle: + device_remove_file(&pd->dev, fan_handle); + +out_fan_handle: + kfree(hsf_handle); + hsf_handle = NULL; + +out_hsf_handle_alloc: + kfree(fan_handle); + fan_handle = NULL; + return result; +} + +static void sony_nc_fanspeed_cleanup(struct platform_device *pd) +{ + if (fan_handle) { + device_remove_file(&pd->dev, fan_handle); + kfree(fan_handle); + fan_handle = NULL; + } + if (hsf_handle) { + device_remove_file(&pd->dev, hsf_handle); + kfree(hsf_handle); + hsf_handle = NULL; + } +} + /* USB charge function */ static struct device_attribute *uc_handle; -- cgit v1.2.3-70-g09d2 From 168de1add461184df0ec27258518be94f277b023 Mon Sep 17 00:00:00 2001 From: Javier Achirica Date: Fri, 21 Mar 2014 08:01:17 +0900 Subject: sony-laptop: add hibernate on low battery function Signed-off-by: Javier Achirica Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 78 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 7b5a56d59bac..ba39a29a9f39 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -164,6 +164,9 @@ static int __sony_nc_gfx_switch_status_get(void); static int sony_nc_highspeed_charging_setup(struct platform_device *pd); static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd); +static int sony_nc_lowbatt_setup(struct platform_device *pd); +static void sony_nc_lowbatt_cleanup(struct platform_device *pd); + static int sony_nc_fanspeed_setup(struct platform_device *pd); static void sony_nc_fanspeed_cleanup(struct platform_device *pd); @@ -1394,6 +1397,12 @@ static void sony_nc_function_setup(struct acpi_device *device, pr_err("couldn't set up keyboard backlight function (%d)\n", result); break; + case 0x0121: + result = sony_nc_lowbatt_setup(pf_device); + if (result) + pr_err("couldn't set up low battery function (%d)\n", + result); + break; case 0x0149: result = sony_nc_fanspeed_setup(pf_device); if (result) @@ -1476,6 +1485,9 @@ static void sony_nc_function_cleanup(struct platform_device *pd) case 0x0163: sony_nc_kbd_backlight_cleanup(pd, handle); break; + case 0x0121: + sony_nc_lowbatt_cleanup(pd); + break; case 0x0149: sony_nc_fanspeed_cleanup(pd); break; @@ -2576,6 +2588,72 @@ static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd) } } +/* low battery function */ +static struct device_attribute *lowbatt_handle; + +static ssize_t sony_nc_lowbatt_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + unsigned int result; + unsigned long value; + + if (count > 31) + return -EINVAL; + + if (kstrtoul(buffer, 10, &value) || value > 1) + return -EINVAL; + + if (sony_call_snc_handle(0x0121, value << 8, &result)) + return -EIO; + + return count; +} + +static ssize_t sony_nc_lowbatt_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + unsigned int result; + + if (sony_call_snc_handle(0x0121, 0x0200, &result)) + return -EIO; + + return snprintf(buffer, PAGE_SIZE, "%d\n", result & 1); +} + +static int sony_nc_lowbatt_setup(struct platform_device *pd) +{ + unsigned int result; + + lowbatt_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); + if (!lowbatt_handle) + return -ENOMEM; + + sysfs_attr_init(&lowbatt_handle->attr); + lowbatt_handle->attr.name = "lowbatt_hibernate"; + lowbatt_handle->attr.mode = S_IRUGO | S_IWUSR; + lowbatt_handle->show = sony_nc_lowbatt_show; + lowbatt_handle->store = sony_nc_lowbatt_store; + + result = device_create_file(&pd->dev, lowbatt_handle); + if (result) { + kfree(lowbatt_handle); + lowbatt_handle = NULL; + return result; + } + + return 0; +} + +static void sony_nc_lowbatt_cleanup(struct platform_device *pd) +{ + if (lowbatt_handle) { + device_remove_file(&pd->dev, lowbatt_handle); + kfree(lowbatt_handle); + lowbatt_handle = NULL; + } +} + /* fan speed function */ static struct device_attribute *fan_handle, *hsf_handle; -- cgit v1.2.3-70-g09d2 From 0380d4711e2a2190d56bf04ecdc3d6dd2621efd7 Mon Sep 17 00:00:00 2001 From: Javier Achirica Date: Fri, 21 Mar 2014 08:01:18 +0900 Subject: sony-laptop: adjust keyboard backlight values for off/auto/on Keyboard backlight can be always off, use some automatic trigger (activity and light sensor), always on. The behaviour of the driver changes whereby previously when passed 1 it tried to turn on backlight immediately now it does nothing. This is however a bug fix since (a) it makes little sense to turn on the backlight when control is automatic and (b) this behaviour is consistent with what the windows driver does. Signed-off-by: Javier Achirica Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index ba39a29a9f39..48e7e5bdadbf 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -129,7 +129,8 @@ static int kbd_backlight = -1; module_param(kbd_backlight, int, 0444); MODULE_PARM_DESC(kbd_backlight, "set this to 0 to disable keyboard backlight, " - "1 to enable it (default: no change from current value)"); + "1 to enable it with automatic control and 2 to have it always " + "on (default: no change from current value)"); static int kbd_backlight_timeout = -1; module_param(kbd_backlight_timeout, int, 0444); @@ -1772,7 +1773,7 @@ static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value) { int result; - if (value > 1) + if (value > 2) return -EINVAL; if (sony_call_snc_handle(kbdbl_ctl->handle, @@ -1780,8 +1781,10 @@ static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value) return -EIO; /* Try to turn the light on/off immediately */ - sony_call_snc_handle(kbdbl_ctl->handle, - (value << 0x10) | (kbdbl_ctl->base + 0x100), &result); + if (value != 1) + sony_call_snc_handle(kbdbl_ctl->handle, + (value << 0x0f) | (kbdbl_ctl->base + 0x100), + &result); kbdbl_ctl->mode = value; -- cgit v1.2.3-70-g09d2 From d58dc780c458d429afa805578c743af4f107139a Mon Sep 17 00:00:00 2001 From: Javier Achirica Date: Fri, 21 Mar 2014 08:01:19 +0900 Subject: sony-laptop: add smart connect control function The current value is not available through the SNC device and therefore the attribute is writable only. Signed-off-by: Javier Achirica Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 67 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 48e7e5bdadbf..19d769e0d774 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -177,6 +177,9 @@ static void sony_nc_usb_charge_cleanup(struct platform_device *pd); static int sony_nc_panelid_setup(struct platform_device *pd); static void sony_nc_panelid_cleanup(struct platform_device *pd); +static int sony_nc_smart_conn_setup(struct platform_device *pd); +static void sony_nc_smart_conn_cleanup(struct platform_device *pd); + static int sony_nc_touchpad_setup(struct platform_device *pd, unsigned int handle); static void sony_nc_touchpad_cleanup(struct platform_device *pd); @@ -1422,6 +1425,12 @@ static void sony_nc_function_setup(struct acpi_device *device, pr_err("couldn't set up panel ID function (%d)\n", result); break; + case 0x0168: + result = sony_nc_smart_conn_setup(pf_device); + if (result) + pr_err("couldn't set up smart connect support (%d)\n", + result); + break; default: continue; } @@ -1498,6 +1507,9 @@ static void sony_nc_function_cleanup(struct platform_device *pd) case 0x011D: sony_nc_panelid_cleanup(pd); break; + case 0x0168: + sony_nc_smart_conn_cleanup(pd); + break; default: continue; } @@ -2885,6 +2897,61 @@ static void sony_nc_panelid_cleanup(struct platform_device *pd) } } +/* smart connect function */ +static struct device_attribute *sc_handle; + +static ssize_t sony_nc_smart_conn_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + unsigned int result; + unsigned long value; + + if (count > 31) + return -EINVAL; + + if (kstrtoul(buffer, 10, &value) || value > 1) + return -EINVAL; + + if (sony_call_snc_handle(0x0168, value << 0x10, &result)) + return -EIO; + + return count; +} + +static int sony_nc_smart_conn_setup(struct platform_device *pd) +{ + unsigned int result; + + sc_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); + if (!sc_handle) + return -ENOMEM; + + sysfs_attr_init(&sc_handle->attr); + sc_handle->attr.name = "smart_connect"; + sc_handle->attr.mode = S_IWUSR; + sc_handle->show = NULL; + sc_handle->store = sony_nc_smart_conn_store; + + result = device_create_file(&pd->dev, sc_handle); + if (result) { + kfree(sc_handle); + sc_handle = NULL; + return result; + } + + return 0; +} + +static void sony_nc_smart_conn_cleanup(struct platform_device *pd) +{ + if (sc_handle) { + device_remove_file(&pd->dev, sc_handle); + kfree(sc_handle); + sc_handle = NULL; + } +} + /* Touchpad enable/disable */ struct touchpad_control { struct device_attribute attr; -- cgit v1.2.3-70-g09d2 From e22510eadd65e73709709c81dc2ab0a6ded97c90 Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Fri, 21 Mar 2014 08:01:22 +0900 Subject: sony-laptop: remove useless sony-laptop versioning Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 19d769e0d774..9c5a07417b2b 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -76,8 +76,6 @@ do { \ pr_warn(fmt, ##__VA_ARGS__); \ } while (0) -#define SONY_LAPTOP_DRIVER_VERSION "0.6" - #define SONY_NC_CLASS "sony-nc" #define SONY_NC_HID "SNY5001" #define SONY_NC_DRIVER_NAME "Sony Notebook Control Driver" @@ -89,7 +87,6 @@ do { \ MODULE_AUTHOR("Stelian Pop, Mattia Dongili"); MODULE_DESCRIPTION("Sony laptop extras driver (SPIC and SNC ACPI device)"); MODULE_LICENSE("GPL"); -MODULE_VERSION(SONY_LAPTOP_DRIVER_VERSION); static int debug; module_param(debug, int, 0); @@ -3154,8 +3151,6 @@ static int sony_nc_add(struct acpi_device *device) int result = 0; struct sony_nc_value *item; - pr_info("%s v%s\n", SONY_NC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION); - sony_nc_acpi_device = device; strcpy(acpi_device_class(device), "sony/hotkey"); @@ -3249,6 +3244,7 @@ static int sony_nc_add(struct acpi_device *device) } } + pr_info("SNC setup done.\n"); return 0; out_sysfs: @@ -4687,8 +4683,6 @@ static int sony_pic_add(struct acpi_device *device) struct sony_pic_ioport *io, *tmp_io; struct sony_pic_irq *irq, *tmp_irq; - pr_info("%s v%s\n", SONY_PIC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION); - spic_dev.acpi_dev = device; strcpy(acpi_device_class(device), "sony/hotkey"); sony_pic_detect_device_type(&spic_dev); @@ -4788,6 +4782,7 @@ static int sony_pic_add(struct acpi_device *device) if (result) goto err_remove_pf; + pr_info("SPIC setup done.\n"); return 0; err_remove_pf: -- cgit v1.2.3-70-g09d2 From c900f291f25ad86270efa36630ee7f128d08096a Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Mon, 3 Mar 2014 21:41:43 -0800 Subject: Revert "X86 platform: New BayTrail IOSF-SB MBI driver" This reverts commit 997ab407d2b4e7d7ce2788d2de68435eb94fcfec. This driver is replaced by the more general SOC IOSF driver in commit 46184415368a. Signed-off-by: David E. Box Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 8 -- drivers/platform/x86/Makefile | 1 - drivers/platform/x86/intel_baytrail.c | 224 ---------------------------------- drivers/platform/x86/intel_baytrail.h | 90 -------------- 4 files changed, 323 deletions(-) delete mode 100644 drivers/platform/x86/intel_baytrail.c delete mode 100644 drivers/platform/x86/intel_baytrail.h diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 5f67843c7fb7..826ed3af77cb 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -817,12 +817,4 @@ config PVPANIC a paravirtualized device provided by QEMU; it lets a virtual machine (guest) communicate panic events to the host. -config INTEL_BAYTRAIL_MBI - tristate - depends on PCI - ---help--- - Needed on Baytrail platforms for access to the IOSF Sideband Mailbox - Interface. This is a requirement for systems that need to configure - the PUNIT for power management features such as RAPL. - endif # X86_PLATFORM_DEVICES diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 9b87cfc42b84..b8c36f785312 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -55,4 +55,3 @@ obj-$(CONFIG_INTEL_RST) += intel-rst.o obj-$(CONFIG_INTEL_SMARTCONNECT) += intel-smartconnect.o obj-$(CONFIG_PVPANIC) += pvpanic.o -obj-$(CONFIG_INTEL_BAYTRAIL_MBI) += intel_baytrail.o diff --git a/drivers/platform/x86/intel_baytrail.c b/drivers/platform/x86/intel_baytrail.c deleted file mode 100644 index f96626b17260..000000000000 --- a/drivers/platform/x86/intel_baytrail.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Baytrail IOSF-SB MailBox Interface Driver - * Copyright (c) 2013, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * - * The IOSF-SB is a fabric bus available on Atom based SOC's that uses a - * mailbox interface (MBI) to communicate with mutiple devices. This - * driver implements BayTrail-specific access to this interface. - */ - -#include -#include -#include -#include - -#include "intel_baytrail.h" - -static DEFINE_SPINLOCK(iosf_mbi_lock); - -static inline u32 iosf_mbi_form_mcr(u8 op, u8 port, u8 offset) -{ - return (op << 24) | (port << 16) | (offset << 8) | BT_MBI_ENABLE; -} - -static struct pci_dev *mbi_pdev; /* one mbi device */ - -/* Hold lock before calling */ -static int iosf_mbi_pci_read_mdr(u32 mcrx, u32 mcr, u32 *mdr) -{ - int result; - - if (!mbi_pdev) - return -ENODEV; - - if (mcrx) { - result = pci_write_config_dword(mbi_pdev, - BT_MBI_MCRX_OFFSET, mcrx); - if (result < 0) - goto iosf_mbi_read_err; - } - - result = pci_write_config_dword(mbi_pdev, - BT_MBI_MCR_OFFSET, mcr); - if (result < 0) - goto iosf_mbi_read_err; - - result = pci_read_config_dword(mbi_pdev, - BT_MBI_MDR_OFFSET, mdr); - if (result < 0) - goto iosf_mbi_read_err; - - return 0; - -iosf_mbi_read_err: - dev_err(&mbi_pdev->dev, "error: PCI config operation returned %d\n", - result); - return result; -} - -/* Hold lock before calling */ -static int iosf_mbi_pci_write_mdr(u32 mcrx, u32 mcr, u32 mdr) -{ - int result; - - if (!mbi_pdev) - return -ENODEV; - - result = pci_write_config_dword(mbi_pdev, - BT_MBI_MDR_OFFSET, mdr); - if (result < 0) - goto iosf_mbi_write_err; - - if (mcrx) { - result = pci_write_config_dword(mbi_pdev, - BT_MBI_MCRX_OFFSET, mcrx); - if (result < 0) - goto iosf_mbi_write_err; - } - - result = pci_write_config_dword(mbi_pdev, - BT_MBI_MCR_OFFSET, mcr); - if (result < 0) - goto iosf_mbi_write_err; - - return 0; - -iosf_mbi_write_err: - dev_err(&mbi_pdev->dev, "error: PCI config operation returned %d\n", - result); - return result; -} - -int bt_mbi_read(u8 port, u8 opcode, u32 offset, u32 *mdr) -{ - u32 mcr, mcrx; - unsigned long flags; - int ret; - - /*Access to the GFX unit is handled by GPU code */ - BUG_ON(port == BT_MBI_UNIT_GFX); - - mcr = iosf_mbi_form_mcr(opcode, port, offset & BT_MBI_MASK_LO); - mcrx = offset & BT_MBI_MASK_HI; - - spin_lock_irqsave(&iosf_mbi_lock, flags); - ret = iosf_mbi_pci_read_mdr(mcrx, mcr, mdr); - spin_unlock_irqrestore(&iosf_mbi_lock, flags); - - return ret; -} -EXPORT_SYMBOL(bt_mbi_read); - -int bt_mbi_write(u8 port, u8 opcode, u32 offset, u32 mdr) -{ - u32 mcr, mcrx; - unsigned long flags; - int ret; - - /*Access to the GFX unit is handled by GPU code */ - BUG_ON(port == BT_MBI_UNIT_GFX); - - mcr = iosf_mbi_form_mcr(opcode, port, offset & BT_MBI_MASK_LO); - mcrx = offset & BT_MBI_MASK_HI; - - spin_lock_irqsave(&iosf_mbi_lock, flags); - ret = iosf_mbi_pci_write_mdr(mcrx, mcr, mdr); - spin_unlock_irqrestore(&iosf_mbi_lock, flags); - - return ret; -} -EXPORT_SYMBOL(bt_mbi_write); - -int bt_mbi_modify(u8 port, u8 opcode, u32 offset, u32 mdr, u32 mask) -{ - u32 mcr, mcrx; - u32 value; - unsigned long flags; - int ret; - - /*Access to the GFX unit is handled by GPU code */ - BUG_ON(port == BT_MBI_UNIT_GFX); - - mcr = iosf_mbi_form_mcr(opcode, port, offset & BT_MBI_MASK_LO); - mcrx = offset & BT_MBI_MASK_HI; - - spin_lock_irqsave(&iosf_mbi_lock, flags); - - /* Read current mdr value */ - ret = iosf_mbi_pci_read_mdr(mcrx, mcr & BT_MBI_RD_MASK, &value); - if (ret < 0) { - spin_unlock_irqrestore(&iosf_mbi_lock, flags); - return ret; - } - - /* Apply mask */ - value &= ~mask; - mdr &= mask; - value |= mdr; - - /* Write back */ - ret = iosf_mbi_pci_write_mdr(mcrx, mcr | BT_MBI_WR_MASK, value); - - spin_unlock_irqrestore(&iosf_mbi_lock, flags); - - return ret; -} -EXPORT_SYMBOL(bt_mbi_modify); - -static int iosf_mbi_probe(struct pci_dev *pdev, - const struct pci_device_id *unused) -{ - int ret; - - ret = pci_enable_device(pdev); - if (ret < 0) { - dev_err(&pdev->dev, "error: could not enable device\n"); - return ret; - } - - mbi_pdev = pci_dev_get(pdev); - return 0; -} - -static DEFINE_PCI_DEVICE_TABLE(iosf_mbi_pci_ids) = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0F00) }, - { 0, }, -}; -MODULE_DEVICE_TABLE(pci, iosf_mbi_pci_ids); - -static struct pci_driver iosf_mbi_pci_driver = { - .name = "iosf_mbi_pci", - .probe = iosf_mbi_probe, - .id_table = iosf_mbi_pci_ids, -}; - -static int __init bt_mbi_init(void) -{ - return pci_register_driver(&iosf_mbi_pci_driver); -} - -static void __exit bt_mbi_exit(void) -{ - pci_unregister_driver(&iosf_mbi_pci_driver); - if (mbi_pdev) { - pci_dev_put(mbi_pdev); - mbi_pdev = NULL; - } -} - -module_init(bt_mbi_init); -module_exit(bt_mbi_exit); - -MODULE_AUTHOR("David E. Box "); -MODULE_DESCRIPTION("BayTrail Mailbox Interface accessor"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel_baytrail.h b/drivers/platform/x86/intel_baytrail.h deleted file mode 100644 index 8bcc311262e9..000000000000 --- a/drivers/platform/x86/intel_baytrail.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * intel_baytrail.h: MailBox access support for Intel BayTrail platforms - */ - -#ifndef INTEL_BAYTRAIL_MBI_SYMS_H -#define INTEL_BAYTRAIL_MBI_SYMS_H - -#define BT_MBI_MCR_OFFSET 0xD0 -#define BT_MBI_MDR_OFFSET 0xD4 -#define BT_MBI_MCRX_OFFSET 0xD8 - -#define BT_MBI_RD_MASK 0xFEFFFFFF -#define BT_MBI_WR_MASK 0X01000000 - -#define BT_MBI_MASK_HI 0xFFFFFF00 -#define BT_MBI_MASK_LO 0x000000FF -#define BT_MBI_ENABLE 0xF0 - -/* BT-SB unit access methods */ -#define BT_MBI_UNIT_AUNIT 0x00 -#define BT_MBI_UNIT_SMC 0x01 -#define BT_MBI_UNIT_CPU 0x02 -#define BT_MBI_UNIT_BUNIT 0x03 -#define BT_MBI_UNIT_PMC 0x04 -#define BT_MBI_UNIT_GFX 0x06 -#define BT_MBI_UNIT_SMI 0x0C -#define BT_MBI_UNIT_USB 0x43 -#define BT_MBI_UNIT_SATA 0xA3 -#define BT_MBI_UNIT_PCIE 0xA6 - -/* Read/write opcodes */ -#define BT_MBI_AUNIT_READ 0x10 -#define BT_MBI_AUNIT_WRITE 0x11 -#define BT_MBI_SMC_READ 0x10 -#define BT_MBI_SMC_WRITE 0x11 -#define BT_MBI_CPU_READ 0x10 -#define BT_MBI_CPU_WRITE 0x11 -#define BT_MBI_BUNIT_READ 0x10 -#define BT_MBI_BUNIT_WRITE 0x11 -#define BT_MBI_PMC_READ 0x06 -#define BT_MBI_PMC_WRITE 0x07 -#define BT_MBI_GFX_READ 0x00 -#define BT_MBI_GFX_WRITE 0x01 -#define BT_MBI_SMIO_READ 0x06 -#define BT_MBI_SMIO_WRITE 0x07 -#define BT_MBI_USB_READ 0x06 -#define BT_MBI_USB_WRITE 0x07 -#define BT_MBI_SATA_READ 0x00 -#define BT_MBI_SATA_WRITE 0x01 -#define BT_MBI_PCIE_READ 0x00 -#define BT_MBI_PCIE_WRITE 0x01 - -/** - * bt_mbi_read() - MailBox Interface read command - * @port: port indicating subunit being accessed - * @opcode: port specific read or write opcode - * @offset: register address offset - * @mdr: register data to be read - * - * Locking is handled by spinlock - cannot sleep. - * Return: Nonzero on error - */ -int bt_mbi_read(u8 port, u8 opcode, u32 offset, u32 *mdr); - -/** - * bt_mbi_write() - MailBox unmasked write command - * @port: port indicating subunit being accessed - * @opcode: port specific read or write opcode - * @offset: register address offset - * @mdr: register data to be written - * - * Locking is handled by spinlock - cannot sleep. - * Return: Nonzero on error - */ -int bt_mbi_write(u8 port, u8 opcode, u32 offset, u32 mdr); - -/** - * bt_mbi_modify() - MailBox masked write command - * @port: port indicating subunit being accessed - * @opcode: port specific read or write opcode - * @offset: register address offset - * @mdr: register data being modified - * @mask: mask indicating bits in mdr to be modified - * - * Locking is handled by spinlock - cannot sleep. - * Return: Nonzero on error - */ -int bt_mbi_modify(u8 port, u8 opcode, u32 offset, u32 mdr, u32 mask); - -#endif /* INTEL_BAYTRAIL_MBI_SYMS_H */ -- cgit v1.2.3-70-g09d2 From 57dcf020f406e430050ed4153c03ce904ede07bc Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 31 Mar 2014 15:15:36 +0200 Subject: sonypi: Simplify dependencies X86 && !64BIT is better expressed as X86_32. Signed-off-by: Jean Delvare Cc: Arnd Bergmann Cc: Greg Kroah-Hartman Cc: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/char/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 1386749b48ff..fbae63e3d304 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -408,7 +408,7 @@ config APPLICOM config SONYPI tristate "Sony Vaio Programmable I/O Control Device support" - depends on X86 && PCI && INPUT && !64BIT + depends on X86_32 && PCI && INPUT ---help--- This driver enables access to the Sony Programmable I/O Control Device which can be found in many (all ?) Sony Vaio laptops. -- cgit v1.2.3-70-g09d2 From 119f449866ad18785b0445adaf0d2859c6dbdaa3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Feb 2014 16:32:45 +0100 Subject: thinkpad_acpi: Fix inconsistent mute LED after resume The mute LED states have to be restored after resume. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=70351 Cc: [v3.13+] Signed-off-by: Takashi Iwai Signed-off-by: Matthew Garrett --- drivers/platform/x86/thinkpad_acpi.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 94bb6157c957..dcc833a0b2fd 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -8447,9 +8447,21 @@ static void mute_led_exit(void) tpacpi_led_set(i, false); } +static void mute_led_resume(void) +{ + int i; + + for (i = 0; i < TPACPI_LED_MAX; i++) { + struct tp_led_table *t = &led_tables[i]; + if (t->state >= 0) + mute_led_on_off(t, t->state); + } +} + static struct ibm_struct mute_led_driver_data = { .name = "mute_led", .exit = mute_led_exit, + .resume = mute_led_resume, }; /**************************************************************************** -- cgit v1.2.3-70-g09d2 From 84a6273f04fd19cad189c8327d0c3c17a053ab8b Mon Sep 17 00:00:00 2001 From: Azael Avalos Date: Tue, 25 Mar 2014 20:38:29 -0600 Subject: toshiba_acpi: Add System Configuration Interface SCI stands for System Configuration Interface, which aim is to conceal differences in hardware between different models. This patch introduces four new calls: sci_open, sci_close, sci_read and sci_write, along with its definitions and return codes which will be used by later patches. More information about the SCI can be found at Jonathan Buzzard's website [1]. [1] http://www.buzzard.me.uk/toshiba/docs.html Signed-off-by: Azael Avalos Signed-off-by: Matthew Garrett --- drivers/platform/x86/toshiba_acpi.c | 79 +++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 90dd7645a9e5..c4680037bbfd 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -77,6 +77,9 @@ MODULE_LICENSE("GPL"); * However the ACPI methods seem to be incomplete in some areas (for * example they allow setting, but not reading, the LCD brightness value), * so this is still useful. + * + * SCI stands for "System Configuration Interface" which aim is to + * conceal differences in hardware between different models. */ #define HCI_WORDS 6 @@ -84,12 +87,20 @@ MODULE_LICENSE("GPL"); /* operations */ #define HCI_SET 0xff00 #define HCI_GET 0xfe00 +#define SCI_OPEN 0xf100 +#define SCI_CLOSE 0xf200 +#define SCI_GET 0xf300 +#define SCI_SET 0xf400 /* return codes */ #define HCI_SUCCESS 0x0000 #define HCI_FAILURE 0x1000 #define HCI_NOT_SUPPORTED 0x8000 #define HCI_EMPTY 0x8c00 +#define SCI_OPEN_CLOSE_OK 0x0044 +#define SCI_ALREADY_OPEN 0x8100 +#define SCI_NOT_OPENED 0x8200 +#define SCI_NOT_PRESENT 0x8600 /* registers */ #define HCI_FAN 0x0004 @@ -280,6 +291,74 @@ static acpi_status hci_read2(struct toshiba_acpi_dev *dev, u32 reg, return status; } +/* common sci tasks + */ + +static int sci_open(struct toshiba_acpi_dev *dev) +{ + u32 in[HCI_WORDS] = { SCI_OPEN, 0, 0, 0, 0, 0 }; + u32 out[HCI_WORDS]; + acpi_status status; + + status = hci_raw(dev, in, out); + if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) { + pr_err("ACPI call to open SCI failed\n"); + return 0; + } + + if (out[0] == SCI_OPEN_CLOSE_OK) { + return 1; + } else if (out[0] == SCI_ALREADY_OPEN) { + pr_info("Toshiba SCI already opened\n"); + return 1; + } else if (out[0] == SCI_NOT_PRESENT) { + pr_info("Toshiba SCI is not present\n"); + } + + return 0; +} + +static void sci_close(struct toshiba_acpi_dev *dev) +{ + u32 in[HCI_WORDS] = { SCI_CLOSE, 0, 0, 0, 0, 0 }; + u32 out[HCI_WORDS]; + acpi_status status; + + status = hci_raw(dev, in, out); + if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) { + pr_err("ACPI call to close SCI failed\n"); + return; + } + + if (out[0] == SCI_OPEN_CLOSE_OK) + return; + else if (out[0] == SCI_NOT_OPENED) + pr_info("Toshiba SCI not opened\n"); + else if (out[0] == SCI_NOT_PRESENT) + pr_info("Toshiba SCI is not present\n"); +} + +static acpi_status sci_read(struct toshiba_acpi_dev *dev, u32 reg, + u32 *out1, u32 *result) +{ + u32 in[HCI_WORDS] = { SCI_GET, reg, 0, 0, 0, 0 }; + u32 out[HCI_WORDS]; + acpi_status status = hci_raw(dev, in, out); + *out1 = out[2]; + *result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE; + return status; +} + +static acpi_status sci_write(struct toshiba_acpi_dev *dev, u32 reg, + u32 in1, u32 *result) +{ + u32 in[HCI_WORDS] = { SCI_SET, reg, in1, 0, 0, 0 }; + u32 out[HCI_WORDS]; + acpi_status status = hci_raw(dev, in, out); + *result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE; + return status; +} + /* Illumination support */ static int toshiba_illumination_available(struct toshiba_acpi_dev *dev) { -- cgit v1.2.3-70-g09d2 From fdb79081fec4a57e124b34f983aac566e210220f Mon Sep 17 00:00:00 2001 From: Azael Avalos Date: Tue, 25 Mar 2014 20:38:30 -0600 Subject: toshiba_acpi: Adapt Illumination code to use SCI Change the toshiba_illumination_* code to use the newly introduced SCI functions, making the code more robust in detecting Illumination capabilities properly, since it was only opening the SCI and the return value was never checked for errors or actual Illumination support. Signed-off-by: Azael Avalos Signed-off-by: Matthew Garrett --- drivers/platform/x86/toshiba_acpi.c | 94 ++++++++++++++----------------------- 1 file changed, 34 insertions(+), 60 deletions(-) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index c4680037bbfd..6ed5be030d58 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -100,6 +100,7 @@ MODULE_LICENSE("GPL"); #define SCI_OPEN_CLOSE_OK 0x0044 #define SCI_ALREADY_OPEN 0x8100 #define SCI_NOT_OPENED 0x8200 +#define SCI_INPUT_DATA_ERROR 0x8300 #define SCI_NOT_PRESENT 0x8600 /* registers */ @@ -110,6 +111,7 @@ MODULE_LICENSE("GPL"); #define HCI_HOTKEY_EVENT 0x001e #define HCI_LCD_BRIGHTNESS 0x002a #define HCI_WIRELESS 0x0056 +#define SCI_ILLUMINATION 0x014e /* field definitions */ #define HCI_HOTKEY_DISABLE 0x0b @@ -362,18 +364,23 @@ static acpi_status sci_write(struct toshiba_acpi_dev *dev, u32 reg, /* Illumination support */ static int toshiba_illumination_available(struct toshiba_acpi_dev *dev) { - u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; + u32 in[HCI_WORDS] = { SCI_GET, SCI_ILLUMINATION, 0, 0, 0, 0 }; u32 out[HCI_WORDS]; acpi_status status; - in[0] = 0xf100; + if (!sci_open(dev)) + return 0; + status = hci_raw(dev, in, out); - if (ACPI_FAILURE(status)) { + sci_close(dev); + if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) { + pr_err("ACPI call to query Illumination support failed\n"); + return 0; + } else if (out[0] == HCI_NOT_SUPPORTED || out[1] != 1) { pr_info("Illumination device not available\n"); return 0; } - in[0] = 0xf400; - status = hci_raw(dev, in, out); + return 1; } @@ -382,82 +389,49 @@ static void toshiba_illumination_set(struct led_classdev *cdev, { struct toshiba_acpi_dev *dev = container_of(cdev, struct toshiba_acpi_dev, led_dev); - u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; - u32 out[HCI_WORDS]; + u32 state, result; acpi_status status; /* First request : initialize communication. */ - in[0] = 0xf100; - status = hci_raw(dev, in, out); - if (ACPI_FAILURE(status)) { - pr_info("Illumination device not available\n"); + if (!sci_open(dev)) return; - } - if (brightness) { - /* Switch the illumination on */ - in[0] = 0xf400; - in[1] = 0x14e; - in[2] = 1; - status = hci_raw(dev, in, out); - if (ACPI_FAILURE(status)) { - pr_info("ACPI call for illumination failed\n"); - return; - } - } else { - /* Switch the illumination off */ - in[0] = 0xf400; - in[1] = 0x14e; - in[2] = 0; - status = hci_raw(dev, in, out); - if (ACPI_FAILURE(status)) { - pr_info("ACPI call for illumination failed.\n"); - return; - } + /* Switch the illumination on/off */ + state = brightness ? 1 : 0; + status = sci_write(dev, SCI_ILLUMINATION, state, &result); + sci_close(dev); + if (ACPI_FAILURE(status)) { + pr_err("ACPI call for illumination failed\n"); + return; + } else if (result == HCI_NOT_SUPPORTED) { + pr_info("Illumination not supported\n"); + return; } - - /* Last request : close communication. */ - in[0] = 0xf200; - in[1] = 0; - in[2] = 0; - hci_raw(dev, in, out); } static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) { struct toshiba_acpi_dev *dev = container_of(cdev, struct toshiba_acpi_dev, led_dev); - u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; - u32 out[HCI_WORDS]; + u32 state, result; acpi_status status; - enum led_brightness result; /* First request : initialize communication. */ - in[0] = 0xf100; - status = hci_raw(dev, in, out); - if (ACPI_FAILURE(status)) { - pr_info("Illumination device not available\n"); + if (!sci_open(dev)) return LED_OFF; - } /* Check the illumination */ - in[0] = 0xf300; - in[1] = 0x14e; - status = hci_raw(dev, in, out); - if (ACPI_FAILURE(status)) { - pr_info("ACPI call for illumination failed.\n"); + status = sci_read(dev, SCI_ILLUMINATION, &state, &result); + sci_close(dev); + if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { + pr_err("ACPI call for illumination failed\n"); + return LED_OFF; + } else if (result == HCI_NOT_SUPPORTED) { + pr_info("Illumination not supported\n"); return LED_OFF; } - result = out[2] ? LED_FULL : LED_OFF; - - /* Last request : close communication. */ - in[0] = 0xf200; - in[1] = 0; - in[2] = 0; - hci_raw(dev, in, out); - - return result; + return state ? LED_FULL : LED_OFF; } /* Bluetooth rfkill handlers */ -- cgit v1.2.3-70-g09d2 From 360f0f39d0c58432574655008ec8dd15e52e1e8d Mon Sep 17 00:00:00 2001 From: Azael Avalos Date: Tue, 25 Mar 2014 20:38:31 -0600 Subject: toshiba_acpi: Add keyboard backlight support Toshiba laptops equiped with an illuminated keyboard can operate in two different modes: Auto and FN-Z. The Auto mode turns on the led on keystrokes and automatically turns it off after some (configurable) time the last key was pressed. The FN-Z mode is used to toggle the keyboard led on/off by userspace. This patch adds support to set the desired KBD mode and timeout via sysfs, creates and registers toshiba::kbd_backlight led device whenever the mode is set to FN-Z. The acceptable values for mode are: 1 (Auto) and 2 (Fn-Z) The time values range are: 1-60 seconds Signed-off-by: Azael Avalos Signed-off-by: Matthew Garrett --- drivers/platform/x86/toshiba_acpi.c | 240 ++++++++++++++++++++++++++++++++++++ 1 file changed, 240 insertions(+) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 6ed5be030d58..95ae1ffaf409 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -111,7 +111,9 @@ MODULE_LICENSE("GPL"); #define HCI_HOTKEY_EVENT 0x001e #define HCI_LCD_BRIGHTNESS 0x002a #define HCI_WIRELESS 0x0056 +#define HCI_KBD_ILLUMINATION 0x0095 #define SCI_ILLUMINATION 0x014e +#define SCI_KBD_ILLUM_STATUS 0x015c /* field definitions */ #define HCI_HOTKEY_DISABLE 0x0b @@ -119,6 +121,7 @@ MODULE_LICENSE("GPL"); #define HCI_LCD_BRIGHTNESS_BITS 3 #define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS) #define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS) +#define HCI_MISC_SHIFT 0x10 #define HCI_VIDEO_OUT_LCD 0x1 #define HCI_VIDEO_OUT_CRT 0x2 #define HCI_VIDEO_OUT_TV 0x4 @@ -126,6 +129,8 @@ MODULE_LICENSE("GPL"); #define HCI_WIRELESS_BT_PRESENT 0x0f #define HCI_WIRELESS_BT_ATTACH 0x40 #define HCI_WIRELESS_BT_POWER 0x80 +#define SCI_KBD_MODE_FNZ 0x1 +#define SCI_KBD_MODE_AUTO 0x2 struct toshiba_acpi_dev { struct acpi_device *acpi_dev; @@ -135,10 +140,13 @@ struct toshiba_acpi_dev { struct work_struct hotkey_work; struct backlight_device *backlight_dev; struct led_classdev led_dev; + struct led_classdev kbd_led; int force_fan; int last_key_event; int key_event_valid; + int kbd_mode; + int kbd_time; unsigned int illumination_supported:1; unsigned int video_supported:1; @@ -147,6 +155,9 @@ struct toshiba_acpi_dev { unsigned int ntfy_supported:1; unsigned int info_supported:1; unsigned int tr_backlight_supported:1; + unsigned int kbd_illum_supported:1; + unsigned int kbd_led_registered:1; + unsigned int sysfs_created:1; struct mutex mutex; }; @@ -433,6 +444,89 @@ static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) return state ? LED_FULL : LED_OFF; } + +/* KBD Illumination */ +static int toshiba_kbd_illum_status_set(struct toshiba_acpi_dev *dev, u32 time) +{ + u32 result; + acpi_status status; + + if (!sci_open(dev)) + return -EIO; + + status = sci_write(dev, SCI_KBD_ILLUM_STATUS, time, &result); + sci_close(dev); + if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { + pr_err("ACPI call to set KBD backlight status failed\n"); + return -EIO; + } else if (result == HCI_NOT_SUPPORTED) { + pr_info("Keyboard backlight status not supported\n"); + return -ENODEV; + } + + return 0; +} + +static int toshiba_kbd_illum_status_get(struct toshiba_acpi_dev *dev, u32 *time) +{ + u32 result; + acpi_status status; + + if (!sci_open(dev)) + return -EIO; + + status = sci_read(dev, SCI_KBD_ILLUM_STATUS, time, &result); + sci_close(dev); + if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { + pr_err("ACPI call to get KBD backlight status failed\n"); + return -EIO; + } else if (result == HCI_NOT_SUPPORTED) { + pr_info("Keyboard backlight status not supported\n"); + return -ENODEV; + } + + return 0; +} + +static enum led_brightness toshiba_kbd_backlight_get(struct led_classdev *cdev) +{ + struct toshiba_acpi_dev *dev = container_of(cdev, + struct toshiba_acpi_dev, kbd_led); + u32 state, result; + acpi_status status; + + /* Check the keyboard backlight state */ + status = hci_read1(dev, HCI_KBD_ILLUMINATION, &state, &result); + if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { + pr_err("ACPI call to get the keyboard backlight failed\n"); + return LED_OFF; + } else if (result == HCI_NOT_SUPPORTED) { + pr_info("Keyboard backlight not supported\n"); + return LED_OFF; + } + + return state ? LED_FULL : LED_OFF; +} + +static void toshiba_kbd_backlight_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct toshiba_acpi_dev *dev = container_of(cdev, + struct toshiba_acpi_dev, kbd_led); + u32 state, result; + acpi_status status; + + /* Set the keyboard backlight state */ + state = brightness ? 1 : 0; + status = hci_write1(dev, HCI_KBD_ILLUMINATION, state, &result); + if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { + pr_err("ACPI call to set KBD Illumination mode failed\n"); + return; + } else if (result == HCI_NOT_SUPPORTED) { + pr_info("Keyboard backlight not supported\n"); + return; + } +} /* Bluetooth rfkill handlers */ @@ -956,6 +1050,117 @@ static const struct backlight_ops toshiba_backlight_data = { .get_brightness = get_lcd_brightness, .update_status = set_lcd_status, }; + +/* + * Sysfs files + */ + +static ssize_t toshiba_kbd_bl_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); + int mode = -1; + int time = -1; + + if (sscanf(buf, "%i", &mode) != 1 && (mode != 2 || mode != 1)) + return -EINVAL; + + /* Set the Keyboard Backlight Mode where: + * Mode - Auto (2) | FN-Z (1) + * Auto - KBD backlight turns off automatically in given time + * FN-Z - KBD backlight "toggles" when hotkey pressed + */ + if (mode != -1 && toshiba->kbd_mode != mode) { + time = toshiba->kbd_time << HCI_MISC_SHIFT; + time = time + toshiba->kbd_mode; + if (toshiba_kbd_illum_status_set(toshiba, time) < 0) + return -EIO; + toshiba->kbd_mode = mode; + } + + return count; +} + +static ssize_t toshiba_kbd_bl_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); + u32 time; + + if (toshiba_kbd_illum_status_get(toshiba, &time) < 0) + return -EIO; + + return sprintf(buf, "%i\n", time & 0x07); +} + +static ssize_t toshiba_kbd_bl_timeout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); + int time = -1; + + if (sscanf(buf, "%i", &time) != 1 && (time < 0 || time > 60)) + return -EINVAL; + + /* Set the Keyboard Backlight Timeout: 0-60 seconds */ + if (time != -1 && toshiba->kbd_time != time) { + time = time << HCI_MISC_SHIFT; + time = (toshiba->kbd_mode == SCI_KBD_MODE_AUTO) ? + time + 1 : time + 2; + if (toshiba_kbd_illum_status_set(toshiba, time) < 0) + return -EIO; + toshiba->kbd_time = time >> HCI_MISC_SHIFT; + } + + return count; +} + +static ssize_t toshiba_kbd_bl_timeout_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); + u32 time; + + if (toshiba_kbd_illum_status_get(toshiba, &time) < 0) + return -EIO; + + return sprintf(buf, "%i\n", time >> HCI_MISC_SHIFT); +} + +static DEVICE_ATTR(kbd_backlight_mode, S_IRUGO | S_IWUSR, + toshiba_kbd_bl_mode_show, toshiba_kbd_bl_mode_store); +static DEVICE_ATTR(kbd_backlight_timeout, S_IRUGO | S_IWUSR, + toshiba_kbd_bl_timeout_show, toshiba_kbd_bl_timeout_store); + +static struct attribute *toshiba_attributes[] = { + &dev_attr_kbd_backlight_mode.attr, + &dev_attr_kbd_backlight_timeout.attr, + NULL, +}; + +static umode_t toshiba_sysfs_is_visible(struct kobject *kobj, + struct attribute *attr, int idx) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct toshiba_acpi_dev *drv = dev_get_drvdata(dev); + bool exists = true; + + if (attr == &dev_attr_kbd_backlight_mode.attr) + exists = (drv->kbd_illum_supported) ? true : false; + else if (attr == &dev_attr_kbd_backlight_timeout.attr) + exists = (drv->kbd_mode == SCI_KBD_MODE_AUTO) ? true : false; + + return exists ? attr->mode : 0; +} + +static struct attribute_group toshiba_attr_group = { + .is_visible = toshiba_sysfs_is_visible, + .attrs = toshiba_attributes, +}; static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str, struct serio *port) @@ -1158,6 +1363,10 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev) struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); remove_toshiba_proc_entries(dev); + + if (dev->sysfs_created) + sysfs_remove_group(&dev->acpi_dev->dev.kobj, + &toshiba_attr_group); if (dev->ntfy_supported) { i8042_remove_filter(toshiba_acpi_i8042_filter); @@ -1179,6 +1388,9 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev) if (dev->illumination_supported) led_classdev_unregister(&dev->led_dev); + + if (dev->kbd_led_registered) + led_classdev_unregister(&dev->kbd_led); if (toshiba_acpi) toshiba_acpi = NULL; @@ -1225,6 +1437,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) dev->acpi_dev = acpi_dev; dev->method_hci = hci_method; acpi_dev->driver_data = dev; + dev_set_drvdata(&acpi_dev->dev, dev); if (toshiba_acpi_setup_keyboard(dev)) pr_info("Unable to activate hotkeys\n"); @@ -1264,6 +1477,25 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) if (!led_classdev_register(&acpi_dev->dev, &dev->led_dev)) dev->illumination_supported = 1; } + + ret = toshiba_kbd_illum_status_get(dev, &dummy); + if (!ret) { + dev->kbd_time = dummy >> HCI_MISC_SHIFT; + dev->kbd_mode = dummy & 0x07; + } + dev->kbd_illum_supported = !ret; + /* + * Only register the LED if KBD illumination is supported + * and the keyboard backlight operation mode is set to FN-Z + */ + if (dev->kbd_illum_supported && dev->kbd_mode == SCI_KBD_MODE_FNZ) { + dev->kbd_led.name = "toshiba::kbd_backlight"; + dev->kbd_led.max_brightness = 1; + dev->kbd_led.brightness_set = toshiba_kbd_backlight_set; + dev->kbd_led.brightness_get = toshiba_kbd_backlight_get; + if (!led_classdev_register(&dev->acpi_dev->dev, &dev->kbd_led)) + dev->kbd_led_registered = 1; + } /* Determine whether or not BIOS supports fan and video interfaces */ @@ -1273,6 +1505,14 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) ret = get_fan_status(dev, &dummy); dev->fan_supported = !ret; + ret = sysfs_create_group(&dev->acpi_dev->dev.kobj, + &toshiba_attr_group); + if (ret) { + dev->sysfs_created = 0; + goto error; + } + dev->sysfs_created = !ret; + create_toshiba_proc_entries(dev); toshiba_acpi = dev; -- cgit v1.2.3-70-g09d2 From 9d8658acd6be9139ef91dfe6c001796e7a03ded6 Mon Sep 17 00:00:00 2001 From: Azael Avalos Date: Tue, 25 Mar 2014 20:38:32 -0600 Subject: toshiba_acpi: Add touchpad enable/disable support- Toshiba laptops have two ways of letting userspace know the touchpad has changed state, one with a button on top of the touchpad that simply emmits scancodes whenever enabled/disabled, and another one by pressing Fn-F9 (touchpad toggle) hotkey. This patch adds support to enable/disable the touchpad by exposing the _touchpad_ file in sysfs that simply makes a call to a SCI register, imitating what Toshiba provided software does on Windows. Signed-off-by: Azael Avalos Signed-off-by: Matthew Garrett --- drivers/platform/x86/toshiba_acpi.c | 81 +++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 95ae1ffaf409..08c53768447b 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -114,6 +114,7 @@ MODULE_LICENSE("GPL"); #define HCI_KBD_ILLUMINATION 0x0095 #define SCI_ILLUMINATION 0x014e #define SCI_KBD_ILLUM_STATUS 0x015c +#define SCI_TOUCHPAD 0x050e /* field definitions */ #define HCI_HOTKEY_DISABLE 0x0b @@ -157,6 +158,7 @@ struct toshiba_acpi_dev { unsigned int tr_backlight_supported:1; unsigned int kbd_illum_supported:1; unsigned int kbd_led_registered:1; + unsigned int touchpad_supported:1; unsigned int sysfs_created:1; struct mutex mutex; @@ -527,6 +529,47 @@ static void toshiba_kbd_backlight_set(struct led_classdev *cdev, return; } } + +/* TouchPad support */ +static int toshiba_touchpad_set(struct toshiba_acpi_dev *dev, u32 state) +{ + u32 result; + acpi_status status; + + if (!sci_open(dev)) + return -EIO; + + status = sci_write(dev, SCI_TOUCHPAD, state, &result); + sci_close(dev); + if (ACPI_FAILURE(status)) { + pr_err("ACPI call to set the touchpad failed\n"); + return -EIO; + } else if (result == HCI_NOT_SUPPORTED) { + return -ENODEV; + } + + return 0; +} + +static int toshiba_touchpad_get(struct toshiba_acpi_dev *dev, u32 *state) +{ + u32 result; + acpi_status status; + + if (!sci_open(dev)) + return -EIO; + + status = sci_read(dev, SCI_TOUCHPAD, state, &result); + sci_close(dev); + if (ACPI_FAILURE(status)) { + pr_err("ACPI call to query the touchpad failed\n"); + return -EIO; + } else if (result == HCI_NOT_SUPPORTED) { + return -ENODEV; + } + + return 0; +} /* Bluetooth rfkill handlers */ @@ -1130,15 +1173,48 @@ static ssize_t toshiba_kbd_bl_timeout_show(struct device *dev, return sprintf(buf, "%i\n", time >> HCI_MISC_SHIFT); } + +static ssize_t toshiba_touchpad_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); + int state; + + /* Set the TouchPad on/off, 0 - Disable | 1 - Enable */ + if (sscanf(buf, "%i", &state) == 1 && (state == 0 || state == 1)) { + if (toshiba_touchpad_set(toshiba, state) < 0) + return -EIO; + } + + return count; +} + +static ssize_t toshiba_touchpad_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); + u32 state; + int ret; + + ret = toshiba_touchpad_get(toshiba, &state); + if (ret < 0) + return ret; + + return sprintf(buf, "%i\n", state); +} static DEVICE_ATTR(kbd_backlight_mode, S_IRUGO | S_IWUSR, toshiba_kbd_bl_mode_show, toshiba_kbd_bl_mode_store); static DEVICE_ATTR(kbd_backlight_timeout, S_IRUGO | S_IWUSR, toshiba_kbd_bl_timeout_show, toshiba_kbd_bl_timeout_store); +static DEVICE_ATTR(touchpad, S_IRUGO | S_IWUSR, + toshiba_touchpad_show, toshiba_touchpad_store); static struct attribute *toshiba_attributes[] = { &dev_attr_kbd_backlight_mode.attr, &dev_attr_kbd_backlight_timeout.attr, + &dev_attr_touchpad.attr, NULL, }; @@ -1153,6 +1229,8 @@ static umode_t toshiba_sysfs_is_visible(struct kobject *kobj, exists = (drv->kbd_illum_supported) ? true : false; else if (attr == &dev_attr_kbd_backlight_timeout.attr) exists = (drv->kbd_mode == SCI_KBD_MODE_AUTO) ? true : false; + else if (attr == &dev_attr_touchpad.attr) + exists = (drv->touchpad_supported) ? true : false; return exists ? attr->mode : 0; } @@ -1496,6 +1574,9 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) if (!led_classdev_register(&dev->acpi_dev->dev, &dev->kbd_led)) dev->kbd_led_registered = 1; } + + ret = toshiba_touchpad_get(dev, &dummy); + dev->touchpad_supported = !ret; /* Determine whether or not BIOS supports fan and video interfaces */ -- cgit v1.2.3-70-g09d2 From def6c4e25d31b874b939bd08e7941eaa9711653f Mon Sep 17 00:00:00 2001 From: Azael Avalos Date: Tue, 25 Mar 2014 20:38:33 -0600 Subject: toshiba_acpi: Add ECO mode led support Newer Toshiba laptops now come with a feature called ECO Mode, where the system is put in low power consupmtion state and a green (world shaped with leaves) icon illuminates indicating that the system is in such power state. This patch adds support to turn on/off the ECO led by creating and registering the toshiba::eco_mode led. Signed-off-by: Azael Avalos Signed-off-by: Matthew Garrett --- drivers/platform/x86/toshiba_acpi.c | 66 +++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 08c53768447b..c8e8bfb78c19 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -112,6 +112,7 @@ MODULE_LICENSE("GPL"); #define HCI_LCD_BRIGHTNESS 0x002a #define HCI_WIRELESS 0x0056 #define HCI_KBD_ILLUMINATION 0x0095 +#define HCI_ECO_MODE 0x0097 #define SCI_ILLUMINATION 0x014e #define SCI_KBD_ILLUM_STATUS 0x015c #define SCI_TOUCHPAD 0x050e @@ -142,6 +143,7 @@ struct toshiba_acpi_dev { struct backlight_device *backlight_dev; struct led_classdev led_dev; struct led_classdev kbd_led; + struct led_classdev eco_led; int force_fan; int last_key_event; @@ -159,6 +161,7 @@ struct toshiba_acpi_dev { unsigned int kbd_illum_supported:1; unsigned int kbd_led_registered:1; unsigned int touchpad_supported:1; + unsigned int eco_supported:1; unsigned int sysfs_created:1; struct mutex mutex; @@ -571,6 +574,57 @@ static int toshiba_touchpad_get(struct toshiba_acpi_dev *dev, u32 *state) return 0; } +/* Eco Mode support */ +static int toshiba_eco_mode_available(struct toshiba_acpi_dev *dev) +{ + acpi_status status; + u32 in[HCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 1, 0, 0 }; + u32 out[HCI_WORDS]; + + status = hci_raw(dev, in, out); + if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { + pr_info("ACPI call to get ECO led failed\n"); + return 0; + } + + return 1; +} + +static enum led_brightness toshiba_eco_mode_get_status(struct led_classdev *cdev) +{ + struct toshiba_acpi_dev *dev = container_of(cdev, + struct toshiba_acpi_dev, eco_led); + u32 in[HCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 1, 0, 0 }; + u32 out[HCI_WORDS]; + acpi_status status; + + status = hci_raw(dev, in, out); + if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { + pr_err("ACPI call to get ECO led failed\n"); + return LED_OFF; + } + + return out[2] ? LED_FULL : LED_OFF; +} + +static void toshiba_eco_mode_set_status(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct toshiba_acpi_dev *dev = container_of(cdev, + struct toshiba_acpi_dev, eco_led); + u32 in[HCI_WORDS] = { HCI_SET, HCI_ECO_MODE, 0, 1, 0, 0 }; + u32 out[HCI_WORDS]; + acpi_status status; + + /* Switch the Eco Mode led on/off */ + in[2] = (brightness) ? 1 : 0; + status = hci_raw(dev, in, out); + if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { + pr_err("ACPI call to set ECO led failed\n"); + return; + } +} + /* Bluetooth rfkill handlers */ static u32 hci_get_bt_present(struct toshiba_acpi_dev *dev, bool *present) @@ -1469,6 +1523,9 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev) if (dev->kbd_led_registered) led_classdev_unregister(&dev->kbd_led); + + if (dev->eco_supported) + led_classdev_unregister(&dev->eco_led); if (toshiba_acpi) toshiba_acpi = NULL; @@ -1556,6 +1613,15 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) dev->illumination_supported = 1; } + if (toshiba_eco_mode_available(dev)) { + dev->eco_led.name = "toshiba::eco_mode"; + dev->eco_led.max_brightness = 1; + dev->eco_led.brightness_set = toshiba_eco_mode_set_status; + dev->eco_led.brightness_get = toshiba_eco_mode_get_status; + if (!led_classdev_register(&dev->acpi_dev->dev, &dev->eco_led)) + dev->eco_supported = 1; + } + ret = toshiba_kbd_illum_status_get(dev, &dummy); if (!ret) { dev->kbd_time = dummy >> HCI_MISC_SHIFT; -- cgit v1.2.3-70-g09d2 From 5a2813e97ae6e5d8c521ace7dd2f4106a9d19e71 Mon Sep 17 00:00:00 2001 From: Azael Avalos Date: Tue, 25 Mar 2014 20:38:34 -0600 Subject: toshiba_acpi: Add accelerometer support Recent Toshiba laptops now come equiped with a built in accelerometer (TOS620A) device, but such device does not expose the axes information, however, HCI calls 0x006d and 0x00a6 can be used to query such info. This patch adds support to read the axes values by exposing them through the _position_ sysfs file. Signed-off-by: Azael Avalos Signed-off-by: Matthew Garrett --- drivers/platform/x86/toshiba_acpi.c | 80 +++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index c8e8bfb78c19..c971b0fae4c4 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -97,6 +97,8 @@ MODULE_LICENSE("GPL"); #define HCI_FAILURE 0x1000 #define HCI_NOT_SUPPORTED 0x8000 #define HCI_EMPTY 0x8c00 +#define HCI_DATA_NOT_AVAILABLE 0x8d20 +#define HCI_NOT_INITIALIZED 0x8d50 #define SCI_OPEN_CLOSE_OK 0x0044 #define SCI_ALREADY_OPEN 0x8100 #define SCI_NOT_OPENED 0x8200 @@ -111,13 +113,16 @@ MODULE_LICENSE("GPL"); #define HCI_HOTKEY_EVENT 0x001e #define HCI_LCD_BRIGHTNESS 0x002a #define HCI_WIRELESS 0x0056 +#define HCI_ACCELEROMETER 0x006d #define HCI_KBD_ILLUMINATION 0x0095 #define HCI_ECO_MODE 0x0097 +#define HCI_ACCELEROMETER2 0x00a6 #define SCI_ILLUMINATION 0x014e #define SCI_KBD_ILLUM_STATUS 0x015c #define SCI_TOUCHPAD 0x050e /* field definitions */ +#define HCI_ACCEL_MASK 0x7fff #define HCI_HOTKEY_DISABLE 0x0b #define HCI_HOTKEY_ENABLE 0x09 #define HCI_LCD_BRIGHTNESS_BITS 3 @@ -162,6 +167,7 @@ struct toshiba_acpi_dev { unsigned int kbd_led_registered:1; unsigned int touchpad_supported:1; unsigned int eco_supported:1; + unsigned int accelerometer_supported:1; unsigned int sysfs_created:1; struct mutex mutex; @@ -624,6 +630,52 @@ static void toshiba_eco_mode_set_status(struct led_classdev *cdev, return; } } + +/* Accelerometer support */ +static int toshiba_accelerometer_supported(struct toshiba_acpi_dev *dev) +{ + u32 in[HCI_WORDS] = { HCI_GET, HCI_ACCELEROMETER2, 0, 0, 0, 0 }; + u32 out[HCI_WORDS]; + acpi_status status; + + /* Check if the accelerometer call exists, + * this call also serves as initialization + */ + status = hci_raw(dev, in, out); + if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { + pr_err("ACPI call to query the accelerometer failed\n"); + return -EIO; + } else if (out[0] == HCI_DATA_NOT_AVAILABLE || + out[0] == HCI_NOT_INITIALIZED) { + pr_err("Accelerometer not initialized\n"); + return -EIO; + } else if (out[0] == HCI_NOT_SUPPORTED) { + pr_info("Accelerometer not supported\n"); + return -ENODEV; + } + + return 0; +} + +static int toshiba_accelerometer_get(struct toshiba_acpi_dev *dev, + u32 *xy, u32 *z) +{ + u32 in[HCI_WORDS] = { HCI_GET, HCI_ACCELEROMETER, 0, 1, 0, 0 }; + u32 out[HCI_WORDS]; + acpi_status status; + + /* Check the Accelerometer status */ + status = hci_raw(dev, in, out); + if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { + pr_err("ACPI call to query the accelerometer failed\n"); + return -EIO; + } + + *xy = out[2]; + *z = out[4]; + + return 0; +} /* Bluetooth rfkill handlers */ @@ -1257,6 +1309,27 @@ static ssize_t toshiba_touchpad_show(struct device *dev, return sprintf(buf, "%i\n", state); } + +static ssize_t toshiba_position_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); + u32 xyval, zval, tmp; + u16 x, y, z; + int ret; + + xyval = zval = 0; + ret = toshiba_accelerometer_get(toshiba, &xyval, &zval); + if (ret < 0) + return ret; + + x = xyval & HCI_ACCEL_MASK; + tmp = xyval >> HCI_MISC_SHIFT; + y = tmp & HCI_ACCEL_MASK; + z = zval & HCI_ACCEL_MASK; + + return sprintf(buf, "%d %d %d\n", x, y, z); +} static DEVICE_ATTR(kbd_backlight_mode, S_IRUGO | S_IWUSR, toshiba_kbd_bl_mode_show, toshiba_kbd_bl_mode_store); @@ -1264,11 +1337,13 @@ static DEVICE_ATTR(kbd_backlight_timeout, S_IRUGO | S_IWUSR, toshiba_kbd_bl_timeout_show, toshiba_kbd_bl_timeout_store); static DEVICE_ATTR(touchpad, S_IRUGO | S_IWUSR, toshiba_touchpad_show, toshiba_touchpad_store); +static DEVICE_ATTR(position, S_IRUGO, toshiba_position_show, NULL); static struct attribute *toshiba_attributes[] = { &dev_attr_kbd_backlight_mode.attr, &dev_attr_kbd_backlight_timeout.attr, &dev_attr_touchpad.attr, + &dev_attr_position.attr, NULL, }; @@ -1285,6 +1360,8 @@ static umode_t toshiba_sysfs_is_visible(struct kobject *kobj, exists = (drv->kbd_mode == SCI_KBD_MODE_AUTO) ? true : false; else if (attr == &dev_attr_touchpad.attr) exists = (drv->touchpad_supported) ? true : false; + else if (attr == &dev_attr_position.attr) + exists = (drv->accelerometer_supported) ? true : false; return exists ? attr->mode : 0; } @@ -1643,6 +1720,9 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) ret = toshiba_touchpad_get(dev, &dummy); dev->touchpad_supported = !ret; + + ret = toshiba_accelerometer_supported(dev); + dev->accelerometer_supported = !ret; /* Determine whether or not BIOS supports fan and video interfaces */ -- cgit v1.2.3-70-g09d2 From 548c43065f7ec894c6d63e6c9036efe2f6cafcf0 Mon Sep 17 00:00:00 2001 From: Azael Avalos Date: Tue, 25 Mar 2014 20:38:35 -0600 Subject: toshiba_acpi: Update version and copyright info Given that some new features were added to the driver, bump its version to 0.20 and add myself to the copyright list for these new features that were added. Signed-off-by: Azael Avalos Signed-off-by: Matthew Garrett --- drivers/platform/x86/toshiba_acpi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index c971b0fae4c4..c08c12f4c182 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -5,6 +5,7 @@ * Copyright (C) 2002-2004 John Belmonte * Copyright (C) 2008 Philip Langdale * Copyright (C) 2010 Pierre Ducroquet + * Copyright (C) 2014 Azael Avalos * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -37,7 +38,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define TOSHIBA_ACPI_VERSION "0.19" +#define TOSHIBA_ACPI_VERSION "0.20" #define PROC_INTERFACE_VERSION 1 #include -- cgit v1.2.3-70-g09d2 From ea6b31f4946ddc527ec4eff7436727c18765d566 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Fri, 4 Apr 2014 14:22:34 -0400 Subject: toshiba_acpi: Fix whitespace Tidy up whitespace introduced by the previous patchset Signed-off-by: Matthew Garrett --- drivers/platform/x86/toshiba_acpi.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index c08c12f4c182..46473ca7566b 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -78,7 +78,7 @@ MODULE_LICENSE("GPL"); * However the ACPI methods seem to be incomplete in some areas (for * example they allow setting, but not reading, the LCD brightness value), * so this is still useful. - * + * * SCI stands for "System Configuration Interface" which aim is to * conceal differences in hardware between different models. */ @@ -456,7 +456,7 @@ static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) return state ? LED_FULL : LED_OFF; } - + /* KBD Illumination */ static int toshiba_kbd_illum_status_set(struct toshiba_acpi_dev *dev, u32 time) { @@ -539,7 +539,7 @@ static void toshiba_kbd_backlight_set(struct led_classdev *cdev, return; } } - + /* TouchPad support */ static int toshiba_touchpad_set(struct toshiba_acpi_dev *dev, u32 state) { @@ -631,7 +631,7 @@ static void toshiba_eco_mode_set_status(struct led_classdev *cdev, return; } } - + /* Accelerometer support */ static int toshiba_accelerometer_supported(struct toshiba_acpi_dev *dev) { @@ -1200,7 +1200,7 @@ static const struct backlight_ops toshiba_backlight_data = { .get_brightness = get_lcd_brightness, .update_status = set_lcd_status, }; - + /* * Sysfs files */ @@ -1280,7 +1280,7 @@ static ssize_t toshiba_kbd_bl_timeout_show(struct device *dev, return sprintf(buf, "%i\n", time >> HCI_MISC_SHIFT); } - + static ssize_t toshiba_touchpad_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -1310,7 +1310,7 @@ static ssize_t toshiba_touchpad_show(struct device *dev, return sprintf(buf, "%i\n", state); } - + static ssize_t toshiba_position_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1573,7 +1573,7 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev) struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); remove_toshiba_proc_entries(dev); - + if (dev->sysfs_created) sysfs_remove_group(&dev->acpi_dev->dev.kobj, &toshiba_attr_group); @@ -1598,10 +1598,10 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev) if (dev->illumination_supported) led_classdev_unregister(&dev->led_dev); - + if (dev->kbd_led_registered) led_classdev_unregister(&dev->kbd_led); - + if (dev->eco_supported) led_classdev_unregister(&dev->eco_led); @@ -1690,7 +1690,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) if (!led_classdev_register(&acpi_dev->dev, &dev->led_dev)) dev->illumination_supported = 1; } - + if (toshiba_eco_mode_available(dev)) { dev->eco_led.name = "toshiba::eco_mode"; dev->eco_led.max_brightness = 1; @@ -1699,7 +1699,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) if (!led_classdev_register(&dev->acpi_dev->dev, &dev->eco_led)) dev->eco_supported = 1; } - + ret = toshiba_kbd_illum_status_get(dev, &dummy); if (!ret) { dev->kbd_time = dummy >> HCI_MISC_SHIFT; @@ -1718,10 +1718,10 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) if (!led_classdev_register(&dev->acpi_dev->dev, &dev->kbd_led)) dev->kbd_led_registered = 1; } - + ret = toshiba_touchpad_get(dev, &dummy); dev->touchpad_supported = !ret; - + ret = toshiba_accelerometer_supported(dev); dev->accelerometer_supported = !ret; -- cgit v1.2.3-70-g09d2 From 3a9d20bda1d6daae9d81a4cc4cc67238c5574d31 Mon Sep 17 00:00:00 2001 From: Shuduo Sang Date: Thu, 6 Mar 2014 18:20:46 +0800 Subject: support Thinkpad X1 Carbon 2nd generation's adaptive keyboard Submit patch V4 to support Adaptive Keyboard on Thinkpad X1 Carbon 2nd generation according to Tobias's comments. Thanks, Shuduo >From b153a7b14791c6e01892c0e274e23eefd625fb8d Mon Sep 17 00:00:00 2001 From: Shuduo Sang Date: Mon, 3 Mar 2014 14:29:32 +0800 Subject: [PATCH] support thinkpad X1 Carbon's adaptive keyboard Thinkpad X1 Carbon's adaptive keyboard has five modes including Home mode, Web browser mode, Web conference mode, Function mode and Lay-flat mode. We support Home mode and Function mode currently. Signed-off-by: Bruce Ma Signed-off-by: Shuduo Sang Signed-off-by: Matthew Garrett --- drivers/platform/x86/thinkpad_acpi.c | 102 +++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index dcc833a0b2fd..d0f2a5330096 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -3437,6 +3437,106 @@ err_exit: return (res < 0)? res : 1; } +/* Thinkpad X1 Carbon support 5 modes including Home mode, Web browser + * mode, Web conference mode, Function mode and Lay-flat mode. + * We support Home mode and Function mode currently. + * + * Will consider support rest of modes in future. + * + */ +enum ADAPTIVE_KEY_MODE { + HOME_MODE, + WEB_BROWSER_MODE, + WEB_CONFERENCE_MODE, + FUNCTION_MODE, + LAYFLAT_MODE +}; + +const int adaptive_keyboard_modes[] = { + HOME_MODE, +/* WEB_BROWSER_MODE = 2, + WEB_CONFERENCE_MODE = 3, */ + FUNCTION_MODE +}; + +#define DFR_CHANGE_ROW 0x101 +#define DFR_SHOW_QUICKVIEW_ROW 0x102 + +/* press Fn key a while second, it will switch to Function Mode. Then + * release Fn key, previous mode be restored. + */ +static bool adaptive_keyboard_mode_is_saved; +static int adaptive_keyboard_prev_mode; + +static int adaptive_keyboard_get_next_mode(int mode) +{ + size_t i; + size_t max_mode = ARRAY_SIZE(adaptive_keyboard_modes) - 1; + + for (i = 0; i <= max_mode; i++) { + if (adaptive_keyboard_modes[i] == mode) + break; + } + + if (i >= max_mode) + i = 0; + else + i++; + + return adaptive_keyboard_modes[i]; +} + +static bool adaptive_keyboard_hotkey_notify_hotkey(unsigned int scancode) +{ + u32 current_mode = 0; + int new_mode = 0; + + switch (scancode) { + case DFR_CHANGE_ROW: + if (adaptive_keyboard_mode_is_saved) { + new_mode = adaptive_keyboard_prev_mode; + adaptive_keyboard_mode_is_saved = false; + } else { + if (!acpi_evalf( + hkey_handle, ¤t_mode, + "GTRW", "dd", 0)) { + pr_err("Cannot read adaptive keyboard mode\n"); + return false; + } else { + new_mode = adaptive_keyboard_get_next_mode( + current_mode); + } + } + + if (!acpi_evalf(hkey_handle, NULL, "STRW", "vd", new_mode)) { + pr_err("Cannot set adaptive keyboard mode\n"); + return false; + } + + return true; + + case DFR_SHOW_QUICKVIEW_ROW: + if (!acpi_evalf(hkey_handle, + &adaptive_keyboard_prev_mode, + "GTRW", "dd", 0)) { + pr_err("Cannot read adaptive keyboard mode\n"); + return false; + } else { + adaptive_keyboard_mode_is_saved = true; + + if (!acpi_evalf(hkey_handle, + NULL, "STRW", "vd", FUNCTION_MODE)) { + pr_err("Cannot set adaptive keyboard mode\n"); + return false; + } + } + return true; + + default: + return false; + } +} + static bool hotkey_notify_hotkey(const u32 hkey, bool *send_acpi_ev, bool *ignore_acpi_ev) @@ -3456,6 +3556,8 @@ static bool hotkey_notify_hotkey(const u32 hkey, *ignore_acpi_ev = true; } return true; + } else { + return adaptive_keyboard_hotkey_notify_hotkey(scancode); } return false; } -- cgit v1.2.3-70-g09d2 From 330947b84382479459e5296a0024c670367b0b57 Mon Sep 17 00:00:00 2001 From: Shuduo Sang Date: Thu, 27 Mar 2014 18:06:25 +0800 Subject: save and restore adaptive keyboard mode for suspend and,resume Dan Aloni has submitted a patch to set adaptive mode to function mode when system resume back. Thanks Dan. :) Following patch can make it to be restored to previous mode like What Windows does. Thanks, Shuduo >From 0ca960138518ceab23110141a0d7c0cafd54a859 Mon Sep 17 00:00:00 2001 From: Shuduo Sang Date: Thu, 27 Mar 2014 17:51:24 +0800 Subject: [PATCH] save and restore adaptive keyboard mode for suspend and resume The mode of adaptive keyboard on X1 Carbon need be saved first before suspend then it can be restored after resume. Otherwise it will be unusable. Signed-off-by: Bruce Ma Signed-off-by: Shuduo Sang Signed-off-by: Matthew Garrett --- drivers/platform/x86/thinkpad_acpi.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index d0f2a5330096..18c9862dd932 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -3830,13 +3830,28 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) static void hotkey_suspend(void) { + int hkeyv; + /* Do these on suspend, we get the events on early resume! */ hotkey_wakeup_reason = TP_ACPI_WAKEUP_NONE; hotkey_autosleep_ack = 0; + + /* save previous mode of adaptive keyboard of X1 Carbon */ + if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) { + if ((hkeyv >> 8) == 2) { + if (!acpi_evalf(hkey_handle, + &adaptive_keyboard_prev_mode, + "GTRW", "dd", 0)) { + pr_err("Cannot read adaptive keyboard mode.\n"); + } + } + } } static void hotkey_resume(void) { + int hkeyv; + tpacpi_disable_brightness_delay(); if (hotkey_status_set(true) < 0 || @@ -3849,6 +3864,18 @@ static void hotkey_resume(void) hotkey_wakeup_reason_notify_change(); hotkey_wakeup_hotunplug_complete_notify_change(); hotkey_poll_setup_safe(false); + + /* restore previous mode of adapive keyboard of X1 Carbon */ + if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) { + if ((hkeyv >> 8) == 2) { + if (!acpi_evalf(hkey_handle, + NULL, + "STRW", "vd", + adaptive_keyboard_prev_mode)) { + pr_err("Cannot set adaptive keyboard mode.\n"); + } + } + } } /* procfs -------------------------------------------------------------- */ -- cgit v1.2.3-70-g09d2 From a4d44ba1266a04cdfda4b2c4ee496d684a47f567 Mon Sep 17 00:00:00 2001 From: Behan Webster Date: Wed, 12 Feb 2014 21:58:46 +0100 Subject: x86, acpi: LLVMLinux: Remove nested functions from Thinkpad ACPI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The only real change is passing in event_mask to the formerly nested functions. Otherwise it's just moving around function and macro code. This is the only place in the Linux kernel where nested functions are still in use. Nested functions aren't part of the C standards, and complicate the generated code. Although the Linux Kernel has never set out to be entirely C standard compliant, it is increasingly compliant to the standard which is supported by other compilers such as Clang. The LLVMLinux project is working on being able to compile the Linux kernel with Clang. The use of nested functions blocks this effort. Signed-off-by: Behan Webster Signed-off-by: Jan-Simon Möller CC: David Woodhouse CC: Matthew Garrett CC: ibm-acpi-devel@lists.sourceforge.net CC: platform-driver-x86@vger.kernel.org CC: linux-kernel@vger.kernel.org Signed-off-by: Matthew Garrett --- drivers/platform/x86/thinkpad_acpi.c | 86 +++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 41 deletions(-) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 18c9862dd932..15e61c16736e 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -2321,53 +2321,55 @@ static void hotkey_read_nvram(struct tp_nvram_state *n, const u32 m) } } -static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, - struct tp_nvram_state *newn, - const u32 event_mask) -{ - #define TPACPI_COMPARE_KEY(__scancode, __member) \ - do { \ - if ((event_mask & (1 << __scancode)) && \ - oldn->__member != newn->__member) \ - tpacpi_hotkey_send_key(__scancode); \ - } while (0) +do { \ + if ((event_mask & (1 << __scancode)) && \ + oldn->__member != newn->__member) \ + tpacpi_hotkey_send_key(__scancode); \ +} while (0) #define TPACPI_MAY_SEND_KEY(__scancode) \ - do { \ - if (event_mask & (1 << __scancode)) \ - tpacpi_hotkey_send_key(__scancode); \ - } while (0) +do { \ + if (event_mask & (1 << __scancode)) \ + tpacpi_hotkey_send_key(__scancode); \ +} while (0) - void issue_volchange(const unsigned int oldvol, - const unsigned int newvol) - { - unsigned int i = oldvol; +static void issue_volchange(const unsigned int oldvol, + const unsigned int newvol, + const u32 event_mask) +{ + unsigned int i = oldvol; - while (i > newvol) { - TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN); - i--; - } - while (i < newvol) { - TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); - i++; - } + while (i > newvol) { + TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN); + i--; + } + while (i < newvol) { + TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); + i++; } +} - void issue_brightnesschange(const unsigned int oldbrt, - const unsigned int newbrt) - { - unsigned int i = oldbrt; +static void issue_brightnesschange(const unsigned int oldbrt, + const unsigned int newbrt, + const u32 event_mask) +{ + unsigned int i = oldbrt; - while (i > newbrt) { - TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND); - i--; - } - while (i < newbrt) { - TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME); - i++; - } + while (i > newbrt) { + TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND); + i--; } + while (i < newbrt) { + TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME); + i++; + } +} + +static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, + struct tp_nvram_state *newn, + const u32 event_mask) +{ TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle); TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle); @@ -2402,7 +2404,8 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, oldn->volume_level != newn->volume_level) { /* recently muted, or repeated mute keypress, or * multiple presses ending in mute */ - issue_volchange(oldn->volume_level, newn->volume_level); + issue_volchange(oldn->volume_level, newn->volume_level, + event_mask); TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE); } } else { @@ -2412,7 +2415,8 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); } if (oldn->volume_level != newn->volume_level) { - issue_volchange(oldn->volume_level, newn->volume_level); + issue_volchange(oldn->volume_level, newn->volume_level, + event_mask); } else if (oldn->volume_toggle != newn->volume_toggle) { /* repeated vol up/down keypress at end of scale ? */ if (newn->volume_level == 0) @@ -2425,7 +2429,7 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, /* handle brightness */ if (oldn->brightness_level != newn->brightness_level) { issue_brightnesschange(oldn->brightness_level, - newn->brightness_level); + newn->brightness_level, event_mask); } else if (oldn->brightness_toggle != newn->brightness_toggle) { /* repeated key presses that didn't change state */ if (newn->brightness_level == 0) -- cgit v1.2.3-70-g09d2 From 48d8b96c7201b4bf9c7f58e932ea14aa7a01f161 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 3 Feb 2014 15:23:40 +0100 Subject: x86, platform: Make HP_WIRELESS option text more descriptive ... so that one can know what this option is about without opening the long help text. Cc: Matthew Garrett Cc: platform-driver-x86@vger.kernel.org Signed-off-by: Borislav Petkov Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 826ed3af77cb..34e3025e25ba 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -196,7 +196,7 @@ config HP_ACCEL be called hp_accel. config HP_WIRELESS - tristate "HP WIRELESS" + tristate "HP wireless button" depends on ACPI depends on INPUT help -- cgit v1.2.3-70-g09d2 From 71db1183d4c661eaedc299b721526160bf304bd3 Mon Sep 17 00:00:00 2001 From: Scott K Logan Date: Fri, 4 Apr 2014 14:13:04 -0400 Subject: fujitsu-tablet: add support for Lifebook T901 and T902 The button mappings for the Fujitsu Lifebook T901 and T902 are quite different from the generic Lifebook T mappings that are defined. This patch adds mappings that are specific to the hardware on these machines, and allows users to take advantage of features like screen rotation. Signed-off-by: Scott K Logan Signed-off-by: Matthew Garrett --- drivers/platform/x86/fujitsu-tablet.c | 65 +++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujitsu-tablet.c index 570926c10014..c3784baceae3 100644 --- a/drivers/platform/x86/fujitsu-tablet.c +++ b/drivers/platform/x86/fujitsu-tablet.c @@ -71,6 +71,44 @@ static unsigned short keymap_Lifebook_Tseries[KEYMAP_LEN] __initdata = { KEY_LEFTALT }; +static unsigned short keymap_Lifebook_T901[KEYMAP_LEN] __initdata = { + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_SCROLLDOWN, + KEY_SCROLLUP, + KEY_CYCLEWINDOWS, + KEY_LEFTCTRL, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_LEFTMETA +}; + +static unsigned short keymap_Lifebook_T902[KEYMAP_LEN] __initdata = { + KEY_RESERVED, + KEY_VOLUMEDOWN, + KEY_VOLUMEUP, + KEY_CYCLEWINDOWS, + KEY_PROG1, + KEY_PROG2, + KEY_LEFTMETA, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, +}; + static unsigned short keymap_Lifebook_U810[KEYMAP_LEN] __initdata = { KEY_RESERVED, KEY_RESERVED, @@ -300,6 +338,33 @@ static int fujitsu_dmi_stylistic(const struct dmi_system_id *dmi) } static const struct dmi_system_id dmi_ids[] __initconst = { + { + .callback = fujitsu_dmi_lifebook, + .ident = "Fujitsu Lifebook T901", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook T901") + }, + .driver_data = keymap_Lifebook_T901 + }, + { + .callback = fujitsu_dmi_lifebook, + .ident = "Fujitsu Lifebook T901", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T901") + }, + .driver_data = keymap_Lifebook_T901 + }, + { + .callback = fujitsu_dmi_lifebook, + .ident = "Fujitsu Lifebook T902", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T902") + }, + .driver_data = keymap_Lifebook_T902 + }, { .callback = fujitsu_dmi_lifebook, .ident = "Fujitsu Siemens P/T Series", -- cgit v1.2.3-70-g09d2 From a46ad0f13bc32a9601f3c5dff43fafdc2c598814 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Fri, 4 Apr 2014 14:15:42 -0400 Subject: Add WMI driver for controlling AlienFX features on some Alienware products Signed-off-by: Mario Limonciello Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 12 + drivers/platform/x86/Makefile | 1 + drivers/platform/x86/alienware-wmi.c | 557 +++++++++++++++++++++++++++++++++++ 3 files changed, 570 insertions(+) create mode 100644 drivers/platform/x86/alienware-wmi.c diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 34e3025e25ba..27df2c533b09 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -53,6 +53,18 @@ config ACERHDF If you have an Acer Aspire One netbook, say Y or M here. +config ALIENWARE_WMI + tristate "Alienware Special feature control" + depends on ACPI + depends on LEDS_CLASS + depends on NEW_LEDS + depends on ACPI_WMI + ---help--- + This is a driver for controlling Alienware BIOS driven + features. It exposes an interface for controlling the AlienFX + zones on Alienware machines that don't contain a dedicated AlienFX + USB MCU such as the X51 and X51-R2. + config ASUS_LAPTOP tristate "Asus Laptop Extras" depends on ACPI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index b8c36f785312..1a2eafc9d48e 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -55,3 +55,4 @@ obj-$(CONFIG_INTEL_RST) += intel-rst.o obj-$(CONFIG_INTEL_SMARTCONNECT) += intel-smartconnect.o obj-$(CONFIG_PVPANIC) += pvpanic.o +obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o diff --git a/drivers/platform/x86/alienware-wmi.c b/drivers/platform/x86/alienware-wmi.c new file mode 100644 index 000000000000..3e17e996021c --- /dev/null +++ b/drivers/platform/x86/alienware-wmi.c @@ -0,0 +1,557 @@ +/* + * Alienware AlienFX control + * + * Copyright (C) 2014 Dell Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +#define LEGACY_CONTROL_GUID "A90597CE-A997-11DA-B012-B622A1EF5492" +#define LEGACY_POWER_CONTROL_GUID "A80593CE-A997-11DA-B012-B622A1EF5492" +#define WMAX_CONTROL_GUID "A70591CE-A997-11DA-B012-B622A1EF5492" + +#define WMAX_METHOD_HDMI_SOURCE 0x1 +#define WMAX_METHOD_HDMI_STATUS 0x2 +#define WMAX_METHOD_BRIGHTNESS 0x3 +#define WMAX_METHOD_ZONE_CONTROL 0x4 + +MODULE_AUTHOR("Mario Limonciello "); +MODULE_DESCRIPTION("Alienware special feature control"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("wmi:" LEGACY_CONTROL_GUID); +MODULE_ALIAS("wmi:" WMAX_CONTROL_GUID); + +enum INTERFACE_FLAGS { + LEGACY, + WMAX, +}; + +enum LEGACY_CONTROL_STATES { + LEGACY_RUNNING = 1, + LEGACY_BOOTING = 0, + LEGACY_SUSPEND = 3, +}; + +enum WMAX_CONTROL_STATES { + WMAX_RUNNING = 0xFF, + WMAX_BOOTING = 0, + WMAX_SUSPEND = 3, +}; + +struct quirk_entry { + u8 num_zones; +}; + +static struct quirk_entry *quirks; + +static struct quirk_entry quirk_unknown = { + .num_zones = 2, +}; + +static struct quirk_entry quirk_x51_family = { + .num_zones = 3, +}; + +static int dmi_matched(const struct dmi_system_id *dmi) +{ + quirks = dmi->driver_data; + return 1; +} + +static struct dmi_system_id alienware_quirks[] = { + { + .callback = dmi_matched, + .ident = "Alienware X51 R1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"), + }, + .driver_data = &quirk_x51_family, + }, + { + .callback = dmi_matched, + .ident = "Alienware X51 R2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"), + }, + .driver_data = &quirk_x51_family, + }, + {} +}; + +struct color_platform { + u8 blue; + u8 green; + u8 red; +} __packed; + +struct platform_zone { + u8 location; + struct device_attribute *attr; + struct color_platform colors; +}; + +struct wmax_brightness_args { + u32 led_mask; + u32 percentage; +}; + +struct hdmi_args { + u8 arg; +}; + +struct legacy_led_args { + struct color_platform colors; + u8 brightness; + u8 state; +} __packed; + +struct wmax_led_args { + u32 led_mask; + struct color_platform colors; + u8 state; +} __packed; + +static struct platform_device *platform_device; +static struct device_attribute *zone_dev_attrs; +static struct attribute **zone_attrs; +static struct platform_zone *zone_data; + +static struct platform_driver platform_driver = { + .driver = { + .name = "alienware-wmi", + .owner = THIS_MODULE, + } +}; + +static struct attribute_group zone_attribute_group = { + .name = "rgb_zones", +}; + +static u8 interface; +static u8 lighting_control_state; +static u8 global_brightness; + +/* + * Helpers used for zone control +*/ +static int parse_rgb(const char *buf, struct platform_zone *zone) +{ + long unsigned int rgb; + int ret; + union color_union { + struct color_platform cp; + int package; + } repackager; + + ret = kstrtoul(buf, 16, &rgb); + if (ret) + return ret; + + /* RGB triplet notation is 24-bit hexadecimal */ + if (rgb > 0xFFFFFF) + return -EINVAL; + + repackager.package = rgb & 0x0f0f0f0f; + pr_debug("alienware-wmi: r: %d g:%d b: %d\n", + repackager.cp.red, repackager.cp.green, repackager.cp.blue); + zone->colors = repackager.cp; + return 0; +} + +static struct platform_zone *match_zone(struct device_attribute *attr) +{ + int i; + for (i = 0; i < quirks->num_zones; i++) { + if ((struct device_attribute *)zone_data[i].attr == attr) { + pr_debug("alienware-wmi: matched zone location: %d\n", + zone_data[i].location); + return &zone_data[i]; + } + } + return NULL; +} + +/* + * Individual RGB zone control +*/ +static int alienware_update_led(struct platform_zone *zone) +{ + int method_id; + acpi_status status; + char *guid; + struct acpi_buffer input; + struct legacy_led_args legacy_args; + struct wmax_led_args wmax_args; + if (interface == WMAX) { + wmax_args.led_mask = 1 << zone->location; + wmax_args.colors = zone->colors; + wmax_args.state = lighting_control_state; + guid = WMAX_CONTROL_GUID; + method_id = WMAX_METHOD_ZONE_CONTROL; + + input.length = (acpi_size) sizeof(wmax_args); + input.pointer = &wmax_args; + } else { + legacy_args.colors = zone->colors; + legacy_args.brightness = global_brightness; + legacy_args.state = 0; + if (lighting_control_state == LEGACY_BOOTING || + lighting_control_state == LEGACY_SUSPEND) { + guid = LEGACY_POWER_CONTROL_GUID; + legacy_args.state = lighting_control_state; + } else + guid = LEGACY_CONTROL_GUID; + method_id = zone->location + 1; + + input.length = (acpi_size) sizeof(legacy_args); + input.pointer = &legacy_args; + } + pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id); + + status = wmi_evaluate_method(guid, 1, method_id, &input, NULL); + if (ACPI_FAILURE(status)) + pr_err("alienware-wmi: zone set failure: %u\n", status); + return ACPI_FAILURE(status); +} + +static ssize_t zone_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct platform_zone *target_zone; + target_zone = match_zone(attr); + if (target_zone == NULL) + return sprintf(buf, "red: -1, green: -1, blue: -1\n"); + return sprintf(buf, "red: %d, green: %d, blue: %d\n", + target_zone->colors.red, + target_zone->colors.green, target_zone->colors.blue); + +} + +static ssize_t zone_set(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_zone *target_zone; + int ret; + target_zone = match_zone(attr); + if (target_zone == NULL) { + pr_err("alienware-wmi: invalid target zone\n"); + return 1; + } + ret = parse_rgb(buf, target_zone); + if (ret) + return ret; + ret = alienware_update_led(target_zone); + return ret ? ret : count; +} + +/* + * LED Brightness (Global) +*/ +static int wmax_brightness(int brightness) +{ + acpi_status status; + struct acpi_buffer input; + struct wmax_brightness_args args = { + .led_mask = 0xFF, + .percentage = brightness, + }; + input.length = (acpi_size) sizeof(args); + input.pointer = &args; + status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1, + WMAX_METHOD_BRIGHTNESS, &input, NULL); + if (ACPI_FAILURE(status)) + pr_err("alienware-wmi: brightness set failure: %u\n", status); + return ACPI_FAILURE(status); +} + +static void global_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + int ret; + global_brightness = brightness; + if (interface == WMAX) + ret = wmax_brightness(brightness); + else + ret = alienware_update_led(&zone_data[0]); + if (ret) + pr_err("LED brightness update failed\n"); +} + +static enum led_brightness global_led_get(struct led_classdev *led_cdev) +{ + return global_brightness; +} + +static struct led_classdev global_led = { + .brightness_set = global_led_set, + .brightness_get = global_led_get, + .name = "alienware::global_brightness", +}; + +/* + * Lighting control state device attribute (Global) +*/ +static ssize_t show_control_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (lighting_control_state == LEGACY_BOOTING) + return scnprintf(buf, PAGE_SIZE, "[booting] running suspend\n"); + else if (lighting_control_state == LEGACY_SUSPEND) + return scnprintf(buf, PAGE_SIZE, "booting running [suspend]\n"); + return scnprintf(buf, PAGE_SIZE, "booting [running] suspend\n"); +} + +static ssize_t store_control_state(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + long unsigned int val; + if (strcmp(buf, "booting\n") == 0) + val = LEGACY_BOOTING; + else if (strcmp(buf, "suspend\n") == 0) + val = LEGACY_SUSPEND; + else if (interface == LEGACY) + val = LEGACY_RUNNING; + else + val = WMAX_RUNNING; + lighting_control_state = val; + pr_debug("alienware-wmi: updated control state to %d\n", + lighting_control_state); + return count; +} + +static DEVICE_ATTR(lighting_control_state, 0644, show_control_state, + store_control_state); + +static int alienware_zone_init(struct platform_device *dev) +{ + int i; + char buffer[10]; + char *name; + + if (interface == WMAX) { + global_led.max_brightness = 100; + lighting_control_state = WMAX_RUNNING; + } else if (interface == LEGACY) { + global_led.max_brightness = 0x0F; + lighting_control_state = LEGACY_RUNNING; + } + global_brightness = global_led.max_brightness; + + /* + * - zone_dev_attrs num_zones + 1 is for individual zones and then + * null terminated + * - zone_attrs num_zones + 2 is for all attrs in zone_dev_attrs + + * the lighting control + null terminated + * - zone_data num_zones is for the distinct zones + */ + zone_dev_attrs = + kzalloc(sizeof(struct device_attribute) * (quirks->num_zones + 1), + GFP_KERNEL); + zone_attrs = + kzalloc(sizeof(struct attribute *) * (quirks->num_zones + 2), + GFP_KERNEL); + zone_data = + kzalloc(sizeof(struct platform_zone) * (quirks->num_zones), + GFP_KERNEL); + + for (i = 0; i < quirks->num_zones; i++) { + sprintf(buffer, "zone%02X", i); + name = kstrdup(buffer, GFP_KERNEL); + if (name == NULL) + return 1; + sysfs_attr_init(&zone_dev_attrs[i].attr); + zone_dev_attrs[i].attr.name = name; + zone_dev_attrs[i].attr.mode = 0644; + zone_dev_attrs[i].show = zone_show; + zone_dev_attrs[i].store = zone_set; + zone_data[i].location = i; + zone_attrs[i] = &zone_dev_attrs[i].attr; + zone_data[i].attr = &zone_dev_attrs[i]; + } + zone_attrs[quirks->num_zones] = &dev_attr_lighting_control_state.attr; + zone_attribute_group.attrs = zone_attrs; + + led_classdev_register(&dev->dev, &global_led); + + return sysfs_create_group(&dev->dev.kobj, &zone_attribute_group); +} + +static void alienware_zone_exit(struct platform_device *dev) +{ + sysfs_remove_group(&dev->dev.kobj, &zone_attribute_group); + led_classdev_unregister(&global_led); + if (zone_dev_attrs) { + int i; + for (i = 0; i < quirks->num_zones; i++) + kfree(zone_dev_attrs[i].attr.name); + } + kfree(zone_dev_attrs); + kfree(zone_data); + kfree(zone_attrs); +} + +/* + The HDMI mux sysfs node indicates the status of the HDMI input mux. + It can toggle between standard system GPU output and HDMI input. +*/ +static ssize_t show_hdmi(struct device *dev, struct device_attribute *attr, + char *buf) +{ + acpi_status status; + struct acpi_buffer input; + union acpi_object *obj; + u32 tmp = 0; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + struct hdmi_args in_args = { + .arg = 0, + }; + input.length = (acpi_size) sizeof(in_args); + input.pointer = &in_args; + status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1, + WMAX_METHOD_HDMI_STATUS, &input, &output); + + if (ACPI_SUCCESS(status)) { + obj = (union acpi_object *)output.pointer; + if (obj && obj->type == ACPI_TYPE_INTEGER) + tmp = (u32) obj->integer.value; + if (tmp == 1) + return scnprintf(buf, PAGE_SIZE, + "[input] gpu unknown\n"); + else if (tmp == 2) + return scnprintf(buf, PAGE_SIZE, + "input [gpu] unknown\n"); + } + pr_err("alienware-wmi: unknown HDMI status: %d\n", status); + return scnprintf(buf, PAGE_SIZE, "input gpu [unknown]\n"); +} + +static ssize_t toggle_hdmi(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct acpi_buffer input; + acpi_status status; + struct hdmi_args args; + if (strcmp(buf, "gpu\n") == 0) + args.arg = 1; + else if (strcmp(buf, "input\n") == 0) + args.arg = 2; + else + args.arg = 3; + pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf); + input.length = (acpi_size) sizeof(args); + input.pointer = &args; + status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1, + WMAX_METHOD_HDMI_SOURCE, &input, NULL); + if (ACPI_FAILURE(status)) + pr_err("alienware-wmi: HDMI toggle failed: results: %u\n", + status); + return count; +} + +static DEVICE_ATTR(hdmi, S_IRUGO | S_IWUSR, show_hdmi, toggle_hdmi); + +static void remove_hdmi(struct platform_device *device) +{ + device_remove_file(&device->dev, &dev_attr_hdmi); +} + +static int create_hdmi(void) +{ + int ret = -ENOMEM; + ret = device_create_file(&platform_device->dev, &dev_attr_hdmi); + if (ret) + goto error_create_hdmi; + return 0; + +error_create_hdmi: + remove_hdmi(platform_device); + return ret; +} + +static int __init alienware_wmi_init(void) +{ + int ret; + + if (wmi_has_guid(LEGACY_CONTROL_GUID)) + interface = LEGACY; + else if (wmi_has_guid(WMAX_CONTROL_GUID)) + interface = WMAX; + else { + pr_warn("alienware-wmi: No known WMI GUID found\n"); + return -ENODEV; + } + + dmi_check_system(alienware_quirks); + if (quirks == NULL) + quirks = &quirk_unknown; + + ret = platform_driver_register(&platform_driver); + if (ret) + goto fail_platform_driver; + platform_device = platform_device_alloc("alienware-wmi", -1); + if (!platform_device) { + ret = -ENOMEM; + goto fail_platform_device1; + } + ret = platform_device_add(platform_device); + if (ret) + goto fail_platform_device2; + + if (interface == WMAX) { + ret = create_hdmi(); + if (ret) + goto fail_prep_hdmi; + } + + ret = alienware_zone_init(platform_device); + if (ret) + goto fail_prep_zones; + + return 0; + +fail_prep_zones: + alienware_zone_exit(platform_device); +fail_prep_hdmi: + platform_device_del(platform_device); +fail_platform_device2: + platform_device_put(platform_device); +fail_platform_device1: + platform_driver_unregister(&platform_driver); +fail_platform_driver: + return ret; +} + +module_init(alienware_wmi_init); + +static void __exit alienware_wmi_exit(void) +{ + alienware_zone_exit(platform_device); + remove_hdmi(platform_device); + if (platform_device) { + platform_device_unregister(platform_device); + platform_driver_unregister(&platform_driver); + } +} + +module_exit(alienware_wmi_exit); -- cgit v1.2.3-70-g09d2 From 562c7cec1e92be85ade6f65fff107651e10ee2ed Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Fri, 4 Apr 2014 14:40:20 -0500 Subject: alienware-wmi: cover some scenarios where memory allocations would fail Intel test builder caught a few instances that should test if kzalloc failed to allocate memory as well as a scenario that platform_driver wasn't properly initialized. Signed-off-by: Mario Limonciello Signed-off-by: Matthew Garrett --- drivers/platform/x86/alienware-wmi.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/alienware-wmi.c b/drivers/platform/x86/alienware-wmi.c index 3e17e996021c..541f9514f76f 100644 --- a/drivers/platform/x86/alienware-wmi.c +++ b/drivers/platform/x86/alienware-wmi.c @@ -368,12 +368,20 @@ static int alienware_zone_init(struct platform_device *dev) zone_dev_attrs = kzalloc(sizeof(struct device_attribute) * (quirks->num_zones + 1), GFP_KERNEL); + if (!zone_dev_attrs) + return -ENOMEM; + zone_attrs = kzalloc(sizeof(struct attribute *) * (quirks->num_zones + 2), GFP_KERNEL); + if (!zone_attrs) + return -ENOMEM; + zone_data = kzalloc(sizeof(struct platform_zone) * (quirks->num_zones), GFP_KERNEL); + if (!zone_data) + return -ENOMEM; for (i = 0; i < quirks->num_zones; i++) { sprintf(buffer, "zone%02X", i); @@ -546,9 +554,9 @@ module_init(alienware_wmi_init); static void __exit alienware_wmi_exit(void) { - alienware_zone_exit(platform_device); - remove_hdmi(platform_device); if (platform_device) { + alienware_zone_exit(platform_device); + remove_hdmi(platform_device); platform_device_unregister(platform_device); platform_driver_unregister(&platform_driver); } -- cgit v1.2.3-70-g09d2