Signed-off-by: Alex Hung <alex.h...@canonical.com>
---
 drivers/platform/x86/thinkpad_acpi.c |  115 +++++++++++++++++++++++++++++++---
 1 files changed, 106 insertions(+), 9 deletions(-)

diff --git a/drivers/platform/x86/thinkpad_acpi.c 
b/drivers/platform/x86/thinkpad_acpi.c
index 7b82868..dc22a4c 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -6486,12 +6486,86 @@ enum tpacpi_volume_capabilities {
        TPACPI_VOL_CAP_MAX
 };
 
+enum  {
+       TPACPI_AML_MUTE_GET_FUNC = 0x01,
+       TPACPI_AML_MUTE_SET_FUNC = 0x02,
+       TPACPI_AML_MUTE_SUPPORT_FUNC = 0x04,
+       TPACPI_AML_MUTE_READ_MASK = 0x01,
+       TPACPI_AML_MUTE_ERROR_STATE_MASK = 0x80000000,
+};
+
 static enum tpacpi_volume_access_mode volume_mode =
        TPACPI_VOL_MODE_MAX;
 
 static enum tpacpi_volume_capabilities volume_capabilities;
 static int volume_control_allowed;
 
+
+static bool volume_bios_support(int func)
+{
+       acpi_handle temp;
+
+       if ((func | TPACPI_AML_MUTE_GET_FUNC) &&
+           !ACPI_SUCCESS(acpi_get_handle(hkey_handle, "GSMS", &temp)))
+               return false;
+
+       if ((func | TPACPI_AML_MUTE_SET_FUNC) &&
+           !ACPI_SUCCESS(acpi_get_handle(hkey_handle, "SSMS", &temp)))
+               return false;
+
+       if ((func | TPACPI_AML_MUTE_SUPPORT_FUNC) &&
+           !ACPI_SUCCESS(acpi_get_handle(hkey_handle, "SHDA", &temp)))
+               return false;
+
+       return true;
+}
+
+static int hotkey_get_mute_state(int *state)
+{
+       if (!acpi_evalf(hkey_handle, state, "GSMS", "dd"))
+               return -EIO;
+
+       if (*state & TPACPI_AML_MUTE_ERROR_STATE_MASK)
+               pr_warning("getting mute state failed.\n");
+
+       *state &= TPACPI_AML_MUTE_READ_MASK;
+       pr_info("get mute state = %s.\n", *state ? "muted" : "unmuted");
+
+       return 0;
+}
+
+static int hotkey_set_mute_state(int state)
+{
+       int output;
+
+       if (!acpi_evalf(hkey_handle, &output, "SSMS", "dd", state))
+               return -EIO;
+
+       if (output & TPACPI_AML_MUTE_ERROR_STATE_MASK) {
+               pr_warning("setting mute state failed.\n");
+               return -EIO;
+       }
+       pr_info("set to mute led state to %s.\n", state ? "on" : "off");
+
+       return 0;
+}
+
+static int hotkey_set_mute_support(int support)
+{
+       int output;
+
+       if (!acpi_evalf(hkey_handle, &output, "SHDA", "dd", support))
+               return -EIO;
+
+       if (output & TPACPI_AML_MUTE_ERROR_STATE_MASK) {
+               pr_warning("setting mute support failed.\n");
+               return -EIO;
+       }
+       pr_info("%s mute led support.\n", support ? "disable" : "enable");
+
+       return 0;
+}
+
 /*
  * Used to syncronize writers to TP_EC_AUDIO and
  * TP_NVRAM_ADDR_MIXER, as we need to do read-modify-write
@@ -6982,6 +7056,7 @@ static int __init volume_init(struct ibm_init_struct 
*iibm)
 static int volume_read(struct seq_file *m)
 {
        u8 status;
+       int mute;
 
        if (volume_get_status(&status) < 0) {
                seq_printf(m, "level:\t\tunreadable\n");
@@ -6991,8 +7066,12 @@ static int volume_read(struct seq_file *m)
                else
                        seq_printf(m, "level:\t\t%d\n",
                                        status & TP_EC_AUDIO_LVL_MSK);
-
-               seq_printf(m, "mute:\t\t%s\n",
+               if (volume_bios_support(TPACPI_AML_MUTE_GET_FUNC) &&
+                   !hotkey_get_mute_state(&mute))
+                       seq_printf(m, "mute:\t\t%s\n",
+                               mute ? "muted" : "unmuted");
+               else
+                       seq_printf(m, "mute:\t\t%s\n",
                                onoff(status, TP_EC_AUDIO_MUTESW));
 
                if (volume_control_allowed) {
@@ -7005,7 +7084,8 @@ static int volume_read(struct seq_file *m)
                                               " (<level> is 0-%d)\n",
                                               TP_EC_VOLUME_MAX);
                        }
-               }
+               } else if (volume_bios_support(TPACPI_AML_MUTE_SET_FUNC))
+                       seq_printf(m, "commands:\tunmute, mute\n");
        }
 
        return 0;
@@ -7019,6 +7099,21 @@ static int volume_write(char *buf)
        char *cmd;
        int rc;
 
+       if (volume_bios_support(
+               TPACPI_AML_MUTE_SET_FUNC | TPACPI_AML_MUTE_SUPPORT_FUNC)) {
+               while ((cmd = next_cmd(&buf))) {
+                       if (strlencmp(cmd, "mute") == 0)
+                               hotkey_set_mute_state(1);
+                       else if (strlencmp(cmd, "unmute") == 0)
+                               hotkey_set_mute_state(0);
+                       else if (strlencmp(cmd, "disable") == 0)
+                               hotkey_set_mute_support(1);
+                       else if (strlencmp(cmd, "enable") == 0)
+                               hotkey_set_mute_support(0);
+               }
+               return -EINVAL;
+       }
+
        /*
         * We do allow volume control at driver startup, so that the
         * user can set initial state through the volume=... parameter hack.
@@ -7061,12 +7156,14 @@ static int volume_write(char *buf)
                                continue;
                        }
                }
-               if (strlencmp(cmd, "mute") == 0)
-                       new_mute = TP_EC_AUDIO_MUTESW_MSK;
-               else if (strlencmp(cmd, "unmute") == 0)
-                       new_mute = 0;
-               else
-                       return -EINVAL;
+               if (!volume_bios_support(TPACPI_AML_MUTE_SET_FUNC)) {
+                       if (strlencmp(cmd, "mute") == 0)
+                               new_mute = TP_EC_AUDIO_MUTESW_MSK;
+                       else if (strlencmp(cmd, "unmute") == 0)
+                               new_mute = 0;
+                       else
+                               return -EINVAL;
+               }
        }
 
        if (tp_features.mixer_no_level_control) {
-- 
1.7.5.4

--
To unsubscribe from this list: send the line "unsubscribe platform-driver-x86" 
in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to