[alsa-devel] [PATCH v2 0/3] Enable mute LEDs and mic mute LEDs on Thinkpad
Encouraged by the quick and helpful feedback from both Takashi and Henrique (thanks!), I went to work with v2 of the patch.
Changes since v1:
- thinkpad-acpi patch: rewritten to follow Takashi's ideas - thinkpad-acpi patch: added documentation update - alsa side: added a separate patch for the new fixup action - alsa side: rewrote patch a little, mostly based on comments from Takashi
David Henningsson (3): thinkpad-acpi: Add mute and mic-mute LED functionality ALSA: hda - add HDA_FIXUP_ACT_FREE action ALSA: hda - add connection to thinkpad_acpi to control mute/micmute LEDs
Documentation/laptops/thinkpad-acpi.txt | 7 ++- drivers/platform/x86/thinkpad_acpi.c | 92 ++++++++++++++++++++++++++++++- include/linux/thinkpad_acpi.h | 15 +++++ sound/pci/hda/hda_local.h | 1 + sound/pci/hda/patch_conexant.c | 92 ++++++++++++++++++++++++++++++- 5 files changed, 202 insertions(+), 5 deletions(-) create mode 100644 include/linux/thinkpad_acpi.h
The LEDs are currently not visible to userspace, for security reasons. They are exported through thinkpad_acpi.h for use by the snd-hda-intel driver.
Thanks to Alex Hung alex.hung@canonical.com and Takashi Iwai tiwai@suse.de for writing parts of this patch.
Signed-off-by: David Henningsson david.henningsson@canonical.com --- Documentation/laptops/thinkpad-acpi.txt | 7 ++- drivers/platform/x86/thinkpad_acpi.c | 92 ++++++++++++++++++++++++++++++- include/linux/thinkpad_acpi.h | 15 +++++ 3 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 include/linux/thinkpad_acpi.h
diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index 86c5236..fc04c14 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt @@ -1,7 +1,7 @@ ThinkPad ACPI Extras Driver
- Version 0.24 - December 11th, 2009 + Version 0.25 + October 16th, 2013
Borislav Deianov borislav@users.sf.net Henrique de Moraes Holschuh hmh@hmh.eng.br @@ -741,6 +741,9 @@ compiled with the CONFIG_THINKPAD_ACPI_UNSAFE_LEDS option enabled. Distributions must never enable this option. Individual users that are aware of the consequences are welcome to enabling it.
+Audio mute and microphone mute LEDs are supported, but currently not +visible to userspace. They are used by the snd-hda-intel audio driver. + procfs notes:
The available commands are: diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 03ca6c1..0b7efb2 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -23,7 +23,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#define TPACPI_VERSION "0.24" +#define TPACPI_VERSION "0.25" #define TPACPI_SYSFS_VERSION 0x020700
/* @@ -88,6 +88,7 @@
#include <linux/pci_ids.h>
+#include <linux/thinkpad_acpi.h>
/* ThinkPad CMOS commands */ #define TP_CMOS_VOLUME_DOWN 0 @@ -8350,6 +8351,91 @@ static struct ibm_struct fan_driver_data = { .resume = fan_resume, };
+/************************************************************************* + * Mute LED subdriver + */ + + +struct tp_led_table { + acpi_string name; + int on_value; + int off_value; + int state; +}; + +static struct tp_led_table led_tables[] = { + [TPACPI_LED_MUTE] = { + .name = "SSMS", + .on_value = 1, + .off_value = 0, + }, + [TPACPI_LED_MICMUTE] = { + .name = "MMTS", + .on_value = 2, + .off_value = 0, + }, +}; + +static int mute_led_on_off(struct tp_led_table *t, bool state) +{ + acpi_handle temp; + int output; + + if (!ACPI_SUCCESS(acpi_get_handle(hkey_handle, t->name, &temp))) { + pr_warn("Thinkpad ACPI has no %s interface.\n", t->name); + return -EIO; + } + + if (!acpi_evalf(hkey_handle, &output, t->name, "dd", + state ? t->on_value : t->off_value)) + return -EIO; + + t->state = state; + return state; +} + +int tpacpi_led_set(int whichled, bool on) +{ + struct tp_led_table *t; + + if (whichled < 0 || whichled >= TPACPI_LED_MAX) + return -EINVAL; + + t = &led_tables[whichled]; + if (t->state < 0 || t->state == on) + return t->state; + return mute_led_on_off(t, on); +} +EXPORT_SYMBOL_GPL(tpacpi_led_set); + +static int mute_led_init(struct ibm_init_struct *iibm) +{ + acpi_handle temp; + int i; + + for (i = 0; i < TPACPI_LED_MAX; i++) { + struct tp_led_table *t = &led_tables[i]; + if (ACPI_SUCCESS(acpi_get_handle(hkey_handle, t->name, &temp))) + mute_led_on_off(t, false); + else + t->state = -ENODEV; + } + return 0; +} + +static void mute_led_exit(void) +{ + int i; + + for (i = 0; i < TPACPI_LED_MAX; i++) + tpacpi_led_set(i, false); +} + +static struct ibm_struct mute_led_driver_data = { + .name = "mute_led", + .exit = mute_led_exit, +}; + /**************************************************************************** **************************************************************************** * @@ -8768,6 +8854,10 @@ static struct ibm_init_struct ibms_init[] __initdata = { .init = fan_init, .data = &fan_driver_data, }, + { + .init = mute_led_init, + .data = &mute_led_driver_data, + }, };
static int __init set_ibm_param(const char *val, struct kernel_param *kp) diff --git a/include/linux/thinkpad_acpi.h b/include/linux/thinkpad_acpi.h new file mode 100644 index 0000000..361de59 --- /dev/null +++ b/include/linux/thinkpad_acpi.h @@ -0,0 +1,15 @@ +#ifndef __THINKPAD_ACPI_H__ +#define __THINKPAD_ACPI_H__ + +/* These two functions return 0 if success, or negative error code + (e g -ENODEV if no led present) */ + +enum { + TPACPI_LED_MUTE, + TPACPI_LED_MICMUTE, + TPACPI_LED_MAX, +}; + +int tpacpi_led_set(int whichled, bool on); + +#endif
On Wed, 16 Oct 2013, David Henningsson wrote:
The LEDs are currently not visible to userspace, for security reasons. They are exported through thinkpad_acpi.h for use by the snd-hda-intel driver.
Thanks to Alex Hung alex.hung@canonical.com and Takashi Iwai tiwai@suse.de for writing parts of this patch.
Signed-off-by: David Henningsson david.henningsson@canonical.com
Acked-by: Henrique de Moraes Holschuh hmh@hmh.eng.br
A fixup which should be called before codec being freed will come to use in the next patch.
Signed-off-by: David Henningsson david.henningsson@canonical.com --- sound/pci/hda/hda_local.h | 1 + 1 file changed, 1 insertion(+)
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 2e7493e..a71bf34 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -428,6 +428,7 @@ enum { HDA_FIXUP_ACT_PROBE, HDA_FIXUP_ACT_INIT, HDA_FIXUP_ACT_BUILD, + HDA_FIXUP_ACT_FREE, };
int snd_hda_add_verbs(struct hda_codec *codec, const struct hda_verb *list);
Signed-off-by: David Henningsson david.henningsson@canonical.com --- sound/pci/hda/patch_conexant.c | 92 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 2 deletions(-)
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index ec68eae..993b25c 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -3208,11 +3208,17 @@ static int cx_auto_init(struct hda_codec *codec) return 0; }
+static void cx_auto_free(struct hda_codec *codec) +{ + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_FREE); + snd_hda_gen_free(codec); +} + static const struct hda_codec_ops cx_auto_patch_ops = { .build_controls = cx_auto_build_controls, .build_pcms = snd_hda_gen_build_pcms, .init = cx_auto_init, - .free = snd_hda_gen_free, + .free = cx_auto_free, .unsol_event = snd_hda_jack_unsol_event, #ifdef CONFIG_PM .check_power_status = snd_hda_gen_check_power_status, @@ -3232,8 +3238,84 @@ enum { CXT_FIXUP_HEADPHONE_MIC_PIN, CXT_FIXUP_HEADPHONE_MIC, CXT_FIXUP_GPIO1, + CXT_FIXUP_THINKPAD_ACPI, };
+#if IS_ENABLED(CONFIG_THINKPAD_ACPI) + +#include <linux/thinkpad_acpi.h> + +static int (*led_set_func)(int, bool); + +static void update_tpacpi_mute_led(void *private_data, int enabled) +{ + struct hda_codec *codec = private_data; + struct conexant_spec *spec = codec->spec; + + if (spec->dynamic_eapd) + cx_auto_vmaster_hook(private_data, enabled); + + if (led_set_func) + led_set_func(TPACPI_LED_MUTE, !enabled); +} + +static void update_tpacpi_micmute_led(struct hda_codec *codec, + struct snd_ctl_elem_value *ucontrol) +{ + if (!ucontrol || !led_set_func) + return; + if (strcmp("Capture Switch", ucontrol->id.name) == 0 && ucontrol->id.index == 0) { + /* TODO: How do I verify if it's a mono or stereo here? */ + bool val = ucontrol->value.integer.value[0] || ucontrol->value.integer.value[1]; + led_set_func(TPACPI_LED_MICMUTE, !val); + } +} + +static void cxt_fixup_thinkpad_acpi(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct conexant_spec *spec = codec->spec; + + bool removefunc = false; + + if (action == HDA_FIXUP_ACT_PROBE) { + if (!led_set_func) + led_set_func = symbol_request(tpacpi_led_set); + if (!led_set_func) { + snd_printk(KERN_WARNING "Failed to find thinkpad-acpi symbol tpacpi_led_set\n"); + return; + } + + removefunc = true; + if (led_set_func(TPACPI_LED_MUTE, false) >= 0) { + spec->gen.vmaster_mute.hook = update_tpacpi_mute_led; + removefunc = false; + } + if (led_set_func(TPACPI_LED_MICMUTE, false) >= 0) { + if (spec->gen.num_adc_nids > 1) + snd_printdd("Skipping micmute LED control due to several ADCs"); + else { + spec->gen.cap_sync_hook = update_tpacpi_micmute_led; + removefunc = false; + } + } + } + + if (led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) { + symbol_put(tpacpi_led_set); + led_set_func = NULL; + } +} + +#else + +static void cxt_fixup_thinkpad_acpi(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ +} + +#endif + static void cxt_fixup_stereo_dmic(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -3344,6 +3426,8 @@ static const struct hda_fixup cxt_fixups[] = { [CXT_PINCFG_LENOVO_TP410] = { .type = HDA_FIXUP_PINS, .v.pins = cxt_pincfg_lenovo_tp410, + .chained = true, + .chain_id = CXT_FIXUP_THINKPAD_ACPI, }, [CXT_PINCFG_LEMOTE_A1004] = { .type = HDA_FIXUP_PINS, @@ -3385,6 +3469,10 @@ static const struct hda_fixup cxt_fixups[] = { { } }, }, + [CXT_FIXUP_THINKPAD_ACPI] = { + .type = HDA_FIXUP_FUNC, + .v.func = cxt_fixup_thinkpad_acpi, + }, };
static const struct snd_pci_quirk cxt5051_fixups[] = { @@ -3507,7 +3595,7 @@ static int patch_conexant_auto(struct hda_codec *codec) return 0;
error: - snd_hda_gen_free(codec); + cx_auto_free(codec); return err; }
At Wed, 16 Oct 2013 23:10:30 +0200, David Henningsson wrote:
Encouraged by the quick and helpful feedback from both Takashi and Henrique (thanks!), I went to work with v2 of the patch.
Changes since v1:
- thinkpad-acpi patch: rewritten to follow Takashi's ideas
- thinkpad-acpi patch: added documentation update
- alsa side: added a separate patch for the new fixup action
- alsa side: rewrote patch a little, mostly based on comments from Takashi
David Henningsson (3): thinkpad-acpi: Add mute and mic-mute LED functionality ALSA: hda - add HDA_FIXUP_ACT_FREE action ALSA: hda - add connection to thinkpad_acpi to control mute/micmute LEDs
The new patches look OK to me. If Henrique agrees, I can apply all to sound git tree.
thanks,
Takashi
Documentation/laptops/thinkpad-acpi.txt | 7 ++- drivers/platform/x86/thinkpad_acpi.c | 92 ++++++++++++++++++++++++++++++- include/linux/thinkpad_acpi.h | 15 +++++ sound/pci/hda/hda_local.h | 1 + sound/pci/hda/patch_conexant.c | 92 ++++++++++++++++++++++++++++++- 5 files changed, 202 insertions(+), 5 deletions(-) create mode 100644 include/linux/thinkpad_acpi.h
-- 1.7.9.5
On Thu, 17 Oct 2013, Takashi Iwai wrote:
The new patches look OK to me. If Henrique agrees, I can apply all to sound git tree.
Sure, go ahead.
At Thu, 17 Oct 2013 08:34:36 -0300, Henrique de Moraes Holschuh wrote:
On Thu, 17 Oct 2013, Takashi Iwai wrote:
The new patches look OK to me. If Henrique agrees, I can apply all to sound git tree.
Sure, go ahead.
OK, I applied all patches to for-next branch now.
thanks,
Takashi
On 10/17/2013 03:15 PM, Takashi Iwai wrote:
At Thu, 17 Oct 2013 08:34:36 -0300, Henrique de Moraes Holschuh wrote:
On Thu, 17 Oct 2013, Takashi Iwai wrote:
The new patches look OK to me. If Henrique agrees, I can apply all to sound git tree.
Sure, go ahead.
OK, I applied all patches to for-next branch now.
Ok, thanks a lot!
participants (3)
-
David Henningsson
-
Henrique de Moraes Holschuh
-
Takashi Iwai