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"); |
