aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy Lutomirski <luto@MIT.EDU>2014-10-17 20:04:29 -0400
committerDarren Hart <dvhart@linux.intel.com>2014-12-04 07:03:22 -0500
commit9a417ec0c9d1f7af5394333411fc4d98adb8761b (patch)
treeb88a65fdd569cebf41a960647436328b82ef7f2b
parente4da91427fbe1dd425478d49fdabeb217945f776 (diff)
thinkpad-acpi: Try to use full software mute control
ThinkPads have hardware volume controls and three buttons to control them. (These are separate from the standard mixer.) By default, the buttons are: - Mute: Mutes the hardware volume control and, on some models, generates KEY_MUTE. - Up: Unmutes, generates KEY_VOLUMEUP, and increases volume if applicable. (Newer thinkpads only have hardware mute/unmute.) - Down: Unmutes, generates KEY_VOLUMEDOWN, and decreases volume if applicable. This behavior is unfortunate, since modern userspace will also handle the hotkeys and change the other mixer. If the software mixer is muted and the hardware mixer is unmuted and you push mute, hilarity ensues as they both switch state. Rather than adding a lot of complex ALSA integration to fix this, just disable the special ThinkPad volume controls when possible. This turns the mute and volume buttons into regular buttons, and standard software controls will work as expected. ALSA already knows about the mute light on models with a mute light, so everything should just work. This should also allow us to remove _OSI(Linux) for all ThinkPads. For future reference: It turns out that we can ask ACPI for one of three behaviors directly on very new models. They are "latch" (the default), "none" (no automatic control), and "toggle" (mute unmutes when muted). All of the modes besides "none" seem to be a bit buggy, though, and there doesn't seem to be a consistent way to get any notification when the HW mute state is changed. Signed-off-by: Andy Lutomirski <luto@mit.edu> Acked-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br> Signed-off-by: Darren Hart <dvhart@linux.intel.com>
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c116
1 files changed, 106 insertions, 10 deletions
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
6627enum 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
6616static enum tpacpi_volume_access_mode volume_mode = 6634static enum tpacpi_volume_access_mode volume_mode =
6617 TPACPI_VOL_MODE_MAX; 6635 TPACPI_VOL_MODE_MAX;
6618 6636
6619static enum tpacpi_volume_capabilities volume_capabilities; 6637static enum tpacpi_volume_capabilities volume_capabilities;
6620static bool volume_control_allowed; 6638static bool volume_control_allowed;
6639static bool software_mute_requested = true;
6640static bool software_mute_active;
6641static 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
6810static 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
6852static 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
6781static int volume_alsa_set_volume(const u8 vol) 6861static 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
6886static void volume_resume(void) 6966static 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
6891static void volume_shutdown(void) 6976static 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
6906static int __init volume_create_alsa_mixer(void) 6994static 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
9186module_param_named(software_mute, software_mute_requested, bool, 0444);
9187MODULE_PARM_DESC(software_mute,
9188 "Request full software mute control");
9189
9094/* ALSA module API parameters */ 9190/* ALSA module API parameters */
9095module_param_named(index, alsa_index, int, 0444); 9191module_param_named(index, alsa_index, int, 0444);
9096MODULE_PARM_DESC(index, "ALSA index for the ACPI EC Mixer"); 9192MODULE_PARM_DESC(index, "ALSA index for the ACPI EC Mixer");