From 093ed561648d43263c009ea88abab21a31cd4f1d Mon Sep 17 00:00:00 2001 From: Smelov Andrey Date: Tue, 20 Sep 2011 09:16:10 -0700 Subject: [PATCH 01/45] Platform: samsung_laptop: samsung backlight for R528/R728 patch works for me, but I need to add "acpi_backlight=vendor" to kernel params Signed-off-by: Smelov Andrey Cc: stable Signed-off-by: Greg Kroah-Hartman Signed-off-by: Matthew Garrett --- drivers/platform/x86/samsung-laptop.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index 359163011044..37374a5878eb 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -686,6 +686,15 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = { }, .callback = dmi_check_cb, }, + { + .ident = "R528/R728", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "R528/R728"), + DMI_MATCH(DMI_BOARD_NAME, "R528/R728"), + }, + .callback = dmi_check_cb, + }, { }, }; MODULE_DEVICE_TABLE(dmi, samsung_dmi_table); From bee460be8c691c544e84ed678280ace6153104c6 Mon Sep 17 00:00:00 2001 From: Jason Stubbs Date: Tue, 20 Sep 2011 09:16:11 -0700 Subject: [PATCH 02/45] platform: samsung_laptop: fix samsung brightness min/max calculations The min_brightness value of the sabi_config is incorrectly used in brightness calculations. For the config where min_brightness = 1 and max_brightness = 8, the user visible range should be 0 to 7 with hardware being set in the range of 1 to 8. What is actually happening is that the user visible range is 0 to 8 with hardware being set in the range of -1 to 7. This patch fixes the above issue as well as a miscalculation that would occur in the case of min_brightness > 1. Signed-off-by: Jason Stubbs Cc: stable Signed-off-by: Greg Kroah-Hartman Signed-off-by: Matthew Garrett --- drivers/platform/x86/samsung-laptop.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index 37374a5878eb..7eb6733138e6 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -370,15 +370,17 @@ static u8 read_brightness(void) &sretval); if (!retval) { user_brightness = sretval.retval[0]; - if (user_brightness != 0) + if (user_brightness > sabi_config->min_brightness) user_brightness -= sabi_config->min_brightness; + else + user_brightness = 0; } return user_brightness; } static void set_brightness(u8 user_brightness) { - u8 user_level = user_brightness - sabi_config->min_brightness; + u8 user_level = user_brightness + sabi_config->min_brightness; sabi_set_command(sabi_config->commands.set_brightness, user_level); } @@ -811,7 +813,8 @@ static int __init samsung_init(void) /* create a backlight device to talk to this one */ memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_PLATFORM; - props.max_brightness = sabi_config->max_brightness; + props.max_brightness = sabi_config->max_brightness - + sabi_config->min_brightness; backlight_device = backlight_device_register("samsung", &sdev->dev, NULL, &backlight_ops, &props); From a7ea19926ffba86f373f6050a106cd162dbb9a78 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Tue, 20 Sep 2011 09:16:12 -0700 Subject: [PATCH 03/45] Platform: Fix error path in samsung-laptop init samsung_init() should not return success if not all devices are initialized. Otherwise, samsung_exit() will dereference sdev NULL pointers and others. Signed-off-by: David Herrmann Cc: stable Signed-off-by: Greg Kroah-Hartman Signed-off-by: Matthew Garrett --- drivers/platform/x86/samsung-laptop.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index 7eb6733138e6..4d3bed476d52 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -781,7 +781,7 @@ static int __init samsung_init(void) sabi_iface = ioremap_nocache(ifaceP, 16); if (!sabi_iface) { pr_err("Can't remap %x\n", ifaceP); - goto exit; + goto error_no_signature; } if (debug) { printk(KERN_DEBUG "ifaceP = 0x%08x\n", ifaceP); @@ -833,7 +833,6 @@ static int __init samsung_init(void) if (retval) goto error_file_create; -exit: return 0; error_file_create: From ac080523141d5bfa5f60ef2436480f645f915e9c Mon Sep 17 00:00:00 2001 From: Jason Stubbs Date: Tue, 20 Sep 2011 09:16:13 -0700 Subject: [PATCH 04/45] Platform: Brightness quirk for samsung laptop driver On some Samsung laptops the brightness regulation works slightly different. All SABI commands except for set_brightness work as expected. The behaviour of set_brightness is as follows: - Setting a new brightness will only step one level toward the new brightness level. For example, setting a level of 5 when the current level is 2 will result in a brightness level of 3. - A spurious KEY_BRIGHTNESS_UP or KEY_BRIGHTNESS_DOWN event is also generated along with the change in brightness. - Neither of the above two issues occur when changing from/to brightness level 0. This patch adds detection and a non-intrusive workaround for the above issues. Signed-off-by: Jason Stubbs Tested-by: David Herrmann Signed-off-by: Greg Kroah-Hartman Signed-off-by: Matthew Garrett --- drivers/platform/x86/samsung-laptop.c | 43 +++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index 4d3bed476d52..6474e426dae9 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -226,6 +226,7 @@ static struct backlight_device *backlight_device; static struct mutex sabi_mutex; static struct platform_device *sdev; static struct rfkill *rfk; +static bool has_stepping_quirk; static int force; module_param(force, bool, 0); @@ -382,6 +383,17 @@ static void set_brightness(u8 user_brightness) { u8 user_level = user_brightness + sabi_config->min_brightness; + if (has_stepping_quirk && user_level != 0) { + /* + * short circuit if the specified level is what's already set + * to prevent the screen from flickering needlessly + */ + if (user_brightness == read_brightness()) + return; + + sabi_set_command(sabi_config->commands.set_brightness, 0); + } + sabi_set_command(sabi_config->commands.set_brightness, user_level); } @@ -390,6 +402,34 @@ static int get_brightness(struct backlight_device *bd) return (int)read_brightness(); } +static void check_for_stepping_quirk(void) +{ + u8 initial_level = read_brightness(); + u8 check_level; + + /* + * Some laptops exhibit the strange behaviour of stepping toward + * (rather than setting) the brightness except when changing to/from + * brightness level 0. This behaviour is checked for here and worked + * around in set_brightness. + */ + + if (initial_level <= 2) + check_level = initial_level + 2; + else + check_level = initial_level - 2; + + has_stepping_quirk = false; + set_brightness(check_level); + + if (read_brightness() != check_level) { + has_stepping_quirk = true; + pr_info("enabled workaround for brightness stepping quirk\n"); + } + + set_brightness(initial_level); +} + static int update_status(struct backlight_device *bd) { set_brightness(bd->props.brightness); @@ -805,6 +845,9 @@ static int __init samsung_init(void) } } + /* Check for stepping quirk */ + check_for_stepping_quirk(); + /* knock up a platform device to hang stuff off of */ sdev = platform_device_register_simple("samsung", -1, NULL, 0); if (IS_ERR(sdev)) From 7b3c257ce4267e004a7c7e68c05d1eb70da8c972 Mon Sep 17 00:00:00 2001 From: Jason Stubbs Date: Tue, 20 Sep 2011 09:16:14 -0700 Subject: [PATCH 05/45] Platform: Samsung laptop DMI info for NC210/NC110 This patch just adds the DMI info for the samsung laptop driver to work with the NC210/NC110. It needs the brightness quirk patch for proper support. Signed-off-by: Jason Stubbs Signed-off-by: Greg Kroah-Hartman Signed-off-by: Matthew Garrett --- drivers/platform/x86/samsung-laptop.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index 6474e426dae9..8ffab5010423 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -737,6 +737,15 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = { }, .callback = dmi_check_cb, }, + { + .ident = "NC210/NC110", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "NC210/NC110"), + DMI_MATCH(DMI_BOARD_NAME, "NC210/NC110"), + }, + .callback = dmi_check_cb, + }, { }, }; MODULE_DEVICE_TABLE(dmi, samsung_dmi_table); From f689c875c13c9d62b3c4de09cd5dad66549f700d Mon Sep 17 00:00:00 2001 From: Raul Gutierrez Segales Date: Tue, 20 Sep 2011 09:16:15 -0700 Subject: [PATCH 06/45] Platform: fix samsung-laptop DMI identification for N220 model This is a follow-up for commit 78a7539b, which didn't cover the Samsung N220 laptop. With this backlight brightness works nicely on the N220 netbook. Signed-off-by: Raul Gutierrez Segales Signed-off-by: Greg Kroah-Hartman Signed-off-by: Matthew Garrett --- drivers/platform/x86/samsung-laptop.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index 8ffab5010423..1e436ebdcffc 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -662,6 +662,16 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = { }, .callback = dmi_check_cb, }, + { + .ident = "N220", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, + "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "N220"), + DMI_MATCH(DMI_BOARD_NAME, "N220"), + }, + .callback = dmi_check_cb, + }, { .ident = "N150/N210/N220/N230", .matches = { From 135740de77641b4180c8a1f19abcfcd5e4351b15 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Tue, 20 Sep 2011 16:55:49 -0500 Subject: [PATCH 07/45] toshiba_acpi: Convert to use acpi_driver Changes toshiba_acpi to register an acpi driver and eliminates the platform device it was using. Also eliminates most global variables, moving them into toshiba_acpi_dev, along with some other miscellaneous fixes and cleanup. Signed-off-by: Azael Avalos Signed-off-by: Seth Forshee Signed-off-by: Matthew Garrett --- drivers/platform/x86/toshiba_acpi.c | 586 ++++++++++++++-------------- 1 file changed, 301 insertions(+), 285 deletions(-) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index cb009b2629ee..d74c97cde0f1 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -47,7 +47,6 @@ #include #include #include -#include #include #include #include @@ -111,6 +110,22 @@ MODULE_LICENSE("GPL"); #define HCI_WIRELESS_BT_ATTACH 0x40 #define HCI_WIRELESS_BT_POWER 0x80 +struct toshiba_acpi_dev { + struct acpi_device *acpi_dev; + const char *method_hci; + struct rfkill *bt_rfk; + struct input_dev *hotkey_dev; + struct backlight_device *backlight_dev; + struct led_classdev led_dev; + int illumination_installed; + int force_fan; + int last_key_event; + int key_event_valid; + acpi_handle handle; + + struct mutex mutex; +}; + static const struct acpi_device_id toshiba_device_ids[] = { {"TOS6200", 0}, {"TOS6208", 0}, @@ -119,7 +134,7 @@ static const struct acpi_device_id toshiba_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, toshiba_device_ids); -static const struct key_entry toshiba_acpi_keymap[] __initconst = { +static const struct key_entry toshiba_acpi_keymap[] __devinitconst = { { KE_KEY, 0x101, { KEY_MUTE } }, { KE_KEY, 0x102, { KEY_ZOOMOUT } }, { KE_KEY, 0x103, { KEY_ZOOMIN } }, @@ -179,29 +194,11 @@ static int write_acpi_int(const char *methodName, int val) return (status == AE_OK); } -#if 0 -static int read_acpi_int(const char *methodName, int *pVal) -{ - struct acpi_buffer results; - union acpi_object out_objs[1]; - acpi_status status; - - results.length = sizeof(out_objs); - results.pointer = out_objs; - - status = acpi_evaluate_object(0, (char *)methodName, 0, &results); - *pVal = out_objs[0].integer.value; - - return (status == AE_OK) && (out_objs[0].type == ACPI_TYPE_INTEGER); -} -#endif - -static const char *method_hci /*= 0*/ ; - /* Perform a raw HCI call. Here we don't care about input or output buffer * format. */ -static acpi_status hci_raw(const u32 in[HCI_WORDS], u32 out[HCI_WORDS]) +static acpi_status hci_raw(struct toshiba_acpi_dev *dev, + const u32 in[HCI_WORDS], u32 out[HCI_WORDS]) { struct acpi_object_list params; union acpi_object in_objs[HCI_WORDS]; @@ -220,7 +217,7 @@ static acpi_status hci_raw(const u32 in[HCI_WORDS], u32 out[HCI_WORDS]) results.length = sizeof(out_objs); results.pointer = out_objs; - status = acpi_evaluate_object(NULL, (char *)method_hci, ¶ms, + status = acpi_evaluate_object(NULL, (char *)dev->method_hci, ¶ms, &results); if ((status == AE_OK) && (out_objs->package.count <= HCI_WORDS)) { for (i = 0; i < out_objs->package.count; ++i) { @@ -237,85 +234,79 @@ static acpi_status hci_raw(const u32 in[HCI_WORDS], u32 out[HCI_WORDS]) * may be useful (such as "not supported"). */ -static acpi_status hci_write1(u32 reg, u32 in1, u32 * result) +static acpi_status hci_write1(struct toshiba_acpi_dev *dev, u32 reg, + u32 in1, u32 *result) { u32 in[HCI_WORDS] = { HCI_SET, reg, in1, 0, 0, 0 }; u32 out[HCI_WORDS]; - acpi_status status = hci_raw(in, out); + acpi_status status = hci_raw(dev, in, out); *result = (status == AE_OK) ? out[0] : HCI_FAILURE; return status; } -static acpi_status hci_read1(u32 reg, u32 * out1, u32 * result) +static acpi_status hci_read1(struct toshiba_acpi_dev *dev, u32 reg, + u32 *out1, u32 *result) { u32 in[HCI_WORDS] = { HCI_GET, reg, 0, 0, 0, 0 }; u32 out[HCI_WORDS]; - acpi_status status = hci_raw(in, out); + acpi_status status = hci_raw(dev, in, out); *out1 = out[2]; *result = (status == AE_OK) ? out[0] : HCI_FAILURE; return status; } -static acpi_status hci_write2(u32 reg, u32 in1, u32 in2, u32 *result) +static acpi_status hci_write2(struct toshiba_acpi_dev *dev, u32 reg, + u32 in1, u32 in2, u32 *result) { u32 in[HCI_WORDS] = { HCI_SET, reg, in1, in2, 0, 0 }; u32 out[HCI_WORDS]; - acpi_status status = hci_raw(in, out); + acpi_status status = hci_raw(dev, in, out); *result = (status == AE_OK) ? out[0] : HCI_FAILURE; return status; } -static acpi_status hci_read2(u32 reg, u32 *out1, u32 *out2, u32 *result) +static acpi_status hci_read2(struct toshiba_acpi_dev *dev, u32 reg, + u32 *out1, u32 *out2, u32 *result) { u32 in[HCI_WORDS] = { HCI_GET, reg, *out1, *out2, 0, 0 }; u32 out[HCI_WORDS]; - acpi_status status = hci_raw(in, out); + acpi_status status = hci_raw(dev, in, out); *out1 = out[2]; *out2 = out[3]; *result = (status == AE_OK) ? out[0] : HCI_FAILURE; return status; } -struct toshiba_acpi_dev { - struct platform_device *p_dev; - struct rfkill *bt_rfk; - struct input_dev *hotkey_dev; - int illumination_installed; - acpi_handle handle; - - const char *bt_name; - - struct mutex mutex; -}; - /* Illumination support */ -static int toshiba_illumination_available(void) +static int toshiba_illumination_available(struct toshiba_acpi_dev *dev) { u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; u32 out[HCI_WORDS]; acpi_status status; in[0] = 0xf100; - status = hci_raw(in, out); + status = hci_raw(dev, in, out); if (ACPI_FAILURE(status)) { pr_info("Illumination device not available\n"); return 0; } in[0] = 0xf400; - status = hci_raw(in, out); + status = hci_raw(dev, in, out); return 1; } static void toshiba_illumination_set(struct led_classdev *cdev, enum led_brightness brightness) { + 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]; acpi_status status; /* First request : initialize communication. */ in[0] = 0xf100; - status = hci_raw(in, out); + status = hci_raw(dev, in, out); if (ACPI_FAILURE(status)) { pr_info("Illumination device not available\n"); return; @@ -326,7 +317,7 @@ static void toshiba_illumination_set(struct led_classdev *cdev, in[0] = 0xf400; in[1] = 0x14e; in[2] = 1; - status = hci_raw(in, out); + status = hci_raw(dev, in, out); if (ACPI_FAILURE(status)) { pr_info("ACPI call for illumination failed\n"); return; @@ -336,7 +327,7 @@ static void toshiba_illumination_set(struct led_classdev *cdev, in[0] = 0xf400; in[1] = 0x14e; in[2] = 0; - status = hci_raw(in, out); + status = hci_raw(dev, in, out); if (ACPI_FAILURE(status)) { pr_info("ACPI call for illumination failed.\n"); return; @@ -347,11 +338,13 @@ static void toshiba_illumination_set(struct led_classdev *cdev, in[0] = 0xf200; in[1] = 0; in[2] = 0; - hci_raw(in, out); + 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]; acpi_status status; @@ -359,7 +352,7 @@ static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) /* First request : initialize communication. */ in[0] = 0xf100; - status = hci_raw(in, out); + status = hci_raw(dev, in, out); if (ACPI_FAILURE(status)) { pr_info("Illumination device not available\n"); return LED_OFF; @@ -368,7 +361,7 @@ static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) /* Check the illumination */ in[0] = 0xf300; in[1] = 0x14e; - status = hci_raw(in, out); + status = hci_raw(dev, in, out); if (ACPI_FAILURE(status)) { pr_info("ACPI call for illumination failed.\n"); return LED_OFF; @@ -380,46 +373,35 @@ static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) in[0] = 0xf200; in[1] = 0; in[2] = 0; - hci_raw(in, out); + hci_raw(dev, in, out); return result; } -static struct led_classdev toshiba_led = { - .name = "toshiba::illumination", - .max_brightness = 1, - .brightness_set = toshiba_illumination_set, - .brightness_get = toshiba_illumination_get, -}; - -static struct toshiba_acpi_dev toshiba_acpi = { - .bt_name = "Toshiba Bluetooth", -}; - /* Bluetooth rfkill handlers */ -static u32 hci_get_bt_present(bool *present) +static u32 hci_get_bt_present(struct toshiba_acpi_dev *dev, bool *present) { u32 hci_result; u32 value, value2; value = 0; value2 = 0; - hci_read2(HCI_WIRELESS, &value, &value2, &hci_result); + hci_read2(dev, HCI_WIRELESS, &value, &value2, &hci_result); if (hci_result == HCI_SUCCESS) *present = (value & HCI_WIRELESS_BT_PRESENT) ? true : false; return hci_result; } -static u32 hci_get_radio_state(bool *radio_state) +static u32 hci_get_radio_state(struct toshiba_acpi_dev *dev, bool *radio_state) { u32 hci_result; u32 value, value2; value = 0; value2 = 0x0001; - hci_read2(HCI_WIRELESS, &value, &value2, &hci_result); + hci_read2(dev, HCI_WIRELESS, &value, &value2, &hci_result); *radio_state = value & HCI_WIRELESS_KILL_SWITCH; return hci_result; @@ -436,7 +418,7 @@ static int bt_rfkill_set_block(void *data, bool blocked) value = (blocked == false); mutex_lock(&dev->mutex); - if (hci_get_radio_state(&radio_state) != HCI_SUCCESS) { + if (hci_get_radio_state(dev, &radio_state) != HCI_SUCCESS) { err = -EBUSY; goto out; } @@ -446,8 +428,8 @@ static int bt_rfkill_set_block(void *data, bool blocked) goto out; } - hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1); - hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2); + hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1); + hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2); if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS) err = -EBUSY; @@ -467,7 +449,7 @@ static void bt_rfkill_poll(struct rfkill *rfkill, void *data) mutex_lock(&dev->mutex); - hci_result = hci_get_radio_state(&value); + hci_result = hci_get_radio_state(dev, &value); if (hci_result != HCI_SUCCESS) { /* Can't do anything useful */ mutex_unlock(&dev->mutex); @@ -488,17 +470,14 @@ static const struct rfkill_ops toshiba_rfk_ops = { }; static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ; -static struct backlight_device *toshiba_backlight_device; -static int force_fan; -static int last_key_event; -static int key_event_valid; static int get_lcd(struct backlight_device *bd) { + struct toshiba_acpi_dev *dev = bl_get_data(bd); u32 hci_result; u32 value; - hci_read1(HCI_LCD_BRIGHTNESS, &value, &hci_result); + hci_read1(dev, HCI_LCD_BRIGHTNESS, &value, &hci_result); if (hci_result == HCI_SUCCESS) { return (value >> HCI_LCD_BRIGHTNESS_SHIFT); } else @@ -507,8 +486,13 @@ static int get_lcd(struct backlight_device *bd) static int lcd_proc_show(struct seq_file *m, void *v) { - int value = get_lcd(NULL); + struct toshiba_acpi_dev *dev = m->private; + int value; + if (!dev->backlight_dev) + return -ENODEV; + + value = get_lcd(dev->backlight_dev); if (value >= 0) { seq_printf(m, "brightness: %d\n", value); seq_printf(m, "brightness_levels: %d\n", @@ -522,15 +506,15 @@ static int lcd_proc_show(struct seq_file *m, void *v) static int lcd_proc_open(struct inode *inode, struct file *file) { - return single_open(file, lcd_proc_show, NULL); + return single_open(file, lcd_proc_show, PDE(inode)->data); } -static int set_lcd(int value) +static int set_lcd(struct toshiba_acpi_dev *dev, int value) { u32 hci_result; value = value << HCI_LCD_BRIGHTNESS_SHIFT; - hci_write1(HCI_LCD_BRIGHTNESS, value, &hci_result); + hci_write1(dev, HCI_LCD_BRIGHTNESS, value, &hci_result); if (hci_result != HCI_SUCCESS) return -EFAULT; @@ -539,12 +523,14 @@ static int set_lcd(int value) static int set_lcd_status(struct backlight_device *bd) { - return set_lcd(bd->props.brightness); + struct toshiba_acpi_dev *dev = bl_get_data(bd); + return set_lcd(dev, bd->props.brightness); } static ssize_t lcd_proc_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) { + struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data; char cmd[42]; size_t len; int value; @@ -557,7 +543,7 @@ static ssize_t lcd_proc_write(struct file *file, const char __user *buf, if (sscanf(cmd, " brightness : %i", &value) == 1 && value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) { - ret = set_lcd(value); + ret = set_lcd(dev, value); if (ret == 0) ret = count; } else { @@ -577,10 +563,11 @@ static const struct file_operations lcd_proc_fops = { static int video_proc_show(struct seq_file *m, void *v) { + struct toshiba_acpi_dev *dev = m->private; u32 hci_result; u32 value; - hci_read1(HCI_VIDEO_OUT, &value, &hci_result); + hci_read1(dev, HCI_VIDEO_OUT, &value, &hci_result); if (hci_result == HCI_SUCCESS) { int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0; int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0; @@ -597,12 +584,13 @@ static int video_proc_show(struct seq_file *m, void *v) static int video_proc_open(struct inode *inode, struct file *file) { - return single_open(file, video_proc_show, NULL); + return single_open(file, video_proc_show, PDE(inode)->data); } static ssize_t video_proc_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) { + struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data; char *cmd, *buffer; int value; int remain = count; @@ -644,7 +632,7 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf, kfree(cmd); - hci_read1(HCI_VIDEO_OUT, &video_out, &hci_result); + hci_read1(dev, HCI_VIDEO_OUT, &video_out, &hci_result); if (hci_result == HCI_SUCCESS) { unsigned int new_video_out = video_out; if (lcd_out != -1) @@ -675,13 +663,14 @@ static const struct file_operations video_proc_fops = { static int fan_proc_show(struct seq_file *m, void *v) { + struct toshiba_acpi_dev *dev = m->private; u32 hci_result; u32 value; - hci_read1(HCI_FAN, &value, &hci_result); + hci_read1(dev, HCI_FAN, &value, &hci_result); if (hci_result == HCI_SUCCESS) { seq_printf(m, "running: %d\n", (value > 0)); - seq_printf(m, "force_on: %d\n", force_fan); + seq_printf(m, "force_on: %d\n", dev->force_fan); } else { pr_err("Error reading fan status\n"); } @@ -691,12 +680,13 @@ static int fan_proc_show(struct seq_file *m, void *v) static int fan_proc_open(struct inode *inode, struct file *file) { - return single_open(file, fan_proc_show, NULL); + return single_open(file, fan_proc_show, PDE(inode)->data); } static ssize_t fan_proc_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) { + struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data; char cmd[42]; size_t len; int value; @@ -709,11 +699,11 @@ static ssize_t fan_proc_write(struct file *file, const char __user *buf, if (sscanf(cmd, " force_on : %i", &value) == 1 && value >= 0 && value <= 1) { - hci_write1(HCI_FAN, value, &hci_result); + hci_write1(dev, HCI_FAN, value, &hci_result); if (hci_result != HCI_SUCCESS) return -EFAULT; else - force_fan = value; + dev->force_fan = value; } else { return -EINVAL; } @@ -732,21 +722,22 @@ static const struct file_operations fan_proc_fops = { static int keys_proc_show(struct seq_file *m, void *v) { + struct toshiba_acpi_dev *dev = m->private; u32 hci_result; u32 value; - if (!key_event_valid) { - hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result); + if (!dev->key_event_valid) { + hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result); if (hci_result == HCI_SUCCESS) { - key_event_valid = 1; - last_key_event = value; + dev->key_event_valid = 1; + dev->last_key_event = value; } else if (hci_result == HCI_EMPTY) { /* better luck next time */ } else if (hci_result == HCI_NOT_SUPPORTED) { /* This is a workaround for an unresolved issue on * some machines where system events sporadically * become disabled. */ - hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result); + hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); pr_notice("Re-enabled hotkeys\n"); } else { pr_err("Error reading hotkey status\n"); @@ -754,20 +745,21 @@ static int keys_proc_show(struct seq_file *m, void *v) } } - seq_printf(m, "hotkey_ready: %d\n", key_event_valid); - seq_printf(m, "hotkey: 0x%04x\n", last_key_event); + seq_printf(m, "hotkey_ready: %d\n", dev->key_event_valid); + seq_printf(m, "hotkey: 0x%04x\n", dev->last_key_event); end: return 0; } static int keys_proc_open(struct inode *inode, struct file *file) { - return single_open(file, keys_proc_show, NULL); + return single_open(file, keys_proc_show, PDE(inode)->data); } static ssize_t keys_proc_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) { + struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data; char cmd[42]; size_t len; int value; @@ -778,7 +770,7 @@ static ssize_t keys_proc_write(struct file *file, const char __user *buf, cmd[len] = '\0'; if (sscanf(cmd, " hotkey_ready : %i", &value) == 1 && value == 0) { - key_event_valid = 0; + dev->key_event_valid = 0; } else { return -EINVAL; } @@ -820,13 +812,19 @@ static const struct file_operations version_proc_fops = { #define PROC_TOSHIBA "toshiba" -static void __init create_toshiba_proc_entries(void) +static void __devinit +create_toshiba_proc_entries(struct toshiba_acpi_dev *dev) { - proc_create("lcd", S_IRUGO | S_IWUSR, toshiba_proc_dir, &lcd_proc_fops); - proc_create("video", S_IRUGO | S_IWUSR, toshiba_proc_dir, &video_proc_fops); - proc_create("fan", S_IRUGO | S_IWUSR, toshiba_proc_dir, &fan_proc_fops); - proc_create("keys", S_IRUGO | S_IWUSR, toshiba_proc_dir, &keys_proc_fops); - proc_create("version", S_IRUGO, toshiba_proc_dir, &version_proc_fops); + proc_create_data("lcd", S_IRUGO | S_IWUSR, toshiba_proc_dir, + &lcd_proc_fops, dev); + proc_create_data("video", S_IRUGO | S_IWUSR, toshiba_proc_dir, + &video_proc_fops, dev); + proc_create_data("fan", S_IRUGO | S_IWUSR, toshiba_proc_dir, + &fan_proc_fops, dev); + proc_create_data("keys", S_IRUGO | S_IWUSR, toshiba_proc_dir, + &keys_proc_fops, dev); + proc_create_data("version", S_IRUGO, toshiba_proc_dir, + &version_proc_fops, dev); } static void remove_toshiba_proc_entries(void) @@ -843,14 +841,183 @@ static const struct backlight_ops toshiba_backlight_data = { .update_status = set_lcd_status, }; -static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context) +static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev, + char *device_path) { + acpi_status status; + int error; + + status = acpi_get_handle(NULL, device_path, &dev->handle); + if (ACPI_FAILURE(status)) { + pr_info("Unable to get notification device\n"); + return -ENODEV; + } + + dev->hotkey_dev = input_allocate_device(); + if (!dev->hotkey_dev) { + pr_info("Unable to register input device\n"); + return -ENOMEM; + } + + dev->hotkey_dev->name = "Toshiba input device"; + dev->hotkey_dev->phys = device_path; + dev->hotkey_dev->id.bustype = BUS_HOST; + + error = sparse_keymap_setup(dev->hotkey_dev, toshiba_acpi_keymap, NULL); + if (error) + goto err_free_dev; + + status = acpi_evaluate_object(dev->handle, "ENAB", NULL, NULL); + if (ACPI_FAILURE(status)) { + pr_info("Unable to enable hotkeys\n"); + error = -ENODEV; + goto err_free_keymap; + } + + error = input_register_device(dev->hotkey_dev); + if (error) { + pr_info("Unable to register input device\n"); + goto err_free_keymap; + } + + return 0; + + err_free_keymap: + sparse_keymap_free(dev->hotkey_dev); + err_free_dev: + input_free_device(dev->hotkey_dev); + dev->hotkey_dev = NULL; + return error; +} + +static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type) +{ + struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); + + remove_toshiba_proc_entries(); + + if (dev->hotkey_dev) { + input_unregister_device(dev->hotkey_dev); + sparse_keymap_free(dev->hotkey_dev); + } + + if (dev->bt_rfk) { + rfkill_unregister(dev->bt_rfk); + rfkill_destroy(dev->bt_rfk); + } + + if (dev->backlight_dev) + backlight_device_unregister(dev->backlight_dev); + + if (dev->illumination_installed) + led_classdev_unregister(&dev->led_dev); + + kfree(dev); + + return 0; +} + +static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) +{ + struct toshiba_acpi_dev *dev; + u32 hci_result; + bool bt_present; + int ret = 0; + struct backlight_properties props; + + pr_info("Toshiba Laptop ACPI Extras version %s\n", + TOSHIBA_ACPI_VERSION); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + dev->acpi_dev = acpi_dev; + acpi_dev->driver_data = dev; + + /* simple device detection: look for HCI method */ + if (is_valid_acpi_path(TOSH_INTERFACE_1 GHCI_METHOD)) { + dev->method_hci = TOSH_INTERFACE_1 GHCI_METHOD; + if (toshiba_acpi_setup_keyboard(dev, TOSH_INTERFACE_1)) + pr_info("Unable to activate hotkeys\n"); + } else if (is_valid_acpi_path(TOSH_INTERFACE_2 GHCI_METHOD)) { + dev->method_hci = TOSH_INTERFACE_2 GHCI_METHOD; + if (toshiba_acpi_setup_keyboard(dev, TOSH_INTERFACE_2)) + pr_info("Unable to activate hotkeys\n"); + } else { + ret = -ENODEV; + goto error; + } + + pr_info("HCI method: %s\n", dev->method_hci); + + mutex_init(&dev->mutex); + + /* enable event fifo */ + hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); + + create_toshiba_proc_entries(dev); + + props.type = BACKLIGHT_PLATFORM; + props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; + dev->backlight_dev = backlight_device_register("toshiba", + &acpi_dev->dev, + dev, + &toshiba_backlight_data, + &props); + if (IS_ERR(dev->backlight_dev)) { + ret = PTR_ERR(dev->backlight_dev); + + pr_err("Could not register toshiba backlight device\n"); + dev->backlight_dev = NULL; + goto error; + } + + /* Register rfkill switch for Bluetooth */ + if (hci_get_bt_present(dev, &bt_present) == HCI_SUCCESS && bt_present) { + dev->bt_rfk = rfkill_alloc("Toshiba Bluetooth", + &acpi_dev->dev, + RFKILL_TYPE_BLUETOOTH, + &toshiba_rfk_ops, + dev); + if (!dev->bt_rfk) { + pr_err("unable to allocate rfkill device\n"); + ret = -ENOMEM; + goto error; + } + + ret = rfkill_register(dev->bt_rfk); + if (ret) { + pr_err("unable to register rfkill device\n"); + rfkill_destroy(dev->bt_rfk); + goto error; + } + } + + if (toshiba_illumination_available(dev)) { + dev->led_dev.name = "toshiba::illumination"; + dev->led_dev.max_brightness = 1; + dev->led_dev.brightness_set = toshiba_illumination_set; + dev->led_dev.brightness_get = toshiba_illumination_get; + if (!led_classdev_register(&acpi_dev->dev, &dev->led_dev)) + dev->illumination_installed = 1; + } + + return 0; + +error: + toshiba_acpi_remove(acpi_dev, 0); + return ret; +} + +static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event) +{ + struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); u32 hci_result, value; if (event != 0x80) return; do { - hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result); + hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result); if (hci_result == HCI_SUCCESS) { if (value == 0x100) continue; @@ -858,7 +1025,7 @@ static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context) if (value & 0x80) continue; - if (!sparse_keymap_report_event(toshiba_acpi.hotkey_dev, + if (!sparse_keymap_report_event(dev->hotkey_dev, value, 1, true)) { pr_info("Unknown key %x\n", value); @@ -867,200 +1034,49 @@ static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context) /* This is a workaround for an unresolved issue on * some machines where system events sporadically * become disabled. */ - hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result); + hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); pr_notice("Re-enabled hotkeys\n"); } } while (hci_result != HCI_EMPTY); } -static int __init toshiba_acpi_setup_keyboard(char *device) -{ - acpi_status status; - int error; - status = acpi_get_handle(NULL, device, &toshiba_acpi.handle); - if (ACPI_FAILURE(status)) { - pr_info("Unable to get notification device\n"); - return -ENODEV; - } - - toshiba_acpi.hotkey_dev = input_allocate_device(); - if (!toshiba_acpi.hotkey_dev) { - pr_info("Unable to register input device\n"); - return -ENOMEM; - } - - toshiba_acpi.hotkey_dev->name = "Toshiba input device"; - toshiba_acpi.hotkey_dev->phys = device; - toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST; - - error = sparse_keymap_setup(toshiba_acpi.hotkey_dev, - toshiba_acpi_keymap, NULL); - if (error) - goto err_free_dev; - - status = acpi_install_notify_handler(toshiba_acpi.handle, - ACPI_DEVICE_NOTIFY, toshiba_acpi_notify, NULL); - if (ACPI_FAILURE(status)) { - pr_info("Unable to install hotkey notification\n"); - error = -ENODEV; - goto err_free_keymap; - } - - status = acpi_evaluate_object(toshiba_acpi.handle, "ENAB", NULL, NULL); - if (ACPI_FAILURE(status)) { - pr_info("Unable to enable hotkeys\n"); - error = -ENODEV; - goto err_remove_notify; - } - - error = input_register_device(toshiba_acpi.hotkey_dev); - if (error) { - pr_info("Unable to register input device\n"); - goto err_remove_notify; - } - - return 0; - - err_remove_notify: - acpi_remove_notify_handler(toshiba_acpi.handle, - ACPI_DEVICE_NOTIFY, toshiba_acpi_notify); - err_free_keymap: - sparse_keymap_free(toshiba_acpi.hotkey_dev); - err_free_dev: - input_free_device(toshiba_acpi.hotkey_dev); - toshiba_acpi.hotkey_dev = NULL; - return error; -} - -static void toshiba_acpi_exit(void) -{ - if (toshiba_acpi.hotkey_dev) { - acpi_remove_notify_handler(toshiba_acpi.handle, - ACPI_DEVICE_NOTIFY, toshiba_acpi_notify); - sparse_keymap_free(toshiba_acpi.hotkey_dev); - input_unregister_device(toshiba_acpi.hotkey_dev); - } - - if (toshiba_acpi.bt_rfk) { - rfkill_unregister(toshiba_acpi.bt_rfk); - rfkill_destroy(toshiba_acpi.bt_rfk); - } - - if (toshiba_backlight_device) - backlight_device_unregister(toshiba_backlight_device); - - remove_toshiba_proc_entries(); - - if (toshiba_proc_dir) - remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); - - if (toshiba_acpi.illumination_installed) - led_classdev_unregister(&toshiba_led); - - platform_device_unregister(toshiba_acpi.p_dev); - - return; -} +static struct acpi_driver toshiba_acpi_driver = { + .name = "Toshiba ACPI driver", + .owner = THIS_MODULE, + .ids = toshiba_device_ids, + .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, + .ops = { + .add = toshiba_acpi_add, + .remove = toshiba_acpi_remove, + .notify = toshiba_acpi_notify, + }, +}; static int __init toshiba_acpi_init(void) { - u32 hci_result; - bool bt_present; - int ret = 0; - struct backlight_properties props; - - if (acpi_disabled) - return -ENODEV; - - /* simple device detection: look for HCI method */ - if (is_valid_acpi_path(TOSH_INTERFACE_1 GHCI_METHOD)) { - method_hci = TOSH_INTERFACE_1 GHCI_METHOD; - if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_1)) - pr_info("Unable to activate hotkeys\n"); - } else if (is_valid_acpi_path(TOSH_INTERFACE_2 GHCI_METHOD)) { - method_hci = TOSH_INTERFACE_2 GHCI_METHOD; - if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_2)) - pr_info("Unable to activate hotkeys\n"); - } else - return -ENODEV; - - pr_info("Toshiba Laptop ACPI Extras version %s\n", - TOSHIBA_ACPI_VERSION); - pr_info(" HCI method: %s\n", method_hci); - - mutex_init(&toshiba_acpi.mutex); - - toshiba_acpi.p_dev = platform_device_register_simple("toshiba_acpi", - -1, NULL, 0); - if (IS_ERR(toshiba_acpi.p_dev)) { - ret = PTR_ERR(toshiba_acpi.p_dev); - pr_err("unable to register platform device\n"); - toshiba_acpi.p_dev = NULL; - toshiba_acpi_exit(); - return ret; - } - - force_fan = 0; - key_event_valid = 0; - - /* enable event fifo */ - hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result); + int ret; toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir); if (!toshiba_proc_dir) { - toshiba_acpi_exit(); + pr_err("Unable to create proc dir " PROC_TOSHIBA "\n"); return -ENODEV; - } else { - create_toshiba_proc_entries(); } - props.type = BACKLIGHT_PLATFORM; - props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; - toshiba_backlight_device = backlight_device_register("toshiba", - &toshiba_acpi.p_dev->dev, - NULL, - &toshiba_backlight_data, - &props); - if (IS_ERR(toshiba_backlight_device)) { - ret = PTR_ERR(toshiba_backlight_device); - - pr_err("Could not register toshiba backlight device\n"); - toshiba_backlight_device = NULL; - toshiba_acpi_exit(); - return ret; + ret = acpi_bus_register_driver(&toshiba_acpi_driver); + if (ret) { + pr_err("Failed to register ACPI driver: %d\n", ret); + remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); } - /* Register rfkill switch for Bluetooth */ - if (hci_get_bt_present(&bt_present) == HCI_SUCCESS && bt_present) { - toshiba_acpi.bt_rfk = rfkill_alloc(toshiba_acpi.bt_name, - &toshiba_acpi.p_dev->dev, - RFKILL_TYPE_BLUETOOTH, - &toshiba_rfk_ops, - &toshiba_acpi); - if (!toshiba_acpi.bt_rfk) { - pr_err("unable to allocate rfkill device\n"); - toshiba_acpi_exit(); - return -ENOMEM; - } + return ret; +} - ret = rfkill_register(toshiba_acpi.bt_rfk); - if (ret) { - pr_err("unable to register rfkill device\n"); - rfkill_destroy(toshiba_acpi.bt_rfk); - toshiba_acpi_exit(); - return ret; - } - } - - toshiba_acpi.illumination_installed = 0; - if (toshiba_illumination_available()) { - if (!led_classdev_register(&(toshiba_acpi.p_dev->dev), - &toshiba_led)) - toshiba_acpi.illumination_installed = 1; - } - - return 0; +static void __exit toshiba_acpi_exit(void) +{ + acpi_bus_unregister_driver(&toshiba_acpi_driver); + if (toshiba_proc_dir) + remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); } module_init(toshiba_acpi_init); From 32bcd5cba02436336053d9c3b7a8ff86f889ebf4 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Tue, 20 Sep 2011 16:55:50 -0500 Subject: [PATCH 08/45] toshiba_acpi: Fix up return codes Many routines are returning success on failure, and those that are indicating failure frequently return incorrect error codes. Fix these up throughout the driver. Signed-off-by: Seth Forshee Signed-off-by: Matthew Garrett --- drivers/platform/x86/toshiba_acpi.c | 45 +++++++++++++---------------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index d74c97cde0f1..6b281c040bc0 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -191,7 +191,7 @@ static int write_acpi_int(const char *methodName, int val) in_objs[0].integer.value = val; status = acpi_evaluate_object(NULL, (char *)methodName, ¶ms, NULL); - return (status == AE_OK); + return (status == AE_OK) ? 0 : -EIO; } /* Perform a raw HCI call. Here we don't care about input or output buffer @@ -419,7 +419,7 @@ static int bt_rfkill_set_block(void *data, bool blocked) mutex_lock(&dev->mutex); if (hci_get_radio_state(dev, &radio_state) != HCI_SUCCESS) { - err = -EBUSY; + err = -EIO; goto out; } @@ -432,7 +432,7 @@ static int bt_rfkill_set_block(void *data, bool blocked) hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2); if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS) - err = -EBUSY; + err = -EIO; else err = 0; out: @@ -478,10 +478,10 @@ static int get_lcd(struct backlight_device *bd) u32 value; hci_read1(dev, HCI_LCD_BRIGHTNESS, &value, &hci_result); - if (hci_result == HCI_SUCCESS) { + if (hci_result == HCI_SUCCESS) return (value >> HCI_LCD_BRIGHTNESS_SHIFT); - } else - return -EFAULT; + + return -EIO; } static int lcd_proc_show(struct seq_file *m, void *v) @@ -497,11 +497,11 @@ static int lcd_proc_show(struct seq_file *m, void *v) seq_printf(m, "brightness: %d\n", value); seq_printf(m, "brightness_levels: %d\n", HCI_LCD_BRIGHTNESS_LEVELS); - } else { - pr_err("Error reading LCD brightness\n"); + return 0; } - return 0; + pr_err("Error reading LCD brightness\n"); + return -EIO; } static int lcd_proc_open(struct inode *inode, struct file *file) @@ -515,10 +515,7 @@ static int set_lcd(struct toshiba_acpi_dev *dev, int value) value = value << HCI_LCD_BRIGHTNESS_SHIFT; hci_write1(dev, HCI_LCD_BRIGHTNESS, value, &hci_result); - if (hci_result != HCI_SUCCESS) - return -EFAULT; - - return 0; + return hci_result == HCI_SUCCESS ? 0 : -EIO; } static int set_lcd_status(struct backlight_device *bd) @@ -575,11 +572,10 @@ static int video_proc_show(struct seq_file *m, void *v) seq_printf(m, "lcd_out: %d\n", is_lcd); seq_printf(m, "crt_out: %d\n", is_crt); seq_printf(m, "tv_out: %d\n", is_tv); - } else { - pr_err("Error reading video out status\n"); + return 0; } - return 0; + return -EIO; } static int video_proc_open(struct inode *inode, struct file *file) @@ -592,6 +588,7 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf, { struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data; char *cmd, *buffer; + int ret = 0; int value; int remain = count; int lcd_out = -1; @@ -644,12 +641,12 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf, /* To avoid unnecessary video disruption, only write the new * video setting if something changed. */ if (new_video_out != video_out) - write_acpi_int(METHOD_VIDEO_OUT, new_video_out); + ret = write_acpi_int(METHOD_VIDEO_OUT, new_video_out); } else { - return -EFAULT; + ret = -EIO; } - return count; + return ret ? ret : count; } static const struct file_operations video_proc_fops = { @@ -671,11 +668,10 @@ static int fan_proc_show(struct seq_file *m, void *v) if (hci_result == HCI_SUCCESS) { seq_printf(m, "running: %d\n", (value > 0)); seq_printf(m, "force_on: %d\n", dev->force_fan); - } else { - pr_err("Error reading fan status\n"); + return 0; } - return 0; + return -EIO; } static int fan_proc_open(struct inode *inode, struct file *file) @@ -701,7 +697,7 @@ static ssize_t fan_proc_write(struct file *file, const char __user *buf, value >= 0 && value <= 1) { hci_write1(dev, HCI_FAN, value, &hci_result); if (hci_result != HCI_SUCCESS) - return -EFAULT; + return -EIO; else dev->force_fan = value; } else { @@ -741,13 +737,12 @@ static int keys_proc_show(struct seq_file *m, void *v) pr_notice("Re-enabled hotkeys\n"); } else { pr_err("Error reading hotkey status\n"); - goto end; + return -EIO; } } seq_printf(m, "hotkey_ready: %d\n", dev->key_event_valid); seq_printf(m, "hotkey: 0x%04x\n", dev->last_key_event); -end: return 0; } From 6e02cc7eb61aeafadb91f7b591a768cdb7a57740 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Tue, 20 Sep 2011 16:55:51 -0500 Subject: [PATCH 09/45] toshiba_acpi: Use handle for HCI calls Now that we're using an acpi driver we already have a handle to the namespace of the HCI call, so there's no need to test various paths to the HCI call or even be aware of the path at all. Signed-off-by: Azael Avalos Signed-off-by: Seth Forshee Signed-off-by: Matthew Garrett --- drivers/platform/x86/toshiba_acpi.c | 55 ++++++++--------------------- 1 file changed, 15 insertions(+), 40 deletions(-) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 6b281c040bc0..a924c7f77220 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -62,11 +62,7 @@ MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver"); MODULE_LICENSE("GPL"); /* Toshiba ACPI method paths */ -#define METHOD_LCD_BRIGHTNESS "\\_SB_.PCI0.VGA_.LCD_._BCM" -#define TOSH_INTERFACE_1 "\\_SB_.VALD" -#define TOSH_INTERFACE_2 "\\_SB_.VALZ" #define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX" -#define GHCI_METHOD ".GHCI" /* Toshiba HCI interface definitions * @@ -121,7 +117,6 @@ struct toshiba_acpi_dev { int force_fan; int last_key_event; int key_event_valid; - acpi_handle handle; struct mutex mutex; }; @@ -170,15 +165,6 @@ static __inline__ void _set_bit(u32 * word, u32 mask, int value) /* acpi interface wrappers */ -static int is_valid_acpi_path(const char *methodName) -{ - acpi_handle handle; - acpi_status status; - - status = acpi_get_handle(NULL, (char *)methodName, &handle); - return !ACPI_FAILURE(status); -} - static int write_acpi_int(const char *methodName, int val) { struct acpi_object_list params; @@ -217,7 +203,8 @@ static acpi_status hci_raw(struct toshiba_acpi_dev *dev, results.length = sizeof(out_objs); results.pointer = out_objs; - status = acpi_evaluate_object(NULL, (char *)dev->method_hci, ¶ms, + status = acpi_evaluate_object(dev->acpi_dev->handle, + (char *)dev->method_hci, ¶ms, &results); if ((status == AE_OK) && (out_objs->package.count <= HCI_WORDS)) { for (i = 0; i < out_objs->package.count; ++i) { @@ -836,18 +823,11 @@ static const struct backlight_ops toshiba_backlight_data = { .update_status = set_lcd_status, }; -static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev, - char *device_path) +static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) { acpi_status status; int error; - status = acpi_get_handle(NULL, device_path, &dev->handle); - if (ACPI_FAILURE(status)) { - pr_info("Unable to get notification device\n"); - return -ENODEV; - } - dev->hotkey_dev = input_allocate_device(); if (!dev->hotkey_dev) { pr_info("Unable to register input device\n"); @@ -855,14 +835,14 @@ static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev, } dev->hotkey_dev->name = "Toshiba input device"; - dev->hotkey_dev->phys = device_path; + dev->hotkey_dev->phys = "toshiba_acpi/input0"; dev->hotkey_dev->id.bustype = BUS_HOST; error = sparse_keymap_setup(dev->hotkey_dev, toshiba_acpi_keymap, NULL); if (error) goto err_free_dev; - status = acpi_evaluate_object(dev->handle, "ENAB", NULL, NULL); + status = acpi_evaluate_object(dev->acpi_dev->handle, "ENAB", NULL, NULL); if (ACPI_FAILURE(status)) { pr_info("Unable to enable hotkeys\n"); error = -ENODEV; @@ -915,6 +895,8 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type) static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) { struct toshiba_acpi_dev *dev; + acpi_status status; + acpi_handle handle; u32 hci_result; bool bt_present; int ret = 0; @@ -923,27 +905,20 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) pr_info("Toshiba Laptop ACPI Extras version %s\n", TOSHIBA_ACPI_VERSION); + /* simple device detection: look for HCI method */ + status = acpi_get_handle(acpi_dev->handle, "GHCI", &handle); + if (ACPI_FAILURE(status)) + return -ENODEV; + dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; dev->acpi_dev = acpi_dev; + dev->method_hci = "GHCI"; acpi_dev->driver_data = dev; - /* simple device detection: look for HCI method */ - if (is_valid_acpi_path(TOSH_INTERFACE_1 GHCI_METHOD)) { - dev->method_hci = TOSH_INTERFACE_1 GHCI_METHOD; - if (toshiba_acpi_setup_keyboard(dev, TOSH_INTERFACE_1)) - pr_info("Unable to activate hotkeys\n"); - } else if (is_valid_acpi_path(TOSH_INTERFACE_2 GHCI_METHOD)) { - dev->method_hci = TOSH_INTERFACE_2 GHCI_METHOD; - if (toshiba_acpi_setup_keyboard(dev, TOSH_INTERFACE_2)) - pr_info("Unable to activate hotkeys\n"); - } else { - ret = -ENODEV; - goto error; - } - - pr_info("HCI method: %s\n", dev->method_hci); + if (toshiba_acpi_setup_keyboard(dev)) + pr_info("Unable to activate hotkeys\n"); mutex_init(&dev->mutex); From a540d6b5b577f5a320d873a9cc8778ff20bf5ddf Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Tue, 20 Sep 2011 16:55:52 -0500 Subject: [PATCH 10/45] toshiba_acpi: Support SPFC as an HCI method Some Toshiba models, notably those with the TOS1900 device, use the SPFC method for HCI calls instead of GHCI. Test for this method if GHCI isn't found, and if it exists use it for all HCI calls. Signed-off-by: Azael Avalos Signed-off-by: Seth Forshee Signed-off-by: Matthew Garrett --- drivers/platform/x86/toshiba_acpi.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index a924c7f77220..cc629e621e46 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -892,11 +892,26 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type) return 0; } +static const char * __devinit find_hci_method(acpi_handle handle) +{ + acpi_status status; + acpi_handle hci_handle; + + status = acpi_get_handle(handle, "GHCI", &hci_handle); + if (ACPI_SUCCESS(status)) + return "GHCI"; + + status = acpi_get_handle(handle, "SPFC", &hci_handle); + if (ACPI_SUCCESS(status)) + return "SPFC"; + + return NULL; +} + static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) { struct toshiba_acpi_dev *dev; - acpi_status status; - acpi_handle handle; + const char *hci_method; u32 hci_result; bool bt_present; int ret = 0; @@ -905,16 +920,17 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) pr_info("Toshiba Laptop ACPI Extras version %s\n", TOSHIBA_ACPI_VERSION); - /* simple device detection: look for HCI method */ - status = acpi_get_handle(acpi_dev->handle, "GHCI", &handle); - if (ACPI_FAILURE(status)) + hci_method = find_hci_method(acpi_dev->handle); + if (!hci_method) { + pr_err("HCI interface not found\n"); return -ENODEV; + } dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; dev->acpi_dev = acpi_dev; - dev->method_hci = "GHCI"; + dev->method_hci = hci_method; acpi_dev->driver_data = dev; if (toshiba_acpi_setup_keyboard(dev)) From 36d03f93558c66527e5dbf602cb2eb4ccbd899a6 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Tue, 20 Sep 2011 16:55:53 -0500 Subject: [PATCH 11/45] toshiba_acpi: Don't add devices for unsupported features Test for features up-front to determine whether or not they are supported, and avoid creating devices and proc files for unsupported features. Signed-off-by: Seth Forshee Signed-off-by: Matthew Garrett --- drivers/platform/x86/toshiba_acpi.c | 102 ++++++++++++++++++---------- 1 file changed, 67 insertions(+), 35 deletions(-) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index cc629e621e46..c51a64c49559 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -113,11 +113,15 @@ struct toshiba_acpi_dev { struct input_dev *hotkey_dev; struct backlight_device *backlight_dev; struct led_classdev led_dev; - int illumination_installed; + int force_fan; int last_key_event; int key_event_valid; + int illumination_supported:1; + int video_supported:1; + int fan_supported:1; + struct mutex mutex; }; @@ -545,24 +549,31 @@ static const struct file_operations lcd_proc_fops = { .write = lcd_proc_write, }; +static int get_video_status(struct toshiba_acpi_dev *dev, u32 *status) +{ + u32 hci_result; + + hci_read1(dev, HCI_VIDEO_OUT, status, &hci_result); + return hci_result == HCI_SUCCESS ? 0 : -EIO; +} + static int video_proc_show(struct seq_file *m, void *v) { struct toshiba_acpi_dev *dev = m->private; - u32 hci_result; u32 value; + int ret; - hci_read1(dev, HCI_VIDEO_OUT, &value, &hci_result); - if (hci_result == HCI_SUCCESS) { + ret = get_video_status(dev, &value); + if (!ret) { int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0; int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0; int is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0; seq_printf(m, "lcd_out: %d\n", is_lcd); seq_printf(m, "crt_out: %d\n", is_crt); seq_printf(m, "tv_out: %d\n", is_tv); - return 0; } - return -EIO; + return ret; } static int video_proc_open(struct inode *inode, struct file *file) @@ -575,13 +586,12 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf, { struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data; char *cmd, *buffer; - int ret = 0; + int ret; int value; int remain = count; int lcd_out = -1; int crt_out = -1; int tv_out = -1; - u32 hci_result; u32 video_out; cmd = kmalloc(count + 1, GFP_KERNEL); @@ -616,8 +626,8 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf, kfree(cmd); - hci_read1(dev, HCI_VIDEO_OUT, &video_out, &hci_result); - if (hci_result == HCI_SUCCESS) { + ret = get_video_status(dev, &video_out); + if (!ret) { unsigned int new_video_out = video_out; if (lcd_out != -1) _set_bit(&new_video_out, HCI_VIDEO_OUT_LCD, lcd_out); @@ -629,8 +639,6 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf, * video setting if something changed. */ if (new_video_out != video_out) ret = write_acpi_int(METHOD_VIDEO_OUT, new_video_out); - } else { - ret = -EIO; } return ret ? ret : count; @@ -645,20 +653,27 @@ static const struct file_operations video_proc_fops = { .write = video_proc_write, }; +static int get_fan_status(struct toshiba_acpi_dev *dev, u32 *status) +{ + u32 hci_result; + + hci_read1(dev, HCI_FAN, status, &hci_result); + return hci_result == HCI_SUCCESS ? 0 : -EIO; +} + static int fan_proc_show(struct seq_file *m, void *v) { struct toshiba_acpi_dev *dev = m->private; - u32 hci_result; + int ret; u32 value; - hci_read1(dev, HCI_FAN, &value, &hci_result); - if (hci_result == HCI_SUCCESS) { + ret = get_fan_status(dev, &value); + if (!ret) { seq_printf(m, "running: %d\n", (value > 0)); seq_printf(m, "force_on: %d\n", dev->force_fan); - return 0; } - return -EIO; + return ret; } static int fan_proc_open(struct inode *inode, struct file *file) @@ -797,24 +812,32 @@ static const struct file_operations version_proc_fops = { static void __devinit create_toshiba_proc_entries(struct toshiba_acpi_dev *dev) { - proc_create_data("lcd", S_IRUGO | S_IWUSR, toshiba_proc_dir, - &lcd_proc_fops, dev); - proc_create_data("video", S_IRUGO | S_IWUSR, toshiba_proc_dir, - &video_proc_fops, dev); - proc_create_data("fan", S_IRUGO | S_IWUSR, toshiba_proc_dir, - &fan_proc_fops, dev); - proc_create_data("keys", S_IRUGO | S_IWUSR, toshiba_proc_dir, - &keys_proc_fops, dev); + if (dev->backlight_dev) + proc_create_data("lcd", S_IRUGO | S_IWUSR, toshiba_proc_dir, + &lcd_proc_fops, dev); + if (dev->video_supported) + proc_create_data("video", S_IRUGO | S_IWUSR, toshiba_proc_dir, + &video_proc_fops, dev); + if (dev->fan_supported) + proc_create_data("fan", S_IRUGO | S_IWUSR, toshiba_proc_dir, + &fan_proc_fops, dev); + if (dev->hotkey_dev) + proc_create_data("keys", S_IRUGO | S_IWUSR, toshiba_proc_dir, + &keys_proc_fops, dev); proc_create_data("version", S_IRUGO, toshiba_proc_dir, &version_proc_fops, dev); } -static void remove_toshiba_proc_entries(void) +static void remove_toshiba_proc_entries(struct toshiba_acpi_dev *dev) { - remove_proc_entry("lcd", toshiba_proc_dir); - remove_proc_entry("video", toshiba_proc_dir); - remove_proc_entry("fan", toshiba_proc_dir); - remove_proc_entry("keys", toshiba_proc_dir); + if (dev->backlight_dev) + remove_proc_entry("lcd", toshiba_proc_dir); + if (dev->video_supported) + remove_proc_entry("video", toshiba_proc_dir); + if (dev->fan_supported) + remove_proc_entry("fan", toshiba_proc_dir); + if (dev->hotkey_dev) + remove_proc_entry("keys", toshiba_proc_dir); remove_proc_entry("version", toshiba_proc_dir); } @@ -869,7 +892,7 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type) { struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); - remove_toshiba_proc_entries(); + remove_toshiba_proc_entries(dev); if (dev->hotkey_dev) { input_unregister_device(dev->hotkey_dev); @@ -884,7 +907,7 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type) if (dev->backlight_dev) backlight_device_unregister(dev->backlight_dev); - if (dev->illumination_installed) + if (dev->illumination_supported) led_classdev_unregister(&dev->led_dev); kfree(dev); @@ -913,6 +936,7 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) struct toshiba_acpi_dev *dev; const char *hci_method; u32 hci_result; + u32 dummy; bool bt_present; int ret = 0; struct backlight_properties props; @@ -941,8 +965,6 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) /* enable event fifo */ hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); - create_toshiba_proc_entries(dev); - props.type = BACKLIGHT_PLATFORM; props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; dev->backlight_dev = backlight_device_register("toshiba", @@ -985,9 +1007,19 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) dev->led_dev.brightness_set = toshiba_illumination_set; dev->led_dev.brightness_get = toshiba_illumination_get; if (!led_classdev_register(&acpi_dev->dev, &dev->led_dev)) - dev->illumination_installed = 1; + dev->illumination_supported = 1; } + /* Determine whether or not BIOS supports fan and video interfaces */ + + ret = get_video_status(dev, &dummy); + dev->video_supported = !ret; + + ret = get_fan_status(dev, &dummy); + dev->fan_supported = !ret; + + create_toshiba_proc_entries(dev); + return 0; error: From ac2dad886d9fae561fe53d693e1b6c33fb0ad674 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Tue, 20 Sep 2011 16:55:54 -0500 Subject: [PATCH 12/45] toshiba_acpi: Initialize brightness in backlight device Signed-off-by: Seth Forshee Signed-off-by: Matthew Garrett --- drivers/platform/x86/toshiba_acpi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index c51a64c49559..13ef8c37471d 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -979,6 +979,7 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) dev->backlight_dev = NULL; goto error; } + dev->backlight_dev->props.brightness = get_lcd(dev->backlight_dev); /* Register rfkill switch for Bluetooth */ if (hci_get_bt_present(dev, &bt_present) == HCI_SUCCESS && bt_present) { From bd038080c70b70a5b31c48af774fada36eacc834 Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Mon, 26 Sep 2011 15:04:54 +0200 Subject: [PATCH 13/45] topstar-latop: ignore 0x82 event Event 0x82 is emitted whenever a backlight event happens (brightness change, backlight on/off). For instance, when display is switched off after a timeout, event 0x82 is emitted, and along with it a keypress which switches the display back on again. This patch adds 0x82 to the event ignore list so that the display stays off when it is automatically switched off. Signed-off-by: Manuel Lauss Signed-off-by: Matthew Garrett --- drivers/platform/x86/topstar-laptop.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c index 4c20447ddbb7..d528daa0e81c 100644 --- a/drivers/platform/x86/topstar-laptop.c +++ b/drivers/platform/x86/topstar-laptop.c @@ -41,6 +41,7 @@ static const struct key_entry topstar_keymap[] = { { KE_KEY, 0x8c, { KEY_MEDIA } }, /* Known non hotkey events don't handled or that we don't care yet */ + { KE_IGNORE, 0x82, }, /* backlight event */ { KE_IGNORE, 0x8e, }, { KE_IGNORE, 0x8f, }, { KE_IGNORE, 0x90, }, From 2d8b90be4f1cadd9921312e2983459f568d29cd1 Mon Sep 17 00:00:00 2001 From: AceLan Kao Date: Tue, 4 Oct 2011 16:25:44 +0800 Subject: [PATCH 14/45] dell-laptop: support Synaptics/Alps touchpad led This patch supports Dell laptop with Synaptics and Alps touchpad chip that with LED to indicate the functionality of touchpad is disabled or enabled. The command for touchpad LED is 0x97, and the data 1 means turn on the touchpad LED, 2 means turn it off. BTW, I add dell_quirks to white list those machines that supports this behavior, so that the code won't affect those who don't have a touchpad LED machine. We can easily to turn it on/off by echo 1 > /sys/class/leds/dell-laptop::touchpad/brightness echo 0 > /sys/class/leds/dell-laptop::touchpad/brightness Signed-off-by: AceLan Kao Signed-off-by: Matthew Garrett --- drivers/platform/x86/dell-laptop.c | 84 ++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index f31fa4efa725..a43cfd906c6d 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -60,6 +60,22 @@ struct calling_interface_structure { struct calling_interface_token tokens[]; } __packed; +struct quirk_entry { + u8 touchpad_led; +}; + +static struct quirk_entry *quirks; + +static struct quirk_entry quirk_dell_vostro_v130 = { + .touchpad_led = 1, +}; + +static int dmi_matched(const struct dmi_system_id *dmi) +{ + quirks = dmi->driver_data; + return 1; +} + static int da_command_address; static int da_command_code; static int da_num_tokens; @@ -149,6 +165,27 @@ static struct dmi_system_id __devinitdata dell_blacklist[] = { {} }; +static struct dmi_system_id __devinitdata dell_quirks[] = { + { + .callback = dmi_matched, + .ident = "Dell Vostro V130", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V130"), + }, + .driver_data = &quirk_dell_vostro_v130, + }, + { + .callback = dmi_matched, + .ident = "Dell Vostro V131", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"), + }, + .driver_data = &quirk_dell_vostro_v130, + }, +}; + static struct calling_interface_buffer *buffer; static struct page *bufferpage; static DEFINE_MUTEX(buffer_mutex); @@ -552,6 +589,44 @@ static const struct backlight_ops dell_ops = { .update_status = dell_send_intensity, }; +static void touchpad_led_on() +{ + int command = 0x97; + char data = 1; + i8042_command(&data, command | 1 << 12); +} + +static void touchpad_led_off() +{ + int command = 0x97; + char data = 2; + i8042_command(&data, command | 1 << 12); +} + +static void touchpad_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + if (value > 0) + touchpad_led_on(); + else + touchpad_led_off(); +} + +static struct led_classdev touchpad_led = { + .name = "dell-laptop::touchpad", + .brightness_set = touchpad_led_set, +}; + +static int __devinit touchpad_led_init(struct device *dev) +{ + return led_classdev_register(dev, &touchpad_led); +} + +static void touchpad_led_exit(void) +{ + led_classdev_unregister(&touchpad_led); +} + static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str, struct serio *port) { @@ -584,6 +659,10 @@ static int __init dell_init(void) if (!dmi_check_system(dell_device_table)) return -ENODEV; + quirks = NULL; + /* find if this machine support other functions */ + dmi_check_system(dell_quirks); + dmi_walk(find_tokens, NULL); if (!da_tokens) { @@ -626,6 +705,9 @@ static int __init dell_init(void) goto fail_filter; } + if (quirks && quirks->touchpad_led) + touchpad_led_init(&platform_device->dev); + dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL); if (dell_laptop_dir != NULL) debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL, @@ -692,6 +774,8 @@ fail_platform_driver: static void __exit dell_exit(void) { debugfs_remove_recursive(dell_laptop_dir); + if (quirks && quirks->touchpad_led) + touchpad_led_exit(); i8042_remove_filter(dell_laptop_i8042_filter); cancel_delayed_work_sync(&dell_rfkill_work); backlight_device_unregister(dell_backlight_device); From be3128b107e36271f7973ef213ccde603a494fe8 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Thu, 6 Oct 2011 15:01:55 -0500 Subject: [PATCH 15/45] acer-wmi: Add wireless quirk for Lenovo 3000 N200 This quirk fixes the wlan rfkill status on this machine. Without it, wlan is permanently soft blocked whenever acer-wmi is loaded. BugLink: https://bugs.launchpad.net/bugs/857297 Signed-off-by: Seth Forshee Reviewed-by: Lee, Chun-Yi Signed-off-by: Matthew Garrett --- drivers/platform/x86/acer-wmi.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index af2bb20cb2fb..dac286a568ea 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -464,6 +464,15 @@ static struct dmi_system_id acer_quirks[] = { }, .driver_data = &quirk_lenovo_ideapad_s205, }, + { + .callback = dmi_matched, + .ident = "Lenovo 3000 N200", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "0687A31"), + }, + .driver_data = &quirk_fujitsu_amilo_li_1718, + }, {} }; From ba05b237372bad82533d1f717569d1d817ff3c27 Mon Sep 17 00:00:00 2001 From: John Serock Date: Thu, 13 Oct 2011 06:42:01 -0400 Subject: [PATCH 16/45] Platform: Detect samsung laptop quirk when initial level is zero This patch depends on the "Platform: Brightness quirk for samsung laptop driver" patch from Jason Stubbs. This patch adds a check for an initial brightness level of 0; if the level is 0, this patch changes the brightness level to 1 before the driver attempts to detect the brightness quirk. The Samsung N150 netbook experiences the brightness quirk. Without Jason's patch, the only brightness levels available on the N150 are 0, 1, and 8. This patch ensures that, when the initial brightness level is 0, the samsang-laptop driver detects the brightness quirk on the N150, thereby making brightness levels 0 through 8 available. Signed-off-by: John Serock Acked-by: Jason Stubbs Signed-off-by: Matthew Garrett --- drivers/platform/x86/samsung-laptop.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index 1e436ebdcffc..31ddd57e8ca7 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -404,8 +404,9 @@ static int get_brightness(struct backlight_device *bd) static void check_for_stepping_quirk(void) { - u8 initial_level = read_brightness(); + u8 initial_level; u8 check_level; + u8 orig_level = read_brightness(); /* * Some laptops exhibit the strange behaviour of stepping toward @@ -414,6 +415,11 @@ static void check_for_stepping_quirk(void) * around in set_brightness. */ + if (orig_level == 0) + set_brightness(1); + + initial_level = read_brightness(); + if (initial_level <= 2) check_level = initial_level + 2; else @@ -427,7 +433,7 @@ static void check_for_stepping_quirk(void) pr_info("enabled workaround for brightness stepping quirk\n"); } - set_brightness(initial_level); + set_brightness(orig_level); } static int update_status(struct backlight_device *bd) From 8819de7f988289111cf4c44946f0d8c008b2d9a7 Mon Sep 17 00:00:00 2001 From: Andy Ross Date: Fri, 14 Oct 2011 11:13:35 +0200 Subject: [PATCH 17/45] asus-laptop: Platform detection for Pegatron Lucid Recognize the Pegatron Lucid tablets by their method signatures. Signed-off-by: Andy Ross Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 16 +++++++++------- drivers/platform/x86/asus-laptop.c | 23 ++++++++++++++++++++--- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 1e88d4785321..4e5623e4fa0b 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -64,15 +64,17 @@ config ASUS_LAPTOP depends on INPUT depends on RFKILL || RFKILL = n select INPUT_SPARSEKMAP + select INPUT_POLLDEV ---help--- - This is the new Linux driver for Asus laptops. It may also support some - MEDION, JVC or VICTOR laptops. It makes all the extra buttons generate - standard ACPI events and input events. It also adds - support for video output switching, LCD backlight control, Bluetooth and - Wlan control, and most importantly, allows you to blink those fancy LEDs. + This is a driver for Asus laptops, Lenovo SL and the Pegatron + Lucid tablet. It may also support some MEDION, JVC or VICTOR + laptops. It makes all the extra buttons generate standard + ACPI events and input events. It also adds support for video + output switching, LCD backlight control, Bluetooth and Wlan + control, and most importantly, allows you to blink those + fancy LEDs. - For more information and a userspace daemon for handling the extra - buttons see . + For more information see . If you have an ACPI-compatible ASUS laptop, say Y or M here. diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index fa6d7ec68b26..c08f4c26b988 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -4,6 +4,7 @@ * * Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor * Copyright (C) 2006-2007 Corentin Chary + * Copyright (C) 2011 Wind River Systems * * 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 @@ -48,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -173,6 +175,12 @@ MODULE_PARM_DESC(wwan_status, "Set the wireless status on boot " #define METHOD_KBD_LIGHT_SET "SLKB" #define METHOD_KBD_LIGHT_GET "GLKB" +/* For Pegatron Lucid tablet */ +#define DEVICE_NAME_PEGA "Lucid" +#define METHOD_PEGA_ENABLE "ENPR" +#define METHOD_PEGA_DISABLE "DAPR" +#define METHOD_PEGA_READ "RDLN" + /* * Define a specific led structure to keep the main structure clean */ @@ -209,6 +217,7 @@ struct asus_laptop { int wireless_status; bool have_rsts; + bool is_pega_lucid; struct rfkill *gps_rfkill; @@ -323,6 +332,14 @@ static int acpi_check_handle(acpi_handle handle, const char *method, return 0; } +static bool asus_check_pega_lucid(struct asus_laptop *asus) +{ + return !strcmp(asus->name, DEVICE_NAME_PEGA) && + !acpi_check_handle(asus->handle, METHOD_PEGA_ENABLE, NULL) && + !acpi_check_handle(asus->handle, METHOD_PEGA_DISABLE, NULL) && + !acpi_check_handle(asus->handle, METHOD_PEGA_READ, NULL); +} + /* Generic LED function */ static int asus_led_set(struct asus_laptop *asus, const char *method, int value) @@ -1203,7 +1220,6 @@ static mode_t asus_sysfs_is_visible(struct kobject *kobj, attr == &dev_attr_ls_level.attr) { supported = !acpi_check_handle(handle, METHOD_ALS_CONTROL, NULL) && !acpi_check_handle(handle, METHOD_ALS_LEVEL, NULL); - } else if (attr == &dev_attr_gps.attr) { supported = !acpi_check_handle(handle, METHOD_GPS_ON, NULL) && !acpi_check_handle(handle, METHOD_GPS_OFF, NULL) && @@ -1439,9 +1455,10 @@ static int __devinit asus_acpi_add(struct acpi_device *device) goto fail_platform; /* - * Register the platform device first. It is used as a parent for the - * sub-devices below. + * Need platform type detection first, then the platform + * device. It is used as a parent for the sub-devices below. */ + asus->is_pega_lucid = asus_check_pega_lucid(asus); result = asus_platform_init(asus); if (result) goto fail_platform; From 33989ba6f7feb3d03916e42d7775d802d2fdea1e Mon Sep 17 00:00:00 2001 From: Andy Ross Date: Fri, 14 Oct 2011 11:13:36 +0200 Subject: [PATCH 18/45] asus-laptop: Pegatron Lucid ALS sensor Ambient light sensor for Pegatron Lucid. Supports pre-existing ls_switch sysfs interface to en/disable automatic control, and exports the brightness from the device as "ls_value". Signed-off-by: Andy Ross Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-laptop.c | 80 ++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index c08f4c26b988..afed9ff33b86 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -177,9 +177,15 @@ MODULE_PARM_DESC(wwan_status, "Set the wireless status on boot " /* For Pegatron Lucid tablet */ #define DEVICE_NAME_PEGA "Lucid" + #define METHOD_PEGA_ENABLE "ENPR" #define METHOD_PEGA_DISABLE "DAPR" +#define PEGA_ALS 0x04 +#define PEGA_ALS_POWER 0x05 + #define METHOD_PEGA_READ "RDLN" +#define PEGA_READ_ALS_H 0x02 +#define PEGA_READ_ALS_L 0x03 /* * Define a specific led structure to keep the main structure clean @@ -340,6 +346,12 @@ static bool asus_check_pega_lucid(struct asus_laptop *asus) !acpi_check_handle(asus->handle, METHOD_PEGA_READ, NULL); } +static int asus_pega_lucid_set(struct asus_laptop *asus, int unit, bool enable) +{ + char *method = enable ? METHOD_PEGA_ENABLE : METHOD_PEGA_DISABLE; + return write_acpi_int(asus->handle, method, unit); +} + /* Generic LED function */ static int asus_led_set(struct asus_laptop *asus, const char *method, int value) @@ -924,8 +936,18 @@ static ssize_t store_disp(struct device *dev, struct device_attribute *attr, */ static void asus_als_switch(struct asus_laptop *asus, int value) { - if (write_acpi_int(asus->handle, METHOD_ALS_CONTROL, value)) - pr_warn("Error setting light sensor switch\n"); + int ret; + + if (asus->is_pega_lucid) { + ret = asus_pega_lucid_set(asus, PEGA_ALS, value); + if (!ret) + ret = asus_pega_lucid_set(asus, PEGA_ALS_POWER, value); + } else { + ret = write_acpi_int(asus->handle, METHOD_ALS_CONTROL, value); + } + if (ret) + pr_warning("Error setting light sensor switch\n"); + asus->light_switch = value; } @@ -981,6 +1003,35 @@ static ssize_t store_lslvl(struct device *dev, struct device_attribute *attr, return rv; } +static int pega_int_read(struct asus_laptop *asus, int arg, int *result) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + int err = write_acpi_int_ret(asus->handle, METHOD_PEGA_READ, arg, + &buffer); + if (!err) { + union acpi_object *obj = buffer.pointer; + if (obj && obj->type == ACPI_TYPE_INTEGER) + *result = obj->integer.value; + else + err = -EIO; + } + return err; +} + +static ssize_t show_lsvalue(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct asus_laptop *asus = dev_get_drvdata(dev); + int err, hi, lo; + + err = pega_int_read(asus, PEGA_READ_ALS_H, &hi); + if (!err) + err = pega_int_read(asus, PEGA_READ_ALS_L, &lo); + if (!err) + return sprintf(buf, "%d\n", 10 * hi + lo); + return err; +} + /* * GPS */ @@ -1169,6 +1220,7 @@ static DEVICE_ATTR(wimax, S_IRUGO | S_IWUSR, show_wimax, store_wimax); static DEVICE_ATTR(wwan, S_IRUGO | S_IWUSR, show_wwan, store_wwan); static DEVICE_ATTR(display, S_IWUSR, NULL, store_disp); static DEVICE_ATTR(ledd, S_IRUGO | S_IWUSR, show_ledd, store_ledd); +static DEVICE_ATTR(ls_value, S_IRUGO, show_lsvalue, NULL); static DEVICE_ATTR(ls_level, S_IRUGO | S_IWUSR, show_lslvl, store_lslvl); static DEVICE_ATTR(ls_switch, S_IRUGO | S_IWUSR, show_lssw, store_lssw); static DEVICE_ATTR(gps, S_IRUGO | S_IWUSR, show_gps, store_gps); @@ -1181,6 +1233,7 @@ static struct attribute *asus_attributes[] = { &dev_attr_wwan.attr, &dev_attr_display.attr, &dev_attr_ledd.attr, + &dev_attr_ls_value.attr, &dev_attr_ls_level.attr, &dev_attr_ls_switch.attr, &dev_attr_gps.attr, @@ -1197,6 +1250,19 @@ static mode_t asus_sysfs_is_visible(struct kobject *kobj, acpi_handle handle = asus->handle; bool supported; + if (asus->is_pega_lucid) { + /* no ls_level interface on the Lucid */ + if (attr == &dev_attr_ls_switch.attr) + supported = true; + else if (attr == &dev_attr_ls_level.attr) + supported = false; + else + goto normal; + + return supported; + } + +normal: if (attr == &dev_attr_wlan.attr) { supported = !acpi_check_handle(handle, METHOD_WLAN, NULL); @@ -1219,7 +1285,9 @@ static mode_t asus_sysfs_is_visible(struct kobject *kobj, } else if (attr == &dev_attr_ls_switch.attr || attr == &dev_attr_ls_level.attr) { supported = !acpi_check_handle(handle, METHOD_ALS_CONTROL, NULL) && - !acpi_check_handle(handle, METHOD_ALS_LEVEL, NULL); + !acpi_check_handle(handle, METHOD_ALS_LEVEL, NULL); + } else if (attr == &dev_attr_ls_value.attr) { + supported = asus->is_pega_lucid; } else if (attr == &dev_attr_gps.attr) { supported = !acpi_check_handle(handle, METHOD_GPS_ON, NULL) && !acpi_check_handle(handle, METHOD_GPS_OFF, NULL) && @@ -1407,8 +1475,10 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus) asus->light_switch = 0; /* Default to light sensor disabled */ asus->light_level = 5; /* level 5 for sensor sensitivity */ - if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) && - !acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) { + if (asus->is_pega_lucid) { + asus_als_switch(asus, asus->light_switch); + } else if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) && + !acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) { asus_als_switch(asus, asus->light_switch); asus_als_level(asus, asus->light_level); } From 5d6afd150321a9e6f1180f5dced9032cce6861ac Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 8 Aug 2011 17:14:19 +0800 Subject: [PATCH 19/45] platform-drivers-x86: eeepc-laptop: fix wrong test for successful registered led_classdev device_create returns &struct device pointer on success, or ERR_PTR() on error. Thus if led_classdev_register fails, led_cdev->dev is always not NULL. If IS_ERR(eeepc->tpd_led.dev) is ture, it means led_classdev_register fails. If (asus->tpd_led.dev) is NULL, it means we call eeepc_led_exit before calling led_classdev_register for &eeepc->tpd_led. We only want to call led_classdev_unregister for sucessfully registered led_classdev, then we should check (!IS_ERR_OR_NULL(eeepc->tpd_led.dev)). Signed-off-by: Axel Lin Acked-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-laptop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 1c45d92e2163..ea44abd8df48 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -568,7 +568,7 @@ static int eeepc_led_init(struct eeepc_laptop *eeepc) static void eeepc_led_exit(struct eeepc_laptop *eeepc) { - if (eeepc->tpd_led.dev) + if (!IS_ERR_OR_NULL(eeepc->tpd_led.dev)) led_classdev_unregister(&eeepc->tpd_led); if (eeepc->led_workqueue) destroy_workqueue(eeepc->led_workqueue); From e9298028ee69bda44607156e7348cd32f7ab9360 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 8 Aug 2011 17:16:01 +0800 Subject: [PATCH 20/45] platform-drivers-x86: asus-wmi: fix resource leak in asus_wmi_led_exit This patch fixes wrong test for successful registered led_classdev, and also fixes a leak due to missing led_classdev_unregister call for asus->kbd_led in asus_wmi_led_exit. device_create returns &struct device pointer on success, or ERR_PTR() on error. Thus if led_classdev_register fails, led_cdev->dev is always not NULL. Thus to unregister a successful registered led_classdev, we should check (!IS_ERR_OR_NULL(asus->tpd_led.dev)) instead of (asus->tpd_led.dev). we use IS_ERR_OR_NULL instead of IS_ERR because if we havn't call led_classdev_register, the led_cdev->dev is NULL. Signed-off-by: Axel Lin Acked-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-wmi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 95cba9ebf6c0..d1049ee3c9e8 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -453,7 +453,9 @@ static enum led_brightness kbd_led_get(struct led_classdev *led_cdev) static void asus_wmi_led_exit(struct asus_wmi *asus) { - if (asus->tpd_led.dev) + if (!IS_ERR_OR_NULL(asus->kbd_led.dev)) + led_classdev_unregister(&asus->kbd_led); + if (!IS_ERR_OR_NULL(asus->tpd_led.dev)) led_classdev_unregister(&asus->tpd_led); if (asus->led_workqueue) destroy_workqueue(asus->led_workqueue); From 8fcf71aa0032acbd30b3222f9cb238919ab3b984 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 8 Aug 2011 17:17:18 +0800 Subject: [PATCH 21/45] platform-drivers-x86: asus-laptop: fix wrong test for successful registered led_classdev device_create returns &struct device pointer on success, or ERR_PTR() on error. Thus if led_classdev_register fails, led_cdev->dev is always not NULL. Thus to unregister a successful registered led_classdev, we should check IS_ERR_OR_NULL macro for led_cdev->dev instead of checking if led_cdev->dev is NULL or not. we use IS_ERR_OR_NULL instead of IS_ERR because if we havn't call led_classdev_register, the led_cdev->dev is NULL. Signed-off-by: Axel Lin Acked-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-laptop.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index afed9ff33b86..10b0a8663bb7 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -459,17 +459,17 @@ static enum led_brightness asus_kled_cdev_get(struct led_classdev *led_cdev) static void asus_led_exit(struct asus_laptop *asus) { - if (asus->mled.led.dev) + if (!IS_ERR_OR_NULL(asus->mled.led.dev)) led_classdev_unregister(&asus->mled.led); - if (asus->tled.led.dev) + if (!IS_ERR_OR_NULL(asus->tled.led.dev)) led_classdev_unregister(&asus->tled.led); - if (asus->pled.led.dev) + if (!IS_ERR_OR_NULL(asus->pled.led.dev)) led_classdev_unregister(&asus->pled.led); - if (asus->rled.led.dev) + if (!IS_ERR_OR_NULL(asus->rled.led.dev)) led_classdev_unregister(&asus->rled.led); - if (asus->gled.led.dev) + if (!IS_ERR_OR_NULL(asus->gled.led.dev)) led_classdev_unregister(&asus->gled.led); - if (asus->kled.led.dev) + if (!IS_ERR_OR_NULL(asus->kled.led.dev)) led_classdev_unregister(&asus->kled.led); if (asus->led_workqueue) { destroy_workqueue(asus->led_workqueue); From 1709adab0773616da7a8190f2762e599afb0a295 Mon Sep 17 00:00:00 2001 From: "Lee, Chun-Yi" Date: Thu, 18 Aug 2011 18:47:33 +0800 Subject: [PATCH 22/45] acer-wmi: check wireless capability flag before register rfkill There will be better to check the wireless capability flag (ACER_CAP_WIRELESS) before register wireless rfkill because maybe the machine doesn't have wifi module or the module removed by user. Tested on Acer Travelmate 8572 Tested on Acer Aspire 4739Z Tested-by: AceLan Kao Cc: Carlos Corbacho Cc: Matthew Garrett Cc: Dmitry Torokhov Cc: Corentin Chary Cc: Thomas Renninger Signed-off-by: Lee, Chun-Yi Signed-off-by: Matthew Garrett --- drivers/platform/x86/acer-wmi.c | 64 +++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 22 deletions(-) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index dac286a568ea..fc7c9733bba5 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -1294,12 +1294,13 @@ static void acer_rfkill_update(struct work_struct *ignored) u32 state; acpi_status status; - status = get_u32(&state, ACER_CAP_WIRELESS); - if (ACPI_SUCCESS(status)) { - if (quirks->wireless == 3) { - rfkill_set_hw_state(wireless_rfkill, !state); - } else { - rfkill_set_sw_state(wireless_rfkill, !state); + if (has_cap(ACER_CAP_WIRELESS)) { + status = get_u32(&state, ACER_CAP_WIRELESS); + if (ACPI_SUCCESS(status)) { + if (quirks->wireless == 3) + rfkill_set_hw_state(wireless_rfkill, !state); + else + rfkill_set_sw_state(wireless_rfkill, !state); } } @@ -1368,19 +1369,24 @@ static struct rfkill *acer_rfkill_register(struct device *dev, static int acer_rfkill_init(struct device *dev) { - wireless_rfkill = acer_rfkill_register(dev, RFKILL_TYPE_WLAN, - "acer-wireless", ACER_CAP_WIRELESS); - if (IS_ERR(wireless_rfkill)) - return PTR_ERR(wireless_rfkill); + int err; + + if (has_cap(ACER_CAP_WIRELESS)) { + wireless_rfkill = acer_rfkill_register(dev, RFKILL_TYPE_WLAN, + "acer-wireless", ACER_CAP_WIRELESS); + if (IS_ERR(wireless_rfkill)) { + err = PTR_ERR(wireless_rfkill); + goto error_wireless; + } + } if (has_cap(ACER_CAP_BLUETOOTH)) { bluetooth_rfkill = acer_rfkill_register(dev, RFKILL_TYPE_BLUETOOTH, "acer-bluetooth", ACER_CAP_BLUETOOTH); if (IS_ERR(bluetooth_rfkill)) { - rfkill_unregister(wireless_rfkill); - rfkill_destroy(wireless_rfkill); - return PTR_ERR(bluetooth_rfkill); + err = PTR_ERR(bluetooth_rfkill); + goto error_bluetooth; } } @@ -1389,30 +1395,44 @@ static int acer_rfkill_init(struct device *dev) RFKILL_TYPE_WWAN, "acer-threeg", ACER_CAP_THREEG); if (IS_ERR(threeg_rfkill)) { - rfkill_unregister(wireless_rfkill); - rfkill_destroy(wireless_rfkill); - rfkill_unregister(bluetooth_rfkill); - rfkill_destroy(bluetooth_rfkill); - return PTR_ERR(threeg_rfkill); + err = PTR_ERR(threeg_rfkill); + goto error_threeg; } } rfkill_inited = true; - if (ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID)) + if ((ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID)) && + has_cap(ACER_CAP_WIRELESS | ACER_CAP_BLUETOOTH | ACER_CAP_THREEG)) schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); return 0; + +error_threeg: + if (has_cap(ACER_CAP_BLUETOOTH)) { + rfkill_unregister(bluetooth_rfkill); + rfkill_destroy(bluetooth_rfkill); + } +error_bluetooth: + if (has_cap(ACER_CAP_WIRELESS)) { + rfkill_unregister(wireless_rfkill); + rfkill_destroy(wireless_rfkill); + } +error_wireless: + return err; } static void acer_rfkill_exit(void) { - if (ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID)) + if ((ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID)) && + has_cap(ACER_CAP_WIRELESS | ACER_CAP_BLUETOOTH | ACER_CAP_THREEG)) cancel_delayed_work_sync(&acer_rfkill_work); - rfkill_unregister(wireless_rfkill); - rfkill_destroy(wireless_rfkill); + if (has_cap(ACER_CAP_WIRELESS)) { + rfkill_unregister(wireless_rfkill); + rfkill_destroy(wireless_rfkill); + } if (has_cap(ACER_CAP_BLUETOOTH)) { rfkill_unregister(bluetooth_rfkill); From d4afc7754a60b885b63ef23fd194984e2d53a4e6 Mon Sep 17 00:00:00 2001 From: Rene Bollford Date: Sun, 23 Oct 2011 09:56:42 +0200 Subject: [PATCH 23/45] [PATCH] ideapad: Check if acpi already handle backlight power to avoid a page fault This patch avoid a page fault in the ideapad-laptop extras when turning the backlight power on or off. Signed-off-by: Rene Bolldorf Signed-off-by: Matthew Garrett --- drivers/platform/x86/ideapad-laptop.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 0c595410e788..0d94eec00f4d 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -493,6 +493,8 @@ static void ideapad_backlight_notify_power(struct ideapad_private *priv) unsigned long power; struct backlight_device *blightdev = priv->blightdev; + if (!blightdev) + return; if (read_ec_data(ideapad_handle, 0x18, &power)) return; blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; From 6cb8c13da10855f17110421d9b13db015e11840e Mon Sep 17 00:00:00 2001 From: Ike Panhc Date: Thu, 25 Aug 2011 22:28:45 +0800 Subject: [PATCH 24/45] MAINTAINERS: add new entry for ideapad-laptop Signed-off-by: Ike Panhc Signed-off-by: Matthew Garrett --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 6185d0513584..be12a619938a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3180,6 +3180,13 @@ F: Documentation/ide/ F: drivers/ide/ F: include/linux/ide.h +IDEAPAD LAPTOP EXTRAS DRIVER +M: Ike Panhc +L: platform-driver-x86@vger.kernel.org +W: http://launchpad.net/ideapad-laptop +S: Maintained +F: drivers/platform/x86/ideapad-laptop.c + IDE/ATAPI DRIVERS M: Borislav Petkov L: linux-ide@vger.kernel.org From 21893ab24c78ec844cbaffbb7bf11b33cfdea8b4 Mon Sep 17 00:00:00 2001 From: "Kirill A. Shutemov" Date: Fri, 26 Aug 2011 11:55:46 +0100 Subject: [PATCH 25/45] intel_scu_ipcutil: fix major device number handling We need to save major device number to be able to use it for unregister_chrdev() in ipc_module_exit(). ipc_module_init() must return 0 on success, not major device number. Signed-off-by: Kirill A. Shutemov Signed-off-by: Alan Cox Signed-off-by: Matthew Garrett --- drivers/platform/x86/intel_scu_ipcutil.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/intel_scu_ipcutil.c b/drivers/platform/x86/intel_scu_ipcutil.c index b93a03259c16..2d0f9136ea9a 100644 --- a/drivers/platform/x86/intel_scu_ipcutil.c +++ b/drivers/platform/x86/intel_scu_ipcutil.c @@ -24,7 +24,7 @@ #include #include -static u32 major; +static int major; #define MAX_FW_SIZE 264192 @@ -117,7 +117,11 @@ static const struct file_operations scu_ipc_fops = { static int __init ipc_module_init(void) { - return register_chrdev(0, "intel_mid_scu", &scu_ipc_fops); + major = register_chrdev(0, "intel_mid_scu", &scu_ipc_fops); + if (major < 0) + return major; + + return 0; } static void __exit ipc_module_exit(void) From 2be1dc215bd34faf4895e4396dab924a24e10944 Mon Sep 17 00:00:00 2001 From: Ike Panhc Date: Tue, 6 Sep 2011 02:31:53 +0800 Subject: [PATCH 26/45] ideapad: define vpc commands Better then write hex number everywhere. Signed-off-by: Ike Panhc Signed-off-by: Matthew Garrett --- drivers/platform/x86/ideapad-laptop.c | 57 +++++++++++++++++++-------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 0d94eec00f4d..0a1ceaa9062e 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -42,6 +42,30 @@ #define CFG_WIFI_BIT (18) #define CFG_CAMERA_BIT (19) +enum { + VPCCMD_R_VPC1 = 0x10, + VPCCMD_R_BL_MAX, + VPCCMD_R_BL, + VPCCMD_W_BL, + VPCCMD_R_WIFI, + VPCCMD_W_WIFI, + VPCCMD_R_BT, + VPCCMD_W_BT, + VPCCMD_R_BL_POWER, + VPCCMD_R_NOVO, + VPCCMD_R_VPC2, + VPCCMD_R_TOUCHPAD, + VPCCMD_W_TOUCHPAD, + VPCCMD_R_CAMERA, + VPCCMD_W_CAMERA, + VPCCMD_R_3G, + VPCCMD_W_3G, + VPCCMD_R_ODD, /* 0x21 */ + VPCCMD_R_RF = 0x23, + VPCCMD_W_RF, + VPCCMD_W_BL_POWER = 0x33, +}; + struct ideapad_private { struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM]; struct platform_device *platform_device; @@ -172,7 +196,7 @@ static ssize_t show_ideapad_cam(struct device *dev, { unsigned long result; - if (read_ec_data(ideapad_handle, 0x1D, &result)) + if (read_ec_data(ideapad_handle, VPCCMD_R_CAMERA, &result)) return sprintf(buf, "-1\n"); return sprintf(buf, "%lu\n", result); } @@ -187,7 +211,7 @@ static ssize_t store_ideapad_cam(struct device *dev, return 0; if (sscanf(buf, "%i", &state) != 1) return -EINVAL; - ret = write_ec_cmd(ideapad_handle, 0x1E, state); + ret = write_ec_cmd(ideapad_handle, VPCCMD_W_CAMERA, state); if (ret < 0) return ret; return count; @@ -244,9 +268,9 @@ struct ideapad_rfk_data { }; const struct ideapad_rfk_data ideapad_rfk_data[] = { - { "ideapad_wlan", CFG_WIFI_BIT, 0x15, RFKILL_TYPE_WLAN }, - { "ideapad_bluetooth", CFG_BT_BIT, 0x17, RFKILL_TYPE_BLUETOOTH }, - { "ideapad_3g", CFG_3G_BIT, 0x20, RFKILL_TYPE_WWAN }, + { "ideapad_wlan", CFG_WIFI_BIT, VPCCMD_W_WIFI, RFKILL_TYPE_WLAN }, + { "ideapad_bluetooth", CFG_BT_BIT, VPCCMD_W_BT, RFKILL_TYPE_BLUETOOTH }, + { "ideapad_3g", CFG_3G_BIT, VPCCMD_W_3G, RFKILL_TYPE_WWAN }, }; static int ideapad_rfk_set(void *data, bool blocked) @@ -266,7 +290,7 @@ static void ideapad_sync_rfk_state(struct acpi_device *adevice) unsigned long hw_blocked; int i; - if (read_ec_data(ideapad_handle, 0x23, &hw_blocked)) + if (read_ec_data(ideapad_handle, VPCCMD_R_RF, &hw_blocked)) return; hw_blocked = !hw_blocked; @@ -426,16 +450,17 @@ static int ideapad_backlight_get_brightness(struct backlight_device *blightdev) { unsigned long now; - if (read_ec_data(ideapad_handle, 0x12, &now)) + if (read_ec_data(ideapad_handle, VPCCMD_R_BL, &now)) return -EIO; return now; } static int ideapad_backlight_update_status(struct backlight_device *blightdev) { - if (write_ec_cmd(ideapad_handle, 0x13, blightdev->props.brightness)) + if (write_ec_cmd(ideapad_handle, VPCCMD_W_BL, + blightdev->props.brightness)) return -EIO; - if (write_ec_cmd(ideapad_handle, 0x33, + if (write_ec_cmd(ideapad_handle, VPCCMD_W_BL_POWER, blightdev->props.power == FB_BLANK_POWERDOWN ? 0 : 1)) return -EIO; @@ -453,11 +478,11 @@ static int ideapad_backlight_init(struct ideapad_private *priv) struct backlight_properties props; unsigned long max, now, power; - if (read_ec_data(ideapad_handle, 0x11, &max)) + if (read_ec_data(ideapad_handle, VPCCMD_R_BL_MAX, &max)) return -EIO; - if (read_ec_data(ideapad_handle, 0x12, &now)) + if (read_ec_data(ideapad_handle, VPCCMD_R_BL, &now)) return -EIO; - if (read_ec_data(ideapad_handle, 0x18, &power)) + if (read_ec_data(ideapad_handle, VPCCMD_R_BL_POWER, &power)) return -EIO; memset(&props, 0, sizeof(struct backlight_properties)); @@ -495,7 +520,7 @@ static void ideapad_backlight_notify_power(struct ideapad_private *priv) if (!blightdev) return; - if (read_ec_data(ideapad_handle, 0x18, &power)) + if (read_ec_data(ideapad_handle, VPCCMD_R_BL_POWER, &power)) return; blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; } @@ -506,7 +531,7 @@ static void ideapad_backlight_notify_brightness(struct ideapad_private *priv) /* if we control brightness via acpi video driver */ if (priv->blightdev == NULL) { - read_ec_data(ideapad_handle, 0x12, &now); + read_ec_data(ideapad_handle, VPCCMD_R_BL, &now); return; } @@ -595,9 +620,9 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) acpi_handle handle = adevice->handle; unsigned long vpc1, vpc2, vpc_bit; - if (read_ec_data(handle, 0x10, &vpc1)) + if (read_ec_data(handle, VPCCMD_R_VPC1, &vpc1)) return; - if (read_ec_data(handle, 0x1A, &vpc2)) + if (read_ec_data(handle, VPCCMD_R_VPC2, &vpc2)) return; vpc1 = (vpc2 << 8) | vpc1; From 923de84ab07cfcee75946191f17a6e7fb98e3ac8 Mon Sep 17 00:00:00 2001 From: Ike Panhc Date: Tue, 6 Sep 2011 02:32:01 +0800 Subject: [PATCH 27/45] ideapad: change parameter of ideapad_sync_rfk_state This could save us a local variable. Signed-off-by: Ike Panhc Signed-off-by: Matthew Garrett --- drivers/platform/x86/ideapad-laptop.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 0a1ceaa9062e..ab3add3327ad 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -284,9 +284,8 @@ static struct rfkill_ops ideapad_rfk_ops = { .set_block = ideapad_rfk_set, }; -static void ideapad_sync_rfk_state(struct acpi_device *adevice) +static void ideapad_sync_rfk_state(struct ideapad_private *priv) { - struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); unsigned long hw_blocked; int i; @@ -577,7 +576,7 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice) else priv->rfk[i] = NULL; } - ideapad_sync_rfk_state(adevice); + ideapad_sync_rfk_state(priv); if (!acpi_video_backlight_support()) { ret = ideapad_backlight_init(priv); @@ -630,7 +629,7 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) if (test_bit(vpc_bit, &vpc1)) { switch (vpc_bit) { case 9: - ideapad_sync_rfk_state(adevice); + ideapad_sync_rfk_state(priv); break; case 4: ideapad_backlight_notify_brightness(priv); From f43d9ec06028b8cb122a7a2b367a7af066342918 Mon Sep 17 00:00:00 2001 From: Ike Panhc Date: Tue, 6 Sep 2011 02:32:10 +0800 Subject: [PATCH 28/45] ideapad: add event for Novo key Novo key is the Lenovo define key on every ideapads, which has two definition. Long press and short click. Driver will report KEY_PROG1 on short click and KEY_PROG2 when long press. On pre-install OS for ideapads there are application waiting for the event and there is no such application for Linux yet, so I think to report KEY_PROG is fine in anyway. Also change the scancode from hex to decimal because they are related to vpc bit in notify function which is in decimal. Signed-off-by: Ike Panhc Signed-off-by: Matthew Garrett --- drivers/platform/x86/ideapad-laptop.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index ab3add3327ad..39a72a8aeb8c 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -386,8 +386,10 @@ static void ideapad_platform_exit(struct ideapad_private *priv) * input device */ static const struct key_entry ideapad_keymap[] = { - { KE_KEY, 0x06, { KEY_SWITCHVIDEOMODE } }, - { KE_KEY, 0x0D, { KEY_WLAN } }, + { KE_KEY, 6, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, 13, { KEY_WLAN } }, + { KE_KEY, 16, { KEY_PROG1 } }, + { KE_KEY, 17, { KEY_PROG2 } }, { KE_END, 0 }, }; @@ -442,6 +444,18 @@ static void ideapad_input_report(struct ideapad_private *priv, sparse_keymap_report_event(priv->inputdev, scancode, 1, true); } +static void ideapad_input_novokey(struct ideapad_private *priv) +{ + unsigned long long_pressed; + + if (read_ec_data(ideapad_handle, VPCCMD_R_NOVO, &long_pressed)) + return; + if (long_pressed) + ideapad_input_report(priv, 17); + else + ideapad_input_report(priv, 16); +} + /* * backlight */ @@ -634,6 +648,9 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) case 4: ideapad_backlight_notify_brightness(priv); break; + case 3: + ideapad_input_novokey(priv); + break; case 2: ideapad_backlight_notify_power(priv); break; From 773e3206e6a8b4c81826dc76057a96ba3cc96672 Mon Sep 17 00:00:00 2001 From: Ike Panhc Date: Tue, 6 Sep 2011 02:32:52 +0800 Subject: [PATCH 29/45] ideapad: add debugfs support Add two nodes in debugfs which shows cfg value and its meaning, and status info read from VPC2004. Signed-off-by: Ike Panhc Signed-off-by: Matthew Garrett --- Documentation/ABI/testing/debugfs-ideapad | 19 +++ drivers/platform/x86/ideapad-laptop.c | 152 ++++++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 Documentation/ABI/testing/debugfs-ideapad diff --git a/Documentation/ABI/testing/debugfs-ideapad b/Documentation/ABI/testing/debugfs-ideapad new file mode 100644 index 000000000000..7079c0b21030 --- /dev/null +++ b/Documentation/ABI/testing/debugfs-ideapad @@ -0,0 +1,19 @@ +What: /sys/kernel/debug/ideapad/cfg +Date: Sep 2011 +KernelVersion: 3.2 +Contact: Ike Panhc +Description: + +cfg shows the return value of _CFG method in VPC2004 device. It tells machine +capability and what graphic component within the machine. + + +What: /sys/kernel/debug/ideapad/status +Date: Sep 2011 +KernelVersion: 3.2 +Contact: Ike Panhc +Description: + +status shows infos we can read and tells its meaning and value. + + diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 39a72a8aeb8c..1c8b5065f34c 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -34,6 +34,8 @@ #include #include #include +#include +#include #define IDEAPAD_RFKILL_DEV_NUM (3) @@ -71,10 +73,12 @@ struct ideapad_private { struct platform_device *platform_device; struct input_dev *inputdev; struct backlight_device *blightdev; + struct dentry *debug; unsigned long cfg; }; static acpi_handle ideapad_handle; +static struct ideapad_private *ideapad_priv; static bool no_bt_rfkill; module_param(no_bt_rfkill, bool, 0444); MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth."); @@ -187,6 +191,146 @@ static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data) return -1; } +/* + * debugfs + */ +#define DEBUGFS_EVENT_LEN (4096) +static int debugfs_status_show(struct seq_file *s, void *data) +{ + unsigned long value; + + if (!read_ec_data(ideapad_handle, VPCCMD_R_BL_MAX, &value)) + seq_printf(s, "Backlight max:\t%lu\n", value); + if (!read_ec_data(ideapad_handle, VPCCMD_R_BL, &value)) + seq_printf(s, "Backlight now:\t%lu\n", value); + if (!read_ec_data(ideapad_handle, VPCCMD_R_BL_POWER, &value)) + seq_printf(s, "BL power value:\t%s\n", value ? "On" : "Off"); + seq_printf(s, "=====================\n"); + + if (!read_ec_data(ideapad_handle, VPCCMD_R_RF, &value)) + seq_printf(s, "Radio status:\t%s(%lu)\n", + value ? "On" : "Off", value); + if (!read_ec_data(ideapad_handle, VPCCMD_R_WIFI, &value)) + seq_printf(s, "Wifi status:\t%s(%lu)\n", + value ? "On" : "Off", value); + if (!read_ec_data(ideapad_handle, VPCCMD_R_BT, &value)) + seq_printf(s, "BT status:\t%s(%lu)\n", + value ? "On" : "Off", value); + if (!read_ec_data(ideapad_handle, VPCCMD_R_3G, &value)) + seq_printf(s, "3G status:\t%s(%lu)\n", + value ? "On" : "Off", value); + seq_printf(s, "=====================\n"); + + if (!read_ec_data(ideapad_handle, VPCCMD_R_TOUCHPAD, &value)) + seq_printf(s, "Touchpad status:%s(%lu)\n", + value ? "On" : "Off", value); + if (!read_ec_data(ideapad_handle, VPCCMD_R_CAMERA, &value)) + seq_printf(s, "Camera status:\t%s(%lu)\n", + value ? "On" : "Off", value); + + return 0; +} + +static int debugfs_status_open(struct inode *inode, struct file *file) +{ + return single_open(file, debugfs_status_show, NULL); +} + +static const struct file_operations debugfs_status_fops = { + .owner = THIS_MODULE, + .open = debugfs_status_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int debugfs_cfg_show(struct seq_file *s, void *data) +{ + if (!ideapad_priv) { + seq_printf(s, "cfg: N/A\n"); + } else { + seq_printf(s, "cfg: 0x%.8lX\n\nCapability: ", + ideapad_priv->cfg); + if (test_bit(CFG_BT_BIT, &ideapad_priv->cfg)) + seq_printf(s, "Bluetooth "); + if (test_bit(CFG_3G_BIT, &ideapad_priv->cfg)) + seq_printf(s, "3G "); + if (test_bit(CFG_WIFI_BIT, &ideapad_priv->cfg)) + seq_printf(s, "Wireless "); + if (test_bit(CFG_CAMERA_BIT, &ideapad_priv->cfg)) + seq_printf(s, "Camera "); + seq_printf(s, "\nGraphic: "); + switch ((ideapad_priv->cfg)&0x700) { + case 0x100: + seq_printf(s, "Intel"); + break; + case 0x200: + seq_printf(s, "ATI"); + break; + case 0x300: + seq_printf(s, "Nvidia"); + break; + case 0x400: + seq_printf(s, "Intel and ATI"); + break; + case 0x500: + seq_printf(s, "Intel and Nvidia"); + break; + } + seq_printf(s, "\n"); + } + return 0; +} + +static int debugfs_cfg_open(struct inode *inode, struct file *file) +{ + return single_open(file, debugfs_cfg_show, NULL); +} + +static const struct file_operations debugfs_cfg_fops = { + .owner = THIS_MODULE, + .open = debugfs_cfg_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __devinit ideapad_debugfs_init(struct ideapad_private *priv) +{ + struct dentry *node; + + priv->debug = debugfs_create_dir("ideapad", NULL); + if (priv->debug == NULL) { + pr_err("failed to create debugfs directory"); + goto errout; + } + + node = debugfs_create_file("cfg", S_IRUGO, priv->debug, NULL, + &debugfs_cfg_fops); + if (!node) { + pr_err("failed to create cfg in debugfs"); + goto errout; + } + + node = debugfs_create_file("status", S_IRUGO, priv->debug, NULL, + &debugfs_status_fops); + if (!node) { + pr_err("failed to create event in debugfs"); + goto errout; + } + + return 0; + +errout: + return -ENOMEM; +} + +static void ideapad_debugfs_exit(struct ideapad_private *priv) +{ + debugfs_remove_recursive(priv->debug); + priv->debug = NULL; +} + /* * sysfs */ @@ -573,6 +717,7 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice) if (!priv) return -ENOMEM; dev_set_drvdata(&adevice->dev, priv); + ideapad_priv = priv; ideapad_handle = adevice->handle; priv->cfg = cfg; @@ -580,6 +725,10 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice) if (ret) goto platform_failed; + ret = ideapad_debugfs_init(priv); + if (ret) + goto debugfs_failed; + ret = ideapad_input_init(priv); if (ret) goto input_failed; @@ -605,6 +754,8 @@ backlight_failed: ideapad_unregister_rfkill(adevice, i); ideapad_input_exit(priv); input_failed: + ideapad_debugfs_exit(priv); +debugfs_failed: ideapad_platform_exit(priv); platform_failed: kfree(priv); @@ -620,6 +771,7 @@ static int __devexit ideapad_acpi_remove(struct acpi_device *adevice, int type) for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) ideapad_unregister_rfkill(adevice, i); ideapad_input_exit(priv); + ideapad_debugfs_exit(priv); ideapad_platform_exit(priv); dev_set_drvdata(&adevice->dev, NULL); kfree(priv); From 349d594be45bdc7a86a08385f2f4f49d08ab68dd Mon Sep 17 00:00:00 2001 From: Ike Panhc Date: Tue, 6 Sep 2011 02:33:00 +0800 Subject: [PATCH 30/45] ideapad: remove sysfs node for cfg Replaced by the one in debugfs. Signed-off-by: Ike Panhc Signed-off-by: Matthew Garrett --- .../ABI/testing/sysfs-platform-ideapad-laptop | 15 --------------- drivers/platform/x86/ideapad-laptop.c | 12 ------------ 2 files changed, 27 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-platform-ideapad-laptop b/Documentation/ABI/testing/sysfs-platform-ideapad-laptop index ff53183c3848..814b01354c41 100644 --- a/Documentation/ABI/testing/sysfs-platform-ideapad-laptop +++ b/Documentation/ABI/testing/sysfs-platform-ideapad-laptop @@ -5,19 +5,4 @@ Contact: "Ike Panhc " Description: Control the power of camera module. 1 means on, 0 means off. -What: /sys/devices/platform/ideapad/cfg -Date: Jun 2011 -KernelVersion: 3.1 -Contact: "Ike Panhc " -Description: - Ideapad capability bits. - Bit 8-10: 1 - Intel graphic only - 2 - ATI graphic only - 3 - Nvidia graphic only - 4 - Intel and ATI graphic - 5 - Intel and Nvidia graphic - Bit 16: Bluetooth exist (1 for exist) - Bit 17: 3G exist (1 for exist) - Bit 18: Wifi exist (1 for exist) - Bit 19: Camera exist (1 for exist) diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 1c8b5065f34c..a36addf106a0 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -363,20 +363,8 @@ static ssize_t store_ideapad_cam(struct device *dev, static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam); -static ssize_t show_ideapad_cfg(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct ideapad_private *priv = dev_get_drvdata(dev); - - return sprintf(buf, "0x%.8lX\n", priv->cfg); -} - -static DEVICE_ATTR(cfg, 0444, show_ideapad_cfg, NULL); - static struct attribute *ideapad_attributes[] = { &dev_attr_camera_power.attr, - &dev_attr_cfg.attr, NULL }; From 023b9565972a4a5e0f01b9aa32680af6e9b5c388 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 7 Sep 2011 15:00:02 -0700 Subject: [PATCH 31/45] WMI: properly cleanup devices to avoid crashes We need to remove devices that we destroy from the list, otherwise we'll crash if there are more than one "_WDG" methods in DSDT. This fixes https://bugzilla.kernel.org/show_bug.cgi?id=32052 Tested-by: Ilya Tumaykin Cc: stable@kernel.org Signed-off-by: Dmitry Torokhov Acked-by: Carlos Corbacho Signed-off-by: Matthew Garrett --- drivers/platform/x86/wmi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index f23d5a84e7b1..9b88be42b6cd 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -754,9 +754,13 @@ static void wmi_free_devices(void) struct wmi_block *wblock, *next; /* Delete devices for all the GUIDs */ - list_for_each_entry_safe(wblock, next, &wmi_block_list, list) + list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { + list_del(&wblock->list); if (wblock->dev.class) device_unregister(&wblock->dev); + else + kfree(wblock); + } } static bool guid_already_parsed(const char *guid_string) From 24570d2293d8874cf0041616af4dc92929f30a7c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 15 Sep 2011 12:24:47 +0200 Subject: [PATCH 32/45] hp_accel: Add a new PNP id New HP laptops assign a new PNP id "HPQ6000" for DriveGuard. Signed-off-by: Takashi Iwai Signed-off-by: Matthew Garrett --- drivers/platform/x86/hp_accel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c index 1b52d00e2f90..074e79e4ad98 100644 --- a/drivers/platform/x86/hp_accel.c +++ b/drivers/platform/x86/hp_accel.c @@ -76,6 +76,7 @@ static inline void delayed_sysfs_set(struct led_classdev *led_cdev, /* For automatic insertion of the module */ static struct acpi_device_id lis3lv02d_device_ids[] = { {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */ + {"HPQ6000", 0}, /* HP Mobile Data Protection System PNP */ {"", 0}, }; MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids); From 85003c3850f4f0b1391df324058a7c5fa0927d25 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 15 Sep 2011 12:29:26 +0200 Subject: [PATCH 33/45] hp_accel: Add axis-mapping for HP ProBook / EliteBook Add the corrected axis-mapping for some HP laptops. Signed-off-by: Takashi Iwai Signed-off-by: Matthew Garrett --- drivers/platform/x86/hp_accel.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c index 074e79e4ad98..64b454855f65 100644 --- a/drivers/platform/x86/hp_accel.c +++ b/drivers/platform/x86/hp_accel.c @@ -229,6 +229,10 @@ static struct dmi_system_id lis3lv02d_dmi_ids[] = { AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap), AXIS_DMI_MATCH("HPB532x", "HP ProBook 532", y_inverted), AXIS_DMI_MATCH("Mini510x", "HP Mini 510", xy_rotated_left_usd), + AXIS_DMI_MATCH("HPB63xx", "HP ProBook 63", xy_swap), + AXIS_DMI_MATCH("HPB64xx", "HP ProBook 64", xy_swap), + AXIS_DMI_MATCH("HPB64xx", "HP EliteBook 84", xy_swap), + AXIS_DMI_MATCH("HPB65xx", "HP ProBook 65", x_inverted), { NULL, } /* Laptop models without axis info (yet): * "NC6910" "HP Compaq 6910" From f87d02996f05ec1789ceecce9ec839f629b7aa80 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Tue, 20 Sep 2011 09:16:08 -0700 Subject: [PATCH 34/45] platform: samsung_laptop: add dmi information for Samsung R700 laptops My DMI model is this: >dmesg |grep DMI [ 0.000000] DMI present. [ 0.000000] DMI: SAMSUNG ELECTRONICS CO., LTD. SR700/SR700, BIOS 04SR 02/20/2008 adding dmi information of Samsung R700 laptops This adds the dmi information of Samsungs R700 laptops. Signed-off-by: Stefan Beller Cc: stable Signed-off-by: Greg Kroah-Hartman Signed-off-by: Matthew Garrett --- drivers/platform/x86/samsung-laptop.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index 31ddd57e8ca7..c209f1fe6962 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -698,6 +698,15 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = { }, .callback = dmi_check_cb, }, + { + .ident = "R700", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "SR700"), + DMI_MATCH(DMI_BOARD_NAME, "SR700"), + }, + .callback = dmi_check_cb, + }, { .ident = "R530/R730", .matches = { From 7500eeb08a179e61a4219288c21407d63d1e9c64 Mon Sep 17 00:00:00 2001 From: Tommaso Massimi Date: Tue, 20 Sep 2011 09:16:09 -0700 Subject: [PATCH 35/45] Platform: samsung_laptop: add support for X520 machines. my samsung laptop would be very happy if you add these lines to the file drivers/platform/x86/samsung-laptop.c Cc: stable Signed-off-by: Greg Kroah-Hartman Signed-off-by: Matthew Garrett --- drivers/platform/x86/samsung-laptop.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index c209f1fe6962..09e26bfd4643 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -770,6 +770,15 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = { DMI_MATCH(DMI_BOARD_NAME, "NC210/NC110"), }, .callback = dmi_check_cb, + }, + { + .ident = "X520", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "X520"), + DMI_MATCH(DMI_BOARD_NAME, "X520"), + }, + .callback = dmi_check_cb, }, { }, }; From abec04dbc3dbe7577ccd9d5d6e188aa153d464eb Mon Sep 17 00:00:00 2001 From: Andy Ross Date: Fri, 14 Oct 2011 11:13:37 +0200 Subject: [PATCH 36/45] asus-laptop: allow boot time control of Pegatron ALS sensor Signed-off-by: Andy Ross Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-laptop.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 10b0a8663bb7..8327d06b6e8a 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -85,6 +85,7 @@ static int wlan_status = 1; static int bluetooth_status = 1; static int wimax_status = -1; static int wwan_status = -1; +static int als_status; module_param(wlan_status, int, 0444); MODULE_PARM_DESC(wlan_status, "Set the wireless status on boot " @@ -106,6 +107,11 @@ MODULE_PARM_DESC(wwan_status, "Set the wireless status on boot " "(0 = disabled, 1 = enabled, -1 = don't do anything). " "default is 1"); +module_param(als_status, int, 0444); +MODULE_PARM_DESC(als_status, "Set the ALS status on boot " + "(0 = disabled, 1 = enabled). " + "default is 0"); + /* * Some events we use, same for all Asus */ @@ -1472,7 +1478,7 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus) asus->ledd_status = 0xFFF; /* Set initial values of light sensor and level */ - asus->light_switch = 0; /* Default to light sensor disabled */ + asus->light_switch = !!als_status; asus->light_level = 5; /* level 5 for sensor sensitivity */ if (asus->is_pega_lucid) { From b23910c2194e0e0ee43e585788085f8e6dd4877e Mon Sep 17 00:00:00 2001 From: Andy Ross Date: Fri, 14 Oct 2011 11:13:38 +0200 Subject: [PATCH 37/45] asus-laptop: Pegatron Lucid accelerometer Support the built-in accelerometer on the Lucid tablets as a standard 3-axis input device. Signed-off-by: Andy Ross Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 9 +- drivers/platform/x86/asus-laptop.c | 129 ++++++++++++++++++++++++++++- 2 files changed, 133 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 4e5623e4fa0b..0d4146ca011f 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -69,10 +69,11 @@ config ASUS_LAPTOP This is a driver for Asus laptops, Lenovo SL and the Pegatron Lucid tablet. It may also support some MEDION, JVC or VICTOR laptops. It makes all the extra buttons generate standard - ACPI events and input events. It also adds support for video - output switching, LCD backlight control, Bluetooth and Wlan - control, and most importantly, allows you to blink those - fancy LEDs. + ACPI events and input events, and on the Lucid the built-in + accelerometer appears as an input device. It also adds + support for video output switching, LCD backlight control, + Bluetooth and Wlan control, and most importantly, allows you + to blink those fancy LEDs. For more information see . diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 8327d06b6e8a..613762d825f9 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -193,6 +193,14 @@ MODULE_PARM_DESC(als_status, "Set the ALS status on boot " #define PEGA_READ_ALS_H 0x02 #define PEGA_READ_ALS_L 0x03 +#define PEGA_ACCEL_NAME "pega_accel" +#define PEGA_ACCEL_DESC "Pegatron Lucid Tablet Accelerometer" +#define METHOD_XLRX "XLRX" +#define METHOD_XLRY "XLRY" +#define METHOD_XLRZ "XLRZ" +#define PEGA_ACC_CLAMP 512 /* 1G accel is reported as ~256, so clamp to 2G */ +#define PEGA_ACC_RETRIES 3 + /* * Define a specific led structure to keep the main structure clean */ @@ -218,6 +226,7 @@ struct asus_laptop { struct input_dev *inputdev; struct key_entry *keymap; + struct input_polled_dev *pega_accel_poll; struct asus_led mled; struct asus_led tled; @@ -230,6 +239,10 @@ struct asus_laptop { int wireless_status; bool have_rsts; bool is_pega_lucid; + bool pega_acc_live; + int pega_acc_x; + int pega_acc_y; + int pega_acc_z; struct rfkill *gps_rfkill; @@ -358,6 +371,113 @@ static int asus_pega_lucid_set(struct asus_laptop *asus, int unit, bool enable) return write_acpi_int(asus->handle, method, unit); } +static int pega_acc_axis(struct asus_laptop *asus, int curr, char *method) +{ + int i, delta; + unsigned long long val; + for (i = 0; i < PEGA_ACC_RETRIES; i++) { + acpi_evaluate_integer(asus->handle, method, NULL, &val); + + /* The output is noisy. From reading the ASL + * dissassembly, timeout errors are returned with 1's + * in the high word, and the lack of locking around + * thei hi/lo byte reads means that a transition + * between (for example) -1 and 0 could be read as + * 0xff00 or 0x00ff. */ + delta = abs(curr - (short)val); + if (delta < 128 && !(val & ~0xffff)) + break; + } + return clamp_val((short)val, -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP); +} + +static void pega_accel_poll(struct input_polled_dev *ipd) +{ + struct device *parent = ipd->input->dev.parent; + struct asus_laptop *asus = dev_get_drvdata(parent); + + /* In some cases, the very first call to poll causes a + * recursive fault under the polldev worker. This is + * apparently related to very early userspace access to the + * device, and perhaps a firmware bug. Fake the first report. */ + if (!asus->pega_acc_live) { + asus->pega_acc_live = true; + input_report_abs(ipd->input, ABS_X, 0); + input_report_abs(ipd->input, ABS_Y, 0); + input_report_abs(ipd->input, ABS_Z, 0); + input_sync(ipd->input); + return; + } + + asus->pega_acc_x = pega_acc_axis(asus, asus->pega_acc_x, METHOD_XLRX); + asus->pega_acc_y = pega_acc_axis(asus, asus->pega_acc_y, METHOD_XLRY); + asus->pega_acc_z = pega_acc_axis(asus, asus->pega_acc_z, METHOD_XLRZ); + + /* Note transform, convert to "right/up/out" in the native + * landscape orientation (i.e. the vector is the direction of + * "real up" in the device's cartiesian coordinates). */ + input_report_abs(ipd->input, ABS_X, -asus->pega_acc_x); + input_report_abs(ipd->input, ABS_Y, -asus->pega_acc_y); + input_report_abs(ipd->input, ABS_Z, asus->pega_acc_z); + input_sync(ipd->input); +} + +static void pega_accel_exit(struct asus_laptop *asus) +{ + if (asus->pega_accel_poll) { + input_unregister_polled_device(asus->pega_accel_poll); + input_free_polled_device(asus->pega_accel_poll); + } + asus->pega_accel_poll = NULL; +} + +static int pega_accel_init(struct asus_laptop *asus) +{ + int err; + struct input_polled_dev *ipd; + + if (!asus->is_pega_lucid) + return -ENODEV; + + if (acpi_check_handle(asus->handle, METHOD_XLRX, NULL) || + acpi_check_handle(asus->handle, METHOD_XLRY, NULL) || + acpi_check_handle(asus->handle, METHOD_XLRZ, NULL)) + return -ENODEV; + + ipd = input_allocate_polled_device(); + if (!ipd) + return -ENOMEM; + + ipd->poll = pega_accel_poll; + ipd->poll_interval = 125; + ipd->poll_interval_min = 50; + ipd->poll_interval_max = 2000; + + ipd->input->name = PEGA_ACCEL_DESC; + ipd->input->phys = PEGA_ACCEL_NAME "/input0"; + ipd->input->dev.parent = &asus->platform_device->dev; + ipd->input->id.bustype = BUS_HOST; + + set_bit(EV_ABS, ipd->input->evbit); + input_set_abs_params(ipd->input, ABS_X, + -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0); + input_set_abs_params(ipd->input, ABS_Y, + -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0); + input_set_abs_params(ipd->input, ABS_Z, + -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0); + + err = input_register_polled_device(ipd); + if (err) + goto exit; + + asus->pega_accel_poll = ipd; + return 0; + +exit: + input_free_polled_device(ipd); + return err; +} + /* Generic LED function */ static int asus_led_set(struct asus_laptop *asus, const char *method, int value) @@ -1348,7 +1468,7 @@ static struct platform_driver platform_driver = { .driver = { .name = ASUS_LAPTOP_FILE, .owner = THIS_MODULE, - } + }, }; /* @@ -1558,9 +1678,15 @@ static int __devinit asus_acpi_add(struct acpi_device *device) if (result) goto fail_rfkill; + result = pega_accel_init(asus); + if (result && result != -ENODEV) + goto fail_pega_accel; + asus_device_present = true; return 0; +fail_pega_accel: + asus_rfkill_exit(asus); fail_rfkill: asus_led_exit(asus); fail_led: @@ -1584,6 +1710,7 @@ static int asus_acpi_remove(struct acpi_device *device, int type) asus_rfkill_exit(asus); asus_led_exit(asus); asus_input_exit(asus); + pega_accel_exit(asus); asus_platform_exit(asus); kfree(asus->name); From 8d38e42c61c53ab63abbb15380b39df4eae8d44e Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Fri, 14 Oct 2011 11:13:39 +0200 Subject: [PATCH 38/45] asus-laptop: hide leds on Pegatron Lucid Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-laptop.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 613762d825f9..1fb8e699baf4 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -628,6 +628,13 @@ static int asus_led_init(struct asus_laptop *asus) { int r; + /* + * The Pegatron Lucid has no physical leds, but all methods are + * available in the DSDT... + */ + if (asus->is_pega_lucid) + return 0; + /* * Functions that actually update the LED's are called from a * workqueue. By doing this as separate work rather than when the LED From 16cbf93369a8d148316a75c333bb81bbb64a46c4 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Fri, 14 Oct 2011 11:13:40 +0200 Subject: [PATCH 39/45] asus-laptop: fix module description Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-laptop.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 1fb8e699baf4..012c44d38d48 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -90,22 +90,22 @@ static int als_status; module_param(wlan_status, int, 0444); MODULE_PARM_DESC(wlan_status, "Set the wireless status on boot " "(0 = disabled, 1 = enabled, -1 = don't do anything). " - "default is 1"); + "default is -1"); module_param(bluetooth_status, int, 0444); MODULE_PARM_DESC(bluetooth_status, "Set the wireless status on boot " "(0 = disabled, 1 = enabled, -1 = don't do anything). " - "default is 1"); + "default is -1"); module_param(wimax_status, int, 0444); MODULE_PARM_DESC(wimax_status, "Set the wireless status on boot " "(0 = disabled, 1 = enabled, -1 = don't do anything). " - "default is 1"); + "default is -1"); module_param(wwan_status, int, 0444); MODULE_PARM_DESC(wwan_status, "Set the wireless status on boot " "(0 = disabled, 1 = enabled, -1 = don't do anything). " - "default is 1"); + "default is -1"); module_param(als_status, int, 0444); MODULE_PARM_DESC(als_status, "Set the ALS status on boot " From b93f82816c9ee4868c5aa1c8e06259233916d988 Mon Sep 17 00:00:00 2001 From: Anisse Astier Date: Fri, 14 Oct 2011 11:13:41 +0200 Subject: [PATCH 40/45] asus-laptop: pega_accel - Report accelerometer orientation change through udev Signed-off-by: Anisse Astier Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-laptop.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 012c44d38d48..05c0e594c036 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -1342,6 +1342,14 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event) } return ; } + + /* Accelerometer "coarse orientation change" event */ + if (asus->pega_accel_poll && event == 0xEA) { + kobject_uevent(&asus->pega_accel_poll->input->dev.kobj, + KOBJ_CHANGE); + return ; + } + asus_input_notify(asus, event); } From 149083996e52c640fdfd73ad92550be4b803f349 Mon Sep 17 00:00:00 2001 From: Anisse Astier Date: Fri, 14 Oct 2011 11:13:42 +0200 Subject: [PATCH 41/45] asus-laptop: Add rfkill support for Pegatron Lucid tablet Add three new rfkill switches in this driver that are specific to Pegatron Lucid tablet. Please note that you might not need all three switches. For example if you don't have a 3G module inside your tablet. Also, on my device, the gpio for the wifi/bt module is connected to the bluetooth line. Therefore to activate your wireless lan interface, you need to use the "pega-bt" rfkill switch. Finally, the rfkill switch only works before the wireless module is loaded the first time. Unloading ath9k doesn't help, a reboot is necessary. Signed-off-by: Anisse Astier Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-laptop.c | 103 +++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 05c0e594c036..edaccad9b5bf 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -186,6 +186,9 @@ MODULE_PARM_DESC(als_status, "Set the ALS status on boot " #define METHOD_PEGA_ENABLE "ENPR" #define METHOD_PEGA_DISABLE "DAPR" +#define PEGA_WLAN 0x00 +#define PEGA_BLUETOOTH 0x01 +#define PEGA_WWAN 0x02 #define PEGA_ALS 0x04 #define PEGA_ALS_POWER 0x05 @@ -212,6 +215,15 @@ struct asus_led { const char *method; }; +/* + * Same thing for rfkill + */ +struct asus_pega_rfkill { + int control_id; /* type of control. Maps to PEGA_* values */ + struct rfkill *rfkill; + struct asus_laptop *asus; +}; + /* * This is the main structure, we can use it to store anything interesting * about the hotk device @@ -246,6 +258,10 @@ struct asus_laptop { struct rfkill *gps_rfkill; + struct asus_pega_rfkill wlanrfk; + struct asus_pega_rfkill btrfk; + struct asus_pega_rfkill wwanrfk; + acpi_handle handle; /* the handle of the hotk device */ u32 ledd_status; /* status of the LED display */ u8 light_level; /* light sensor level */ @@ -1263,6 +1279,86 @@ static int asus_rfkill_init(struct asus_laptop *asus) return result; } +static int pega_rfkill_set(void *data, bool blocked) +{ + struct asus_pega_rfkill *pega_rfk = data; + + int ret = asus_pega_lucid_set(pega_rfk->asus, pega_rfk->control_id, !blocked); + pr_warn("Setting rfkill %d, to %d; returned %d\n", pega_rfk->control_id, !blocked, ret); + + return ret; +} + +static const struct rfkill_ops pega_rfkill_ops = { + .set_block = pega_rfkill_set, +}; + +static void pega_rfkill_terminate(struct asus_pega_rfkill *pega_rfk) +{ + pr_warn("Terminating %d\n", pega_rfk->control_id); + if (pega_rfk->rfkill) { + rfkill_unregister(pega_rfk->rfkill); + rfkill_destroy(pega_rfk->rfkill); + pega_rfk->rfkill = NULL; + } +} + +static void pega_rfkill_exit(struct asus_laptop *asus) +{ + pega_rfkill_terminate(&asus->wwanrfk); + pega_rfkill_terminate(&asus->btrfk); + pega_rfkill_terminate(&asus->wlanrfk); +} + +static int pega_rfkill_setup(struct asus_laptop *asus, struct asus_pega_rfkill *pega_rfk, + const char *name, int controlid, int rfkill_type) +{ + int result; + + pr_warn("Setting up rfk %s, control %d, type %d\n", name, controlid, rfkill_type); + pega_rfk->control_id = controlid; + pega_rfk->asus = asus; + pega_rfk->rfkill = rfkill_alloc(name, &asus->platform_device->dev, + rfkill_type, &pega_rfkill_ops, pega_rfk); + if (!pega_rfk->rfkill) + return -EINVAL; + + result = rfkill_register(pega_rfk->rfkill); + if (result) { + rfkill_destroy(pega_rfk->rfkill); + pega_rfk->rfkill = NULL; + } + + return result; +} + +static int pega_rfkill_init(struct asus_laptop *asus) +{ + int ret = 0; + + if(!asus->is_pega_lucid) + return -ENODEV; + + ret = pega_rfkill_setup(asus, &asus->wlanrfk, "pega-wlan", PEGA_WLAN, RFKILL_TYPE_WLAN); + if(ret) + return ret; + ret = pega_rfkill_setup(asus, &asus->btrfk, "pega-bt", PEGA_BLUETOOTH, RFKILL_TYPE_BLUETOOTH); + if(ret) + goto err_btrfk; + ret = pega_rfkill_setup(asus, &asus->wwanrfk, "pega-wwan", PEGA_WWAN, RFKILL_TYPE_WWAN); + if(ret) + goto err_wwanrfk; + + pr_warn("Pega rfkill init succeeded\n"); + return 0; +err_wwanrfk: + pega_rfkill_terminate(&asus->btrfk); +err_btrfk: + pega_rfkill_terminate(&asus->wlanrfk); + + return ret; +} + /* * Input device (i.e. hotkeys) */ @@ -1697,9 +1793,15 @@ static int __devinit asus_acpi_add(struct acpi_device *device) if (result && result != -ENODEV) goto fail_pega_accel; + result = pega_rfkill_init(asus); + if (result && result != -ENODEV) + goto fail_pega_rfkill; + asus_device_present = true; return 0; +fail_pega_rfkill: + pega_accel_exit(asus); fail_pega_accel: asus_rfkill_exit(asus); fail_rfkill: @@ -1726,6 +1828,7 @@ static int asus_acpi_remove(struct acpi_device *device, int type) asus_led_exit(asus); asus_input_exit(asus); pega_accel_exit(asus); + pega_rfkill_exit(asus); asus_platform_exit(asus); kfree(asus->name); From 6a0d89c2641671a5d29d262dbc7db8e403b543be Mon Sep 17 00:00:00 2001 From: Yong Zhang Date: Sat, 22 Oct 2011 17:56:50 +0800 Subject: [PATCH 42/45] sony-laptop:irq: Remove IRQF_DISABLED Since commit [e58aa3d2: genirq: Run irq handlers with interrupts disabled], We run all interrupt handlers with interrupts disabled and we even check and yell when an interrupt handler returns with interrupts enabled (see commit [b738a50a: genirq: Warn when handler enables interrupts]). So now this flag is a NOOP and can be removed. Signed-off-by: Yong Zhang Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index bbd182e178cb..c006dee5ebfe 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -3281,7 +3281,7 @@ static int sony_pic_add(struct acpi_device *device) /* request IRQ */ list_for_each_entry_reverse(irq, &spic_dev.interrupts, list) { if (!request_irq(irq->irq.interrupts[0], sony_pic_irq, - IRQF_DISABLED, "sony-laptop", &spic_dev)) { + 0, "sony-laptop", &spic_dev)) { dprintk("IRQ: %d - triggering: %d - " "polarity: %d - shr: %d\n", irq->irq.interrupts[0], From 72e71de15fceb0fd7cdfd8bb35f4f8aa8b0e20b5 Mon Sep 17 00:00:00 2001 From: "Lee, Chun-Yi" Date: Thu, 18 Aug 2011 18:47:32 +0800 Subject: [PATCH 43/45] acer-wmi: add ACER_WMID_v2 interface flag to represent new notebooks There have new acer notebooks' BIOS provide new WMID_GUID3 and ACERWMID_EVENT_GUID methods. Some of machines still keep the old WMID_GUID1 method but more and more machines were already removed old wmi methods from DSDT. So, this patch add a new ACER_WMID_v2 interface flag to represent new acer notebooks, the following is definition: + ACER_WMID: It means this machine only provides WMID_GUID1/2 methods. + ACER_WMID_v2: It means this machine provide new WMID_GUID3 and WMID_EVENT_GUID methods. Some ACER_WMID_v2 machines also provide old WMID_GUID1/2 methods, but we still query/set communication device's state by new WMID_GUID3 method. Tested on Acer Travelmate 8572 Tested on Acer Aspire 4739Z Tested-by: AceLan Kao Cc: Carlos Corbacho Cc: Matthew Garrett Cc: Dmitry Torokhov Cc: Corentin Chary Cc: Thomas Renninger Signed-off-by: Lee, Chun-Yi Signed-off-by: Matthew Garrett --- drivers/platform/x86/acer-wmi.c | 416 ++++++++++++++++---------------- 1 file changed, 213 insertions(+), 203 deletions(-) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index fc7c9733bba5..017b1a7eac8e 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -190,6 +190,7 @@ enum interface_flags { ACER_AMW0, ACER_AMW0_V2, ACER_WMID, + ACER_WMID_v2, }; #define ACER_DEFAULT_WIRELESS 0 @@ -877,6 +878,177 @@ static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface) return WMI_execute_u32(method_id, (u32)value, NULL); } +static acpi_status wmid3_get_device_status(u32 *value, u16 device) +{ + struct wmid3_gds_return_value return_value; + acpi_status status; + union acpi_object *obj; + struct wmid3_gds_input_param params = { + .function_num = 0x1, + .hotkey_number = 0x01, + .devices = device, + }; + struct acpi_buffer input = { + sizeof(struct wmid3_gds_input_param), + ¶ms + }; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + + status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output); + if (ACPI_FAILURE(status)) + return status; + + obj = output.pointer; + + if (!obj) + return AE_ERROR; + else if (obj->type != ACPI_TYPE_BUFFER) { + kfree(obj); + return AE_ERROR; + } + if (obj->buffer.length != 8) { + pr_warn("Unknown buffer length %d\n", obj->buffer.length); + kfree(obj); + return AE_ERROR; + } + + return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer); + kfree(obj); + + if (return_value.error_code || return_value.ec_return_value) + pr_warn("Get 0x%x Device Status failed: 0x%x - 0x%x\n", + device, + return_value.error_code, + return_value.ec_return_value); + else + *value = !!(return_value.devices & device); + + return status; +} + +static acpi_status wmid_v2_get_u32(u32 *value, u32 cap) +{ + u16 device; + + switch (cap) { + case ACER_CAP_WIRELESS: + device = ACER_WMID3_GDS_WIRELESS; + break; + case ACER_CAP_BLUETOOTH: + device = ACER_WMID3_GDS_BLUETOOTH; + break; + case ACER_CAP_THREEG: + device = ACER_WMID3_GDS_THREEG; + break; + default: + return AE_ERROR; + } + return wmid3_get_device_status(value, device); +} + +static acpi_status wmid3_set_device_status(u32 value, u16 device) +{ + struct wmid3_gds_return_value return_value; + acpi_status status; + union acpi_object *obj; + u16 devices; + struct wmid3_gds_input_param params = { + .function_num = 0x1, + .hotkey_number = 0x01, + .devices = ACER_WMID3_GDS_WIRELESS | + ACER_WMID3_GDS_THREEG | + ACER_WMID3_GDS_WIMAX | + ACER_WMID3_GDS_BLUETOOTH, + }; + struct acpi_buffer input = { + sizeof(struct wmid3_gds_input_param), + ¶ms + }; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_buffer output2 = { ACPI_ALLOCATE_BUFFER, NULL }; + + status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output); + if (ACPI_FAILURE(status)) + return status; + + obj = output.pointer; + + if (!obj) + return AE_ERROR; + else if (obj->type != ACPI_TYPE_BUFFER) { + kfree(obj); + return AE_ERROR; + } + if (obj->buffer.length != 8) { + pr_warning("Unknown buffer length %d\n", obj->buffer.length); + kfree(obj); + return AE_ERROR; + } + + return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer); + kfree(obj); + + if (return_value.error_code || return_value.ec_return_value) { + pr_warning("Get Current Device Status failed: " + "0x%x - 0x%x\n", return_value.error_code, + return_value.ec_return_value); + return status; + } + + devices = return_value.devices; + params.function_num = 0x2; + params.hotkey_number = 0x01; + params.devices = (value) ? (devices | device) : (devices & ~device); + + status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output2); + if (ACPI_FAILURE(status)) + return status; + + obj = output2.pointer; + + if (!obj) + return AE_ERROR; + else if (obj->type != ACPI_TYPE_BUFFER) { + kfree(obj); + return AE_ERROR; + } + if (obj->buffer.length != 4) { + pr_warning("Unknown buffer length %d\n", obj->buffer.length); + kfree(obj); + return AE_ERROR; + } + + return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer); + kfree(obj); + + if (return_value.error_code || return_value.ec_return_value) + pr_warning("Set Device Status failed: " + "0x%x - 0x%x\n", return_value.error_code, + return_value.ec_return_value); + + return status; +} + +static acpi_status wmid_v2_set_u32(u32 value, u32 cap) +{ + u16 device; + + switch (cap) { + case ACER_CAP_WIRELESS: + device = ACER_WMID3_GDS_WIRELESS; + break; + case ACER_CAP_BLUETOOTH: + device = ACER_WMID3_GDS_BLUETOOTH; + break; + case ACER_CAP_THREEG: + device = ACER_WMID3_GDS_THREEG; + break; + default: + return AE_ERROR; + } + return wmid3_set_device_status(value, device); +} + static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy) { struct hotkey_function_type_aa *type_aa; @@ -922,17 +1094,11 @@ static acpi_status WMID_set_capabilities(void) return AE_ERROR; } - dmi_walk(type_aa_dmi_decode, NULL); - if (!has_type_aa) { - interface->capability |= ACER_CAP_WIRELESS; - if (devices & 0x40) - interface->capability |= ACER_CAP_THREEG; - if (devices & 0x10) - interface->capability |= ACER_CAP_BLUETOOTH; - } - - /* WMID always provides brightness methods */ - interface->capability |= ACER_CAP_BRIGHTNESS; + interface->capability |= ACER_CAP_WIRELESS; + if (devices & 0x40) + interface->capability |= ACER_CAP_THREEG; + if (devices & 0x10) + interface->capability |= ACER_CAP_BLUETOOTH; if (!(devices & 0x20)) max_brightness = 0x9; @@ -945,6 +1111,10 @@ static struct wmi_interface wmid_interface = { .type = ACER_WMID, }; +static struct wmi_interface wmid_v2_interface = { + .type = ACER_WMID_v2, +}; + /* * Generic Device (interface-independent) */ @@ -965,6 +1135,14 @@ static acpi_status get_u32(u32 *value, u32 cap) case ACER_WMID: status = WMID_get_u32(value, cap, interface); break; + case ACER_WMID_v2: + if (cap & (ACER_CAP_WIRELESS | + ACER_CAP_BLUETOOTH | + ACER_CAP_THREEG)) + status = wmid_v2_get_u32(value, cap); + else if (wmi_has_guid(WMID_GUID2)) + status = WMID_get_u32(value, cap, interface); + break; } return status; @@ -998,6 +1176,13 @@ static acpi_status set_u32(u32 value, u32 cap) } case ACER_WMID: return WMID_set_u32(value, cap, interface); + case ACER_WMID_v2: + if (cap & (ACER_CAP_WIRELESS | + ACER_CAP_BLUETOOTH | + ACER_CAP_THREEG)) + return wmid_v2_set_u32(value, cap); + else if (wmi_has_guid(WMID_GUID2)) + return WMID_set_u32(value, cap, interface); default: return AE_BAD_PARAMETER; } @@ -1104,186 +1289,6 @@ static void acer_backlight_exit(void) backlight_device_unregister(acer_backlight_device); } -static acpi_status wmid3_get_device_status(u32 *value, u16 device) -{ - struct wmid3_gds_return_value return_value; - acpi_status status; - union acpi_object *obj; - struct wmid3_gds_input_param params = { - .function_num = 0x1, - .hotkey_number = 0x01, - .devices = device, - }; - struct acpi_buffer input = { - sizeof(struct wmid3_gds_input_param), - ¶ms - }; - struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; - - status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output); - if (ACPI_FAILURE(status)) - return status; - - obj = output.pointer; - - if (!obj) - return AE_ERROR; - else if (obj->type != ACPI_TYPE_BUFFER) { - kfree(obj); - return AE_ERROR; - } - if (obj->buffer.length != 8) { - pr_warn("Unknown buffer length %d\n", obj->buffer.length); - kfree(obj); - return AE_ERROR; - } - - return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer); - kfree(obj); - - if (return_value.error_code || return_value.ec_return_value) - pr_warn("Get Device Status failed: 0x%x - 0x%x\n", - return_value.error_code, - return_value.ec_return_value); - else - *value = !!(return_value.devices & device); - - return status; -} - -static acpi_status get_device_status(u32 *value, u32 cap) -{ - if (wmi_has_guid(WMID_GUID3)) { - u16 device; - - switch (cap) { - case ACER_CAP_WIRELESS: - device = ACER_WMID3_GDS_WIRELESS; - break; - case ACER_CAP_BLUETOOTH: - device = ACER_WMID3_GDS_BLUETOOTH; - break; - case ACER_CAP_THREEG: - device = ACER_WMID3_GDS_THREEG; - break; - default: - return AE_ERROR; - } - return wmid3_get_device_status(value, device); - - } else { - return get_u32(value, cap); - } -} - -static acpi_status wmid3_set_device_status(u32 value, u16 device) -{ - struct wmid3_gds_return_value return_value; - acpi_status status; - union acpi_object *obj; - u16 devices; - struct wmid3_gds_input_param params = { - .function_num = 0x1, - .hotkey_number = 0x01, - .devices = ACER_WMID3_GDS_WIRELESS | - ACER_WMID3_GDS_THREEG | - ACER_WMID3_GDS_WIMAX | - ACER_WMID3_GDS_BLUETOOTH, - }; - struct acpi_buffer input = { - sizeof(struct wmid3_gds_input_param), - ¶ms - }; - struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; - struct acpi_buffer output2 = { ACPI_ALLOCATE_BUFFER, NULL }; - - status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output); - if (ACPI_FAILURE(status)) - return status; - - obj = output.pointer; - - if (!obj) - return AE_ERROR; - else if (obj->type != ACPI_TYPE_BUFFER) { - kfree(obj); - return AE_ERROR; - } - if (obj->buffer.length != 8) { - pr_warning("Unknown buffer length %d\n", obj->buffer.length); - kfree(obj); - return AE_ERROR; - } - - return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer); - kfree(obj); - - if (return_value.error_code || return_value.ec_return_value) { - pr_warning("Get Current Device Status failed: " - "0x%x - 0x%x\n", return_value.error_code, - return_value.ec_return_value); - return status; - } - - devices = return_value.devices; - params.function_num = 0x2; - params.hotkey_number = 0x01; - params.devices = (value) ? (devices | device) : (devices & ~device); - - status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output2); - if (ACPI_FAILURE(status)) - return status; - - obj = output2.pointer; - - if (!obj) - return AE_ERROR; - else if (obj->type != ACPI_TYPE_BUFFER) { - kfree(obj); - return AE_ERROR; - } - if (obj->buffer.length != 4) { - pr_warning("Unknown buffer length %d\n", obj->buffer.length); - kfree(obj); - return AE_ERROR; - } - - return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer); - kfree(obj); - - if (return_value.error_code || return_value.ec_return_value) - pr_warning("Set Device Status failed: " - "0x%x - 0x%x\n", return_value.error_code, - return_value.ec_return_value); - - return status; -} - -static acpi_status set_device_status(u32 value, u32 cap) -{ - if (wmi_has_guid(WMID_GUID3)) { - u16 device; - - switch (cap) { - case ACER_CAP_WIRELESS: - device = ACER_WMID3_GDS_WIRELESS; - break; - case ACER_CAP_BLUETOOTH: - device = ACER_WMID3_GDS_BLUETOOTH; - break; - case ACER_CAP_THREEG: - device = ACER_WMID3_GDS_THREEG; - break; - default: - return AE_ERROR; - } - return wmid3_set_device_status(value, device); - - } else { - return set_u32(value, cap); - } -} - /* * Rfkill devices */ @@ -1311,8 +1316,7 @@ static void acer_rfkill_update(struct work_struct *ignored) } if (has_cap(ACER_CAP_THREEG) && wmi_has_guid(WMID_GUID3)) { - status = wmid3_get_device_status(&state, - ACER_WMID3_GDS_THREEG); + status = get_u32(&state, ACER_WMID3_GDS_THREEG); if (ACPI_SUCCESS(status)) rfkill_set_sw_state(threeg_rfkill, !state); } @@ -1326,7 +1330,7 @@ static int acer_rfkill_set(void *data, bool blocked) u32 cap = (unsigned long)data; if (rfkill_inited) { - status = set_device_status(!blocked, cap); + status = set_u32(!blocked, cap); if (ACPI_FAILURE(status)) return -ENODEV; } @@ -1353,7 +1357,7 @@ static struct rfkill *acer_rfkill_register(struct device *dev, if (!rfkill_dev) return ERR_PTR(-ENOMEM); - status = get_device_status(&state, cap); + status = get_u32(&state, cap); err = rfkill_register(rfkill_dev); if (err) { @@ -1457,11 +1461,7 @@ static ssize_t show_bool_threeg(struct device *dev, pr_info("This threeg sysfs will be removed in 2012" " - used by: %s\n", current->comm); - if (wmi_has_guid(WMID_GUID3)) - status = wmid3_get_device_status(&result, - ACER_WMID3_GDS_THREEG); - else - status = get_u32(&result, ACER_CAP_THREEG); + status = get_u32(&result, ACER_CAP_THREEG); if (ACPI_SUCCESS(status)) return sprintf(buf, "%u\n", result); return sprintf(buf, "Read error\n"); @@ -1493,6 +1493,8 @@ static ssize_t show_interface(struct device *dev, struct device_attribute *attr, return sprintf(buf, "AMW0 v2\n"); case ACER_WMID: return sprintf(buf, "WMID\n"); + case ACER_WMID_v2: + return sprintf(buf, "WMID v2\n"); default: return sprintf(buf, "Error!\n"); } @@ -1912,12 +1914,20 @@ static int __init acer_wmi_init(void) if (!wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1)) interface = &wmid_interface; + if (wmi_has_guid(WMID_GUID3)) + interface = &wmid_v2_interface; + + if (interface) + dmi_walk(type_aa_dmi_decode, NULL); + if (wmi_has_guid(WMID_GUID2) && interface) { - if (ACPI_FAILURE(WMID_set_capabilities())) { + if (!has_type_aa && ACPI_FAILURE(WMID_set_capabilities())) { pr_err("Unable to detect available WMID devices\n"); return -ENODEV; } - } else if (!wmi_has_guid(WMID_GUID2) && interface) { + /* WMID always provides brightness methods */ + interface->capability |= ACER_CAP_BRIGHTNESS; + } else if (!wmi_has_guid(WMID_GUID2) && interface && !has_type_aa) { pr_err("No WMID device detection method found\n"); return -ENODEV; } @@ -1941,7 +1951,7 @@ static int __init acer_wmi_init(void) set_quirks(); - if (acpi_video_backlight_support() && has_cap(ACER_CAP_BRIGHTNESS)) { + if (acpi_video_backlight_support()) { interface->capability &= ~ACER_CAP_BRIGHTNESS; pr_info("Brightness must be controlled by " "generic video driver\n"); From 1fbc01a7b0f2cf5dc375ecde3833df2fbbfdc812 Mon Sep 17 00:00:00 2001 From: "Lee, Chun-Yi" Date: Thu, 18 Aug 2011 18:47:34 +0800 Subject: [PATCH 44/45] acer-wmi: check the existence of internal wireless device when set capability That will be better to check the existence of internal wireless device when we set wireless capability and generate killswitch for it. It can avoid userland access wireless rfkill but the machine doesn't have internal wireless device. Tested on Acer Travelmate 8572 Cc: Carlos Corbacho Cc: Matthew Garrett Cc: Dmitry Torokhov Cc: Corentin Chary Cc: Thomas Renninger Signed-off-by: Lee, Chun-Yi Signed-off-by: Matthew Garrett --- drivers/platform/x86/acer-wmi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 017b1a7eac8e..ca52639af2d1 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -1094,7 +1094,9 @@ static acpi_status WMID_set_capabilities(void) return AE_ERROR; } - interface->capability |= ACER_CAP_WIRELESS; + pr_info("Function bitmap for Communication Device: 0x%x\n", devices); + if (devices & 0x07) + interface->capability |= ACER_CAP_WIRELESS; if (devices & 0x40) interface->capability |= ACER_CAP_THREEG; if (devices & 0x10) From 1d1fc8a75fec3a2b29cc5864d45c5d03048f62e0 Mon Sep 17 00:00:00 2001 From: "Lee, Chun-Yi" Date: Thu, 6 Oct 2011 19:06:13 +0800 Subject: [PATCH 45/45] acer-wmi: replaced the hard coded bitmap by the communication devices bitmap from SMBIOS Before set communication devices state, we need query out all devices state to set the states bitmap. That will be better use the devices bitmap in SMBIOS type Aah instead of hardcode in driver. Tested on Acer Travelmate 8572. Cc: Carlos Corbacho Cc: Matthew Garrett Cc: Dmitry Torokhov Cc: Corentin Chary Cc: Thomas Renninger Signed-off-by: Lee, Chun-Yi Signed-off-by: Matthew Garrett --- drivers/platform/x86/acer-wmi.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index ca52639af2d1..b848277171a4 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -206,6 +206,7 @@ static int threeg = -1; static int force_series; static bool ec_raw_mode; static bool has_type_aa; +static u16 commun_func_bitmap; module_param(mailled, int, 0444); module_param(brightness, int, 0444); @@ -955,10 +956,7 @@ static acpi_status wmid3_set_device_status(u32 value, u16 device) struct wmid3_gds_input_param params = { .function_num = 0x1, .hotkey_number = 0x01, - .devices = ACER_WMID3_GDS_WIRELESS | - ACER_WMID3_GDS_THREEG | - ACER_WMID3_GDS_WIMAX | - ACER_WMID3_GDS_BLUETOOTH, + .devices = commun_func_bitmap, }; struct acpi_buffer input = { sizeof(struct wmid3_gds_input_param), @@ -1062,6 +1060,7 @@ static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy) pr_info("Function bitmap for Communication Button: 0x%x\n", type_aa->commun_func_bitmap); + commun_func_bitmap = type_aa->commun_func_bitmap; if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_WIRELESS) interface->capability |= ACER_CAP_WIRELESS;