diff options
-rw-r--r-- | drivers/acpi/blacklist.c | 54 | ||||
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 116 |
2 files changed, 106 insertions, 64 deletions
diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c index 7556e7c4a055..9b693d54c743 100644 --- a/drivers/acpi/blacklist.c +++ b/drivers/acpi/blacklist.c | |||
@@ -305,60 +305,6 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { | |||
305 | */ | 305 | */ |
306 | 306 | ||
307 | /* | 307 | /* |
308 | * Lenovo has a mix of systems OSI(Linux) situations | ||
309 | * and thus we can not wildcard the vendor. | ||
310 | * | ||
311 | * _OSI(Linux) helps sound | ||
312 | * DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad R61"), | ||
313 | * DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T61"), | ||
314 | * T400, T500 | ||
315 | * _OSI(Linux) has Linux specific hooks | ||
316 | * DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X61"), | ||
317 | * _OSI(Linux) is a NOP: | ||
318 | * DMI_MATCH(DMI_PRODUCT_VERSION, "3000 N100"), | ||
319 | * DMI_MATCH(DMI_PRODUCT_VERSION, "LENOVO3000 V100"), | ||
320 | */ | ||
321 | { | ||
322 | .callback = dmi_enable_osi_linux, | ||
323 | .ident = "Lenovo ThinkPad R61", | ||
324 | .matches = { | ||
325 | DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), | ||
326 | DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad R61"), | ||
327 | }, | ||
328 | }, | ||
329 | { | ||
330 | .callback = dmi_enable_osi_linux, | ||
331 | .ident = "Lenovo ThinkPad T61", | ||
332 | .matches = { | ||
333 | DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), | ||
334 | DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T61"), | ||
335 | }, | ||
336 | }, | ||
337 | { | ||
338 | .callback = dmi_enable_osi_linux, | ||
339 | .ident = "Lenovo ThinkPad X61", | ||
340 | .matches = { | ||
341 | DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), | ||
342 | DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X61"), | ||
343 | }, | ||
344 | }, | ||
345 | { | ||
346 | .callback = dmi_enable_osi_linux, | ||
347 | .ident = "Lenovo ThinkPad T400", | ||
348 | .matches = { | ||
349 | DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), | ||
350 | DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T400"), | ||
351 | }, | ||
352 | }, | ||
353 | { | ||
354 | .callback = dmi_enable_osi_linux, | ||
355 | .ident = "Lenovo ThinkPad T500", | ||
356 | .matches = { | ||
357 | DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), | ||
358 | DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T500"), | ||
359 | }, | ||
360 | }, | ||
361 | /* | ||
362 | * Without this this EEEpc exports a non working WMI interface, with | 308 | * Without this this EEEpc exports a non working WMI interface, with |
363 | * this it exports a working "good old" eeepc_laptop interface, fixing | 309 | * this it exports a working "good old" eeepc_laptop interface, fixing |
364 | * both brightness control, and rfkill not working. | 310 | * both brightness control, and rfkill not working. |
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index cf0f89364d44..bdabd3fd844c 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c | |||
@@ -6559,6 +6559,17 @@ static struct ibm_struct brightness_driver_data = { | |||
6559 | * bits 3-0 (volume). Other bits in NVRAM may have other functions, | 6559 | * bits 3-0 (volume). Other bits in NVRAM may have other functions, |
6560 | * such as bit 7 which is used to detect repeated presses of MUTE, | 6560 | * such as bit 7 which is used to detect repeated presses of MUTE, |
6561 | * and we leave them unchanged. | 6561 | * and we leave them unchanged. |
6562 | * | ||
6563 | * On newer Lenovo ThinkPads, the EC can automatically change the volume | ||
6564 | * in response to user input. Unfortunately, this rarely works well. | ||
6565 | * The laptop changes the state of its internal MUTE gate and, on some | ||
6566 | * models, sends KEY_MUTE, causing any user code that responds to the | ||
6567 | * mute button to get confused. The hardware MUTE gate is also | ||
6568 | * unnecessary, since user code can handle the mute button without | ||
6569 | * kernel or EC help. | ||
6570 | * | ||
6571 | * To avoid confusing userspace, we simply disable all EC-based mute | ||
6572 | * and volume controls when possible. | ||
6562 | */ | 6573 | */ |
6563 | 6574 | ||
6564 | #ifdef CONFIG_THINKPAD_ACPI_ALSA_SUPPORT | 6575 | #ifdef CONFIG_THINKPAD_ACPI_ALSA_SUPPORT |
@@ -6613,11 +6624,21 @@ enum tpacpi_volume_capabilities { | |||
6613 | TPACPI_VOL_CAP_MAX | 6624 | TPACPI_VOL_CAP_MAX |
6614 | }; | 6625 | }; |
6615 | 6626 | ||
6627 | enum tpacpi_mute_btn_mode { | ||
6628 | TP_EC_MUTE_BTN_LATCH = 0, /* Mute mutes; up/down unmutes */ | ||
6629 | /* We don't know what mode 1 is. */ | ||
6630 | TP_EC_MUTE_BTN_NONE = 2, /* Mute and up/down are just keys */ | ||
6631 | TP_EC_MUTE_BTN_TOGGLE = 3, /* Mute toggles; up/down unmutes */ | ||
6632 | }; | ||
6633 | |||
6616 | static enum tpacpi_volume_access_mode volume_mode = | 6634 | static enum tpacpi_volume_access_mode volume_mode = |
6617 | TPACPI_VOL_MODE_MAX; | 6635 | TPACPI_VOL_MODE_MAX; |
6618 | 6636 | ||
6619 | static enum tpacpi_volume_capabilities volume_capabilities; | 6637 | static enum tpacpi_volume_capabilities volume_capabilities; |
6620 | static bool volume_control_allowed; | 6638 | static bool volume_control_allowed; |
6639 | static bool software_mute_requested = true; | ||
6640 | static bool software_mute_active; | ||
6641 | static int software_mute_orig_mode; | ||
6621 | 6642 | ||
6622 | /* | 6643 | /* |
6623 | * Used to syncronize writers to TP_EC_AUDIO and | 6644 | * Used to syncronize writers to TP_EC_AUDIO and |
@@ -6635,6 +6656,8 @@ static void tpacpi_volume_checkpoint_nvram(void) | |||
6635 | return; | 6656 | return; |
6636 | if (!volume_control_allowed) | 6657 | if (!volume_control_allowed) |
6637 | return; | 6658 | return; |
6659 | if (software_mute_active) | ||
6660 | return; | ||
6638 | 6661 | ||
6639 | vdbg_printk(TPACPI_DBG_MIXER, | 6662 | vdbg_printk(TPACPI_DBG_MIXER, |
6640 | "trying to checkpoint mixer state to NVRAM...\n"); | 6663 | "trying to checkpoint mixer state to NVRAM...\n"); |
@@ -6696,6 +6719,12 @@ static int volume_set_status_ec(const u8 status) | |||
6696 | 6719 | ||
6697 | dbg_printk(TPACPI_DBG_MIXER, "set EC mixer to 0x%02x\n", status); | 6720 | dbg_printk(TPACPI_DBG_MIXER, "set EC mixer to 0x%02x\n", status); |
6698 | 6721 | ||
6722 | /* | ||
6723 | * On X200s, and possibly on others, it can take a while for | ||
6724 | * reads to become correct. | ||
6725 | */ | ||
6726 | msleep(1); | ||
6727 | |||
6699 | return 0; | 6728 | return 0; |
6700 | } | 6729 | } |
6701 | 6730 | ||
@@ -6778,6 +6807,57 @@ unlock: | |||
6778 | return rc; | 6807 | return rc; |
6779 | } | 6808 | } |
6780 | 6809 | ||
6810 | static int volume_set_software_mute(bool startup) | ||
6811 | { | ||
6812 | int result; | ||
6813 | |||
6814 | if (!tpacpi_is_lenovo()) | ||
6815 | return -ENODEV; | ||
6816 | |||
6817 | if (startup) { | ||
6818 | if (!acpi_evalf(ec_handle, &software_mute_orig_mode, | ||
6819 | "HAUM", "qd")) | ||
6820 | return -EIO; | ||
6821 | |||
6822 | dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, | ||
6823 | "Initial HAUM setting was %d\n", | ||
6824 | software_mute_orig_mode); | ||
6825 | } | ||
6826 | |||
6827 | if (!acpi_evalf(ec_handle, &result, "SAUM", "qdd", | ||
6828 | (int)TP_EC_MUTE_BTN_NONE)) | ||
6829 | return -EIO; | ||
6830 | |||
6831 | if (result != TP_EC_MUTE_BTN_NONE) | ||
6832 | pr_warn("Unexpected SAUM result %d\n", | ||
6833 | result); | ||
6834 | |||
6835 | /* | ||
6836 | * In software mute mode, the standard codec controls take | ||
6837 | * precendence, so we unmute the ThinkPad HW switch at | ||
6838 | * startup. Just on case there are SAUM-capable ThinkPads | ||
6839 | * with level controls, set max HW volume as well. | ||
6840 | */ | ||
6841 | if (tp_features.mixer_no_level_control) | ||
6842 | result = volume_set_mute(false); | ||
6843 | else | ||
6844 | result = volume_set_status(TP_EC_VOLUME_MAX); | ||
6845 | |||
6846 | if (result != 0) | ||
6847 | pr_warn("Failed to unmute the HW mute switch\n"); | ||
6848 | |||
6849 | return 0; | ||
6850 | } | ||
6851 | |||
6852 | static void volume_exit_software_mute(void) | ||
6853 | { | ||
6854 | int r; | ||
6855 | |||
6856 | if (!acpi_evalf(ec_handle, &r, "SAUM", "qdd", software_mute_orig_mode) | ||
6857 | || r != software_mute_orig_mode) | ||
6858 | pr_warn("Failed to restore mute mode\n"); | ||
6859 | } | ||
6860 | |||
6781 | static int volume_alsa_set_volume(const u8 vol) | 6861 | static int volume_alsa_set_volume(const u8 vol) |
6782 | { | 6862 | { |
6783 | dbg_printk(TPACPI_DBG_MIXER, | 6863 | dbg_printk(TPACPI_DBG_MIXER, |
@@ -6885,7 +6965,12 @@ static void volume_suspend(void) | |||
6885 | 6965 | ||
6886 | static void volume_resume(void) | 6966 | static void volume_resume(void) |
6887 | { | 6967 | { |
6888 | volume_alsa_notify_change(); | 6968 | if (software_mute_active) { |
6969 | if (volume_set_software_mute(false) < 0) | ||
6970 | pr_warn("Failed to restore software mute\n"); | ||
6971 | } else { | ||
6972 | volume_alsa_notify_change(); | ||
6973 | } | ||
6889 | } | 6974 | } |
6890 | 6975 | ||
6891 | static void volume_shutdown(void) | 6976 | static void volume_shutdown(void) |
@@ -6901,6 +6986,9 @@ static void volume_exit(void) | |||
6901 | } | 6986 | } |
6902 | 6987 | ||
6903 | tpacpi_volume_checkpoint_nvram(); | 6988 | tpacpi_volume_checkpoint_nvram(); |
6989 | |||
6990 | if (software_mute_active) | ||
6991 | volume_exit_software_mute(); | ||
6904 | } | 6992 | } |
6905 | 6993 | ||
6906 | static int __init volume_create_alsa_mixer(void) | 6994 | static int __init volume_create_alsa_mixer(void) |
@@ -7085,16 +7173,20 @@ static int __init volume_init(struct ibm_init_struct *iibm) | |||
7085 | "mute is supported, volume control is %s\n", | 7173 | "mute is supported, volume control is %s\n", |
7086 | str_supported(!tp_features.mixer_no_level_control)); | 7174 | str_supported(!tp_features.mixer_no_level_control)); |
7087 | 7175 | ||
7088 | rc = volume_create_alsa_mixer(); | 7176 | if (software_mute_requested && volume_set_software_mute(true) == 0) { |
7089 | if (rc) { | 7177 | software_mute_active = true; |
7090 | pr_err("Could not create the ALSA mixer interface\n"); | 7178 | } else { |
7091 | return rc; | 7179 | rc = volume_create_alsa_mixer(); |
7092 | } | 7180 | if (rc) { |
7181 | pr_err("Could not create the ALSA mixer interface\n"); | ||
7182 | return rc; | ||
7183 | } | ||
7093 | 7184 | ||
7094 | pr_info("Console audio control enabled, mode: %s\n", | 7185 | pr_info("Console audio control enabled, mode: %s\n", |
7095 | (volume_control_allowed) ? | 7186 | (volume_control_allowed) ? |
7096 | "override (read/write)" : | 7187 | "override (read/write)" : |
7097 | "monitor (read only)"); | 7188 | "monitor (read only)"); |
7189 | } | ||
7098 | 7190 | ||
7099 | vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, | 7191 | vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, |
7100 | "registering volume hotkeys as change notification\n"); | 7192 | "registering volume hotkeys as change notification\n"); |
@@ -9091,6 +9183,10 @@ MODULE_PARM_DESC(volume_control, | |||
9091 | "Enables software override for the console audio " | 9183 | "Enables software override for the console audio " |
9092 | "control when true"); | 9184 | "control when true"); |
9093 | 9185 | ||
9186 | module_param_named(software_mute, software_mute_requested, bool, 0444); | ||
9187 | MODULE_PARM_DESC(software_mute, | ||
9188 | "Request full software mute control"); | ||
9189 | |||
9094 | /* ALSA module API parameters */ | 9190 | /* ALSA module API parameters */ |
9095 | module_param_named(index, alsa_index, int, 0444); | 9191 | module_param_named(index, alsa_index, int, 0444); |
9096 | MODULE_PARM_DESC(index, "ALSA index for the ACPI EC Mixer"); | 9192 | MODULE_PARM_DESC(index, "ALSA index for the ACPI EC Mixer"); |