From bf61c42a446f58eb61b7af05e9ff0b79aba348b6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 18 Jun 2020 13:08:30 +0200 Subject: [PATCH 01/13] ALSA: hda: generic: Always call led-trigger for mic mute LED Instead of adding a special hook to trigger the mic-mute LED device, call it always from the common callback function. It won't hurt even if no corresponding led cdev is present. This is basically a preliminary change for the later patches to convert the all mic-mute LED handling to LED class cdev. Tested-by: Kai-Heng Feng Link: https://lore.kernel.org/r/20200618110842.27238-2-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f4e9d9445e18..c95ccffbd0cf 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3923,6 +3923,10 @@ static void call_micmute_led_update(struct hda_codec *codec) spec->micmute_led.led_value = val; if (spec->micmute_led.update) spec->micmute_led.update(codec); +#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO) + ledtrig_audio_set(LED_AUDIO_MICMUTE, + spec->micmute_led.led_value ? LED_ON : LED_OFF); +#endif } static void update_micmute_led(struct hda_codec *codec, @@ -4023,16 +4027,6 @@ int snd_hda_gen_add_micmute_led(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_gen_add_micmute_led); -#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO) -static void call_ledtrig_micmute(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - - ledtrig_audio_set(LED_AUDIO_MICMUTE, - spec->micmute_led.led_value ? LED_ON : LED_OFF); -} -#endif - /** * snd_hda_gen_fixup_micmute_led - A fixup for mic-mute LED trigger * @@ -4051,10 +4045,8 @@ static void call_ledtrig_micmute(struct hda_codec *codec) void snd_hda_gen_fixup_micmute_led(struct hda_codec *codec, const struct hda_fixup *fix, int action) { -#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO) if (action == HDA_FIXUP_ACT_PROBE) - snd_hda_gen_add_micmute_led(codec, call_ledtrig_micmute); -#endif + snd_hda_gen_add_micmute_led(codec, NULL); } EXPORT_SYMBOL_GPL(snd_hda_gen_fixup_micmute_led); From 7cdf8c49b1df0a385db06c4f9a5ba1b16510fdcc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 18 Jun 2020 13:08:31 +0200 Subject: [PATCH 02/13] ALSA: hda: generic: Add a helper for mic-mute LED with LED classdev A new helper, snd_hda_gen_add_micmute_led_cdev(), is introduced here for creating a LED classdev and setting up the hook to the capture control for controlling the mic-mute LED to follow the capture switch change. This will replace the existing users of snd_hda_gen_add_micmute_led() in later patches. Also, introduce a new kconfig CONFIG_SND_HDA_GENERIC_LEDS, to indicate the usage of mute / mic-mute LED helpers. It's selected by the codec drivers (Realtek, Conexant and Sigmatel), while it selects the necessary LED class dependencies. Tested-by: Kai-Heng Feng Link: https://lore.kernel.org/r/20200618110842.27238-3-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/Kconfig | 9 +++++++ sound/pci/hda/hda_generic.c | 47 +++++++++++++++++++++++++++++++++-- sound/pci/hda/hda_generic.h | 5 ++++ sound/pci/hda/patch_realtek.c | 30 +--------------------- 4 files changed, 60 insertions(+), 31 deletions(-) diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 7ba542e45a3d..3f9abdaef7f5 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -8,6 +8,9 @@ config SND_HDA select SND_JACK select SND_HDA_CORE +config SND_HDA_GENERIC_LEDS + bool + config SND_HDA_INTEL tristate "HD Audio PCI" depends on SND_PCI @@ -91,6 +94,7 @@ config SND_HDA_PATCH_LOADER config SND_HDA_CODEC_REALTEK tristate "Build Realtek HD-audio codec support" select SND_HDA_GENERIC + select SND_HDA_GENERIC_LEDS help Say Y or M here to include Realtek HD-audio codec support in snd-hda-intel driver, such as ALC880. @@ -111,6 +115,7 @@ comment "Set to Y if you want auto-loading the codec driver" config SND_HDA_CODEC_SIGMATEL tristate "Build IDT/Sigmatel HD-audio codec support" select SND_HDA_GENERIC + select SND_HDA_GENERIC_LEDS help Say Y or M here to include IDT (Sigmatel) HD-audio codec support in snd-hda-intel driver, such as STAC9200. @@ -155,6 +160,7 @@ comment "Set to Y if you want auto-loading the codec driver" config SND_HDA_CODEC_CONEXANT tristate "Build Conexant HD-audio codec support" select SND_HDA_GENERIC + select SND_HDA_GENERIC_LEDS help Say Y or M here to include Conexant HD-audio codec support in snd-hda-intel driver, such as CX20549. @@ -215,6 +221,9 @@ comment "Set to Y if you want auto-loading the codec driver" config SND_HDA_GENERIC tristate "Enable generic HD-audio codec parser" + select LEDS_CLASS if SND_HDA_GENERIC_LEDS + select LEDS_TRIGGERS if SND_HDA_GENERIC_LEDS + select LEDS_TRIGGER_AUDIO if SND_HDA_GENERIC_LEDS help Say Y or M here to enable the generic HD-audio codec parser in snd-hda-intel driver. diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index c95ccffbd0cf..419cfbd0dc5c 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3887,6 +3887,7 @@ static int parse_mic_boost(struct hda_codec *codec) return 0; } +#ifdef CONFIG_SND_HDA_GENERIC_LEDS /* * mic mute LED hook helpers */ @@ -3923,10 +3924,8 @@ static void call_micmute_led_update(struct hda_codec *codec) spec->micmute_led.led_value = val; if (spec->micmute_led.update) spec->micmute_led.update(codec); -#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO) ledtrig_audio_set(LED_AUDIO_MICMUTE, spec->micmute_led.led_value ? LED_ON : LED_OFF); -#endif } static void update_micmute_led(struct hda_codec *codec, @@ -4050,6 +4049,50 @@ void snd_hda_gen_fixup_micmute_led(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_gen_fixup_micmute_led); +/** + * snd_dha_gen_add_micmute_led_cdev - Create a LED classdev and enable as mic-mute LED + * @codec: the HDA codec + * @callback: the callback for LED classdev brightness_set_blocking + * + * Called from the codec drivers for offering the mic mute LED controls. + * This creates a LED classdev and sets up the cap_sync_hook that is called at + * each time when the capture mixer switch changes. + * + * When NULL is passed to @callback, no classdev is created but only the + * LED-trigger is set up. + * + * Returns 0 or a negative error. + */ +int snd_hda_gen_add_micmute_led_cdev(struct hda_codec *codec, + int (*callback)(struct led_classdev *, + enum led_brightness)) +{ + int err; + struct led_classdev *cdev; + + if (callback) { + cdev = devm_kzalloc(&codec->core.dev, sizeof(*cdev), GFP_KERNEL); + if (!cdev) + return -ENOMEM; + + cdev->name = "hda::micmute"; + cdev->max_brightness = 1; + cdev->default_trigger = "audio-micmute"; + cdev->brightness_set_blocking = callback; + cdev->brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE); + + err = devm_led_classdev_register(&codec->core.dev, cdev); + if (err) { + codec_warn(codec, "failed to create a mic-mute LED cdev\n"); + return err; + } + } + + return snd_hda_gen_add_micmute_led(codec, NULL); +} +EXPORT_SYMBOL_GPL(snd_hda_gen_add_micmute_led_cdev); +#endif /* CONFIG_SND_HDA_GENERIC_LEDS */ + /* * parse digital I/Os and set up NIDs in BIOS auto-parse mode */ diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index fb9f1a90238b..f56bc8da20b2 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -8,6 +8,8 @@ #ifndef __SOUND_HDA_GENERIC_H #define __SOUND_HDA_GENERIC_H +#include + /* table entry for multi-io paths */ struct hda_multi_io { hda_nid_t pin; /* multi-io widget pin NID */ @@ -357,5 +359,8 @@ int snd_hda_gen_add_micmute_led(struct hda_codec *codec, void (*hook)(struct hda_codec *)); void snd_hda_gen_fixup_micmute_led(struct hda_codec *codec, const struct hda_fixup *fix, int action); +int snd_hda_gen_add_micmute_led_cdev(struct hda_codec *codec, + int (*callback)(struct led_classdev *, + enum led_brightness)); #endif /* __SOUND_HDA_GENERIC_H */ diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 6d73f8beadb6..1d0fe9a0983f 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4100,16 +4100,6 @@ static void alc_fixup_gpio_mute_hook(void *private_data, int enabled) } /* turn on/off mic-mute LED via GPIO per capture hook */ -static void alc_gpio_micmute_update(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - alc_update_gpio_led(codec, spec->gpio_mic_led_mask, - spec->micmute_led_polarity, - spec->gen.micmute_led.led_value); -} - -#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO) static int micmute_led_set(struct led_classdev *led_cdev, enum led_brightness brightness) { @@ -4121,14 +4111,6 @@ static int micmute_led_set(struct led_classdev *led_cdev, return 0; } -static struct led_classdev micmute_led_cdev = { - .name = "hda::micmute", - .max_brightness = 1, - .brightness_set_blocking = micmute_led_set, - .default_trigger = "audio-micmute", -}; -#endif - /* setup mute and mic-mute GPIO bits, add hooks appropriately */ static void alc_fixup_hp_gpio_led(struct hda_codec *codec, int action, @@ -4136,9 +4118,6 @@ static void alc_fixup_hp_gpio_led(struct hda_codec *codec, unsigned int micmute_mask) { struct alc_spec *spec = codec->spec; -#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO) - int err; -#endif alc_fixup_gpio(codec, action, mute_mask | micmute_mask); @@ -4150,14 +4129,7 @@ static void alc_fixup_hp_gpio_led(struct hda_codec *codec, } if (micmute_mask) { spec->gpio_mic_led_mask = micmute_mask; - snd_hda_gen_add_micmute_led(codec, alc_gpio_micmute_update); - -#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO) - micmute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE); - err = devm_led_classdev_register(&codec->core.dev, &micmute_led_cdev); - if (err) - codec_warn(codec, "failed to register micmute LED\n"); -#endif + snd_hda_gen_add_micmute_led_cdev(codec, micmute_led_set); } } From 8a503555be4647bfa5312db4dd622a357492beb7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 18 Jun 2020 13:08:32 +0200 Subject: [PATCH 03/13] ALSA: hda/realtek: Convert to cdev-variant of mic-mute LED controls This patch converts the remaining user of snd_hda_gen_add_micmute_led() in Realtek codec driver into the new snd_hda_gen_add_micmute_led_cdev(). The Thinkpad helper code is updated accordingly, too. Also, the usage of snd_hda_gen_fixup_micmute_led() is replaced with either the local alc_fixup_micmute_led() or the explicit call of snd_hda_gen_add_micmute_led_cdev() with NULL callback. Tested-by: Kai-Heng Feng Link: https://lore.kernel.org/r/20200618110842.27238-4-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 39 ++++++++++++++++++++++----------- sound/pci/hda/thinkpad_helper.c | 2 +- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 1d0fe9a0983f..6831587fc4d6 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -287,6 +287,13 @@ static void alc_fixup_gpio4(struct hda_codec *codec, alc_fixup_gpio(codec, action, 0x04); } +static void alc_fixup_micmute_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PROBE) + snd_hda_gen_add_micmute_led_cdev(codec, NULL); +} + /* * Fix hardware PLL issue * On some codecs, the analog PLL gating control must be off while @@ -4155,21 +4162,24 @@ static void alc286_fixup_hp_gpio_led(struct hda_codec *codec, alc_fixup_hp_gpio_led(codec, action, 0x02, 0x20); } -/* turn on/off mic-mute LED per capture hook */ -static void alc_cap_micmute_update(struct hda_codec *codec) +/* turn on/off mic-mute LED per capture hook via VREF change */ +static int vref_micmute_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) { + struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); struct alc_spec *spec = codec->spec; unsigned int pinval; if (!spec->cap_mute_led_nid) - return; + return 0; pinval = snd_hda_codec_get_pin_target(codec, spec->cap_mute_led_nid); pinval &= ~AC_PINCTL_VREFEN; - if (spec->gen.micmute_led.led_value) + if (brightness) pinval |= AC_PINCTL_VREF_80; else pinval |= AC_PINCTL_VREF_HIZ; snd_hda_set_pin_ctl_cache(codec, spec->cap_mute_led_nid, pinval); + return 0; } static void alc269_fixup_hp_gpio_mic1_led(struct hda_codec *codec, @@ -4185,7 +4195,7 @@ static void alc269_fixup_hp_gpio_mic1_led(struct hda_codec *codec, spec->gpio_mask |= 0x10; spec->gpio_dir |= 0x10; spec->cap_mute_led_nid = 0x18; - snd_hda_gen_add_micmute_led(codec, alc_cap_micmute_update); + snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set); codec->power_filter = led_power_filter; } } @@ -4198,7 +4208,7 @@ static void alc280_fixup_hp_gpio4(struct hda_codec *codec, alc_fixup_hp_gpio_led(codec, action, 0x08, 0); if (action == HDA_FIXUP_ACT_PRE_PROBE) { spec->cap_mute_led_nid = 0x18; - snd_hda_gen_add_micmute_led(codec, alc_cap_micmute_update); + snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set); codec->power_filter = led_power_filter; } } @@ -4254,16 +4264,19 @@ static void alc236_fixup_hp_mute_led_coefbit(struct hda_codec *codec, } /* turn on/off mic-mute LED per capture hook by coef bit */ -static void alc_hp_cap_micmute_update(struct hda_codec *codec) +static int coef_micmute_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) { + struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); struct alc_spec *spec = codec->spec; - if (spec->gen.micmute_led.led_value) + if (brightness) alc_update_coef_idx(codec, spec->mic_led_coef_idx, spec->mic_led_coefbit_mask, spec->mic_led_coefbit_on); else alc_update_coef_idx(codec, spec->mic_led_coef_idx, spec->mic_led_coefbit_mask, spec->mic_led_coefbit_off); + return 0; } static void alc285_fixup_hp_coef_micmute_led(struct hda_codec *codec, @@ -4276,7 +4289,7 @@ static void alc285_fixup_hp_coef_micmute_led(struct hda_codec *codec, spec->mic_led_coefbit_mask = 1<<13; spec->mic_led_coefbit_on = 1<<13; spec->mic_led_coefbit_off = 0; - snd_hda_gen_add_micmute_led(codec, alc_hp_cap_micmute_update); + snd_hda_gen_add_micmute_led_cdev(codec, coef_micmute_led_set); } } @@ -4290,7 +4303,7 @@ static void alc236_fixup_hp_coef_micmute_led(struct hda_codec *codec, spec->mic_led_coefbit_mask = 3<<2; spec->mic_led_coefbit_on = 2<<2; spec->mic_led_coefbit_off = 1<<2; - snd_hda_gen_add_micmute_led(codec, alc_hp_cap_micmute_update); + snd_hda_gen_add_micmute_led_cdev(codec, coef_micmute_led_set); } } @@ -4430,7 +4443,7 @@ static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec, alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x1a); if (action == HDA_FIXUP_ACT_PRE_PROBE) { spec->cap_mute_led_nid = 0x18; - snd_hda_gen_add_micmute_led(codec, alc_cap_micmute_update); + snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set); } } @@ -6660,7 +6673,7 @@ static const struct hda_fixup alc269_fixups[] = { }, [ALC255_FIXUP_MIC_MUTE_LED] = { .type = HDA_FIXUP_FUNC, - .v.func = snd_hda_gen_fixup_micmute_led, + .v.func = alc_fixup_micmute_led, }, [ALC282_FIXUP_ASPIRE_V5_PINS] = { .type = HDA_FIXUP_PINS, @@ -6763,7 +6776,7 @@ static const struct hda_fixup alc269_fixups[] = { }, [ALC292_FIXUP_DELL_E7X] = { .type = HDA_FIXUP_FUNC, - .v.func = snd_hda_gen_fixup_micmute_led, + .v.func = alc_fixup_micmute_led, /* micmute fixup must be applied at last */ .chained_before = true, .chain_id = ALC292_FIXUP_DELL_E7X_AAMIX, diff --git a/sound/pci/hda/thinkpad_helper.c b/sound/pci/hda/thinkpad_helper.c index 4089feb8c68e..cd14d5a55b42 100644 --- a/sound/pci/hda/thinkpad_helper.c +++ b/sound/pci/hda/thinkpad_helper.c @@ -35,7 +35,7 @@ static void hda_fixup_thinkpad_acpi(struct hda_codec *codec, return; old_vmaster_hook = spec->vmaster_mute.hook; spec->vmaster_mute.hook = update_tpacpi_mute_led; - snd_hda_gen_fixup_micmute_led(codec, fix, action); + snd_hda_gen_add_micmute_led_cdev(codec, NULL); } } From e65a2caf3b05d616adf3bdd83e3581bbdced5d82 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 18 Jun 2020 13:08:33 +0200 Subject: [PATCH 04/13] ALSA: hda/conexant: Convert to cdev-variant of mic-mute LED controls This patch converts the remaining user of snd_hda_gen_add_micmute_led() in Conexant codec driver into the new snd_hda_gen_add_micmute_led_cdev(). Tested-by: Kai-Heng Feng Link: https://lore.kernel.org/r/20200618110842.27238-5-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 396b5503038a..5a7c16e39d7a 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -640,12 +640,14 @@ static void cxt_fixup_gpio_mute_hook(void *private_data, int enabled) } /* turn on/off mic-mute LED via GPIO per capture hook */ -static void cxt_gpio_micmute_update(struct hda_codec *codec) +static int cxt_gpio_micmute_update(struct led_classdev *led_cdev, + enum led_brightness brightness) { + struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); struct conexant_spec *spec = codec->spec; - cxt_update_gpio_led(codec, spec->gpio_mic_led_mask, - spec->gen.micmute_led.led_value); + cxt_update_gpio_led(codec, spec->gpio_mic_led_mask, brightness); + return 0; } @@ -665,7 +667,7 @@ static void cxt_fixup_mute_led_gpio(struct hda_codec *codec, spec->mute_led_polarity = 0; spec->gpio_mute_led_mask = 0x01; spec->gpio_mic_led_mask = 0x02; - snd_hda_gen_add_micmute_led(codec, cxt_gpio_micmute_update); + snd_hda_gen_add_micmute_led_cdev(codec, cxt_gpio_micmute_update); } snd_hda_add_verbs(codec, gpio_init); if (spec->gpio_led) From 23a2b46966bde1e54a7b67e4968554905d71cad5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 18 Jun 2020 13:08:34 +0200 Subject: [PATCH 05/13] ALSA: hda/sigmatel: Convert to cdev-variant of mic-mute LED controls This patch converts the remaining user of snd_hda_gen_add_micmute_led() in IDT/Sigmatel codec driver into the new snd_hda_gen_add_micmute_led_cdev(). Tested-by: Kai-Heng Feng Link: https://lore.kernel.org/r/20200618110842.27238-6-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index a608d0486ae4..3b796d0d7a67 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -320,15 +320,18 @@ static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, } /* hook for controlling mic-mute LED GPIO */ -static void stac_capture_led_update(struct hda_codec *codec) +static int stac_capture_led_update(struct led_classdev *led_cdev, + enum led_brightness brightness) { + struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); struct sigmatel_spec *spec = codec->spec; - if (spec->gen.micmute_led.led_value) + if (brightness) spec->gpio_data |= spec->mic_mute_led_gpio; else spec->gpio_data &= ~spec->mic_mute_led_gpio; stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data); + return 0; } static int stac_vrefout_set(struct hda_codec *codec, @@ -4636,7 +4639,7 @@ static void stac_setup_gpio(struct hda_codec *codec) spec->gpio_dir |= spec->mic_mute_led_gpio; spec->mic_enabled = 0; spec->gpio_data |= spec->mic_mute_led_gpio; - snd_hda_gen_add_micmute_led(codec, stac_capture_led_update); + snd_hda_gen_add_micmute_led_cdev(codec, stac_capture_led_update); } } From 5fc0f6930035c5ef30f1a869e6450854618b29d7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 18 Jun 2020 13:08:35 +0200 Subject: [PATCH 06/13] ALSA: hda: generic: Drop unused snd_hda_gen_fixup_micmute_led() The fixup function is no longer used. Let's drop. Tested-by: Kai-Heng Feng Link: https://lore.kernel.org/r/20200618110842.27238-7-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 23 ----------------------- sound/pci/hda/hda_generic.h | 2 -- 2 files changed, 25 deletions(-) diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 419cfbd0dc5c..53a328d21e96 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -4026,29 +4026,6 @@ int snd_hda_gen_add_micmute_led(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_gen_add_micmute_led); -/** - * snd_hda_gen_fixup_micmute_led - A fixup for mic-mute LED trigger - * - * Pass this function to the quirk entry if another driver supports the - * audio mic-mute LED trigger. Then this will bind the mixer capture switch - * change with the LED. - * - * Note that this fixup has to be called after other fixup that sets - * cap_sync_hook. Otherwise the chaining wouldn't work. - * - * @codec: the HDA codec - * @fix: fixup pointer - * @action: only supports HDA_FIXUP_ACT_PROBE value - * - */ -void snd_hda_gen_fixup_micmute_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PROBE) - snd_hda_gen_add_micmute_led(codec, NULL); -} -EXPORT_SYMBOL_GPL(snd_hda_gen_fixup_micmute_led); - /** * snd_dha_gen_add_micmute_led_cdev - Create a LED classdev and enable as mic-mute LED * @codec: the HDA codec diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index f56bc8da20b2..6fcb62ed30c6 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -357,8 +357,6 @@ int snd_hda_gen_fix_pin_power(struct hda_codec *codec, hda_nid_t pin); int snd_hda_gen_add_micmute_led(struct hda_codec *codec, void (*hook)(struct hda_codec *)); -void snd_hda_gen_fixup_micmute_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action); int snd_hda_gen_add_micmute_led_cdev(struct hda_codec *codec, int (*callback)(struct led_classdev *, enum led_brightness)); From fe1a162191a59cbfd3f917e92cdb2d5de996a8a0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 18 Jun 2020 13:08:36 +0200 Subject: [PATCH 07/13] ALSA: hda: generic: Drop the old mic-mute LED hook Now all users of the old snd_hda_gen_add_micmute_led() have been converted to the new LED-classdev variant, and we can make it local, and remove the unused hda_gen_spec.micmute_led.update callback field. Tested-by: Kai-Heng Feng Link: https://lore.kernel.org/r/20200618110842.27238-8-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 22 +++------------------- sound/pci/hda/hda_generic.h | 3 --- 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 53a328d21e96..c97a1404af6b 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3922,8 +3922,6 @@ static void call_micmute_led_update(struct hda_codec *codec) if (val == spec->micmute_led.led_value) return; spec->micmute_led.led_value = val; - if (spec->micmute_led.update) - spec->micmute_led.update(codec); ledtrig_audio_set(LED_AUDIO_MICMUTE, spec->micmute_led.led_value ? LED_ON : LED_OFF); } @@ -3997,20 +3995,8 @@ static const struct snd_kcontrol_new micmute_led_mode_ctl = { .put = micmute_led_mode_put, }; -/** - * snd_hda_gen_add_micmute_led - helper for setting up mic mute LED hook - * @codec: the HDA codec - * @hook: the callback for updating LED - * - * Called from the codec drivers for offering the mic mute LED controls. - * When established, it sets up cap_sync_hook and triggers the callback at - * each time when the capture mixer switch changes. The callback is supposed - * to update the LED accordingly. - * - * Returns 0 if the hook is established or a negative error code. - */ -int snd_hda_gen_add_micmute_led(struct hda_codec *codec, - void (*hook)(struct hda_codec *)) +/* Set up the capture sync hook for controlling the mic-mute LED */ +static int add_micmute_led_hook(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; @@ -4018,13 +4004,11 @@ int snd_hda_gen_add_micmute_led(struct hda_codec *codec, spec->micmute_led.capture = 0; spec->micmute_led.led_value = 0; spec->micmute_led.old_hook = spec->cap_sync_hook; - spec->micmute_led.update = hook; spec->cap_sync_hook = update_micmute_led; if (!snd_hda_gen_add_kctl(spec, NULL, &micmute_led_mode_ctl)) return -ENOMEM; return 0; } -EXPORT_SYMBOL_GPL(snd_hda_gen_add_micmute_led); /** * snd_dha_gen_add_micmute_led_cdev - Create a LED classdev and enable as mic-mute LED @@ -4065,7 +4049,7 @@ int snd_hda_gen_add_micmute_led_cdev(struct hda_codec *codec, } } - return snd_hda_gen_add_micmute_led(codec, NULL); + return add_micmute_led_hook(codec); } EXPORT_SYMBOL_GPL(snd_hda_gen_add_micmute_led_cdev); #endif /* CONFIG_SND_HDA_GENERIC_LEDS */ diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 6fcb62ed30c6..a59e66f9ff33 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -88,7 +88,6 @@ struct hda_micmute_hook { unsigned int led_mode; unsigned int capture; unsigned int led_value; - void (*update)(struct hda_codec *codec); void (*old_hook)(struct hda_codec *codec, struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); @@ -355,8 +354,6 @@ unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec, void snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on); int snd_hda_gen_fix_pin_power(struct hda_codec *codec, hda_nid_t pin); -int snd_hda_gen_add_micmute_led(struct hda_codec *codec, - void (*hook)(struct hda_codec *)); int snd_hda_gen_add_micmute_led_cdev(struct hda_codec *codec, int (*callback)(struct led_classdev *, enum led_brightness)); From 15509b6344726de22bdbfff88b65341dd0dd33af Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 18 Jun 2020 13:08:37 +0200 Subject: [PATCH 08/13] ALSA: hda: generic: Add vmaster mute LED helper Like mic-mute LED handling, add a new helper to deal with the master mute LED with LED classdev. Unlike the mic-mute case, the playback master mute is hooked on vmaster, and we suppose no nested hooks allowed there. The classdev creation code is factored out to a common function that is called from both mute and mic-mute LED helpers. Tested-by: Kai-Heng Feng Link: https://lore.kernel.org/r/20200618110842.27238-9-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 71 ++++++++++++++++++++++++++++++------- sound/pci/hda/hda_generic.h | 3 ++ 2 files changed, 62 insertions(+), 12 deletions(-) diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index c97a1404af6b..c74519d7096b 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3888,6 +3888,64 @@ static int parse_mic_boost(struct hda_codec *codec) } #ifdef CONFIG_SND_HDA_GENERIC_LEDS +/* + * vmaster mute LED hook helpers + */ + +static int create_mute_led_cdev(struct hda_codec *codec, + int (*callback)(struct led_classdev *, + enum led_brightness), + bool micmute) +{ + struct led_classdev *cdev; + + cdev = devm_kzalloc(&codec->core.dev, sizeof(*cdev), GFP_KERNEL); + if (!cdev) + return -ENOMEM; + + cdev->name = micmute ? "hda::micmute" : "hda::mute"; + cdev->max_brightness = 1; + cdev->default_trigger = micmute ? "audio-micmute" : "audio-mute"; + cdev->brightness_set_blocking = callback; + cdev->brightness = ledtrig_audio_get(micmute ? LED_AUDIO_MICMUTE : LED_AUDIO_MUTE); + + return devm_led_classdev_register(&codec->core.dev, cdev); +} + +static void vmaster_update_mute_led(void *private_data, int enabled) +{ + ledtrig_audio_set(LED_AUDIO_MUTE, enabled ? LED_OFF : LED_ON); +} + +/** + * snd_dha_gen_add_mute_led_cdev - Create a LED classdev and enable as vmaster mute LED + * @codec: the HDA codec + * @callback: the callback for LED classdev brightness_set_blocking + */ +int snd_hda_gen_add_mute_led_cdev(struct hda_codec *codec, + int (*callback)(struct led_classdev *, + enum led_brightness)) +{ + struct hda_gen_spec *spec = codec->spec; + int err; + + if (callback) { + err = create_mute_led_cdev(codec, callback, false); + if (err) { + codec_warn(codec, "failed to create a mute LED cdev\n"); + return err; + } + } + + if (spec->vmaster_mute.hook) + codec_err(codec, "vmaster hook already present before cdev!\n"); + + spec->vmaster_mute.hook = vmaster_update_mute_led; + spec->vmaster_mute_enum = 1; + return 0; +} +EXPORT_SYMBOL_GPL(snd_hda_gen_add_mute_led_cdev); + /* * mic mute LED hook helpers */ @@ -4029,20 +4087,9 @@ int snd_hda_gen_add_micmute_led_cdev(struct hda_codec *codec, enum led_brightness)) { int err; - struct led_classdev *cdev; if (callback) { - cdev = devm_kzalloc(&codec->core.dev, sizeof(*cdev), GFP_KERNEL); - if (!cdev) - return -ENOMEM; - - cdev->name = "hda::micmute"; - cdev->max_brightness = 1; - cdev->default_trigger = "audio-micmute"; - cdev->brightness_set_blocking = callback; - cdev->brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE); - - err = devm_led_classdev_register(&codec->core.dev, cdev); + err = create_mute_led_cdev(codec, callback, true); if (err) { codec_warn(codec, "failed to create a mic-mute LED cdev\n"); return err; diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index a59e66f9ff33..bbd6d2b741f2 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -354,6 +354,9 @@ unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec, void snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on); int snd_hda_gen_fix_pin_power(struct hda_codec *codec, hda_nid_t pin); +int snd_hda_gen_add_mute_led_cdev(struct hda_codec *codec, + int (*callback)(struct led_classdev *, + enum led_brightness)); int snd_hda_gen_add_micmute_led_cdev(struct hda_codec *codec, int (*callback)(struct led_classdev *, enum led_brightness)); From 8d3d1ece311837591341544e458b2dd9628fe6e0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 18 Jun 2020 13:08:38 +0200 Subject: [PATCH 09/13] ALSA: hda/realtek: Use the new vmaster mute LED helper Convert the mute LED handling in Realtek codec to the new vmaster mute helper. A point to be cautiously handled is that the value passed to the callback is inverted; the vmaster passes "enabled" (0 = mute), while LED classdev passes "brightness" (1 = mute). The code in Thinkpad helper is also converted. In that case, just call the new function and remove the open-code. Tested-by: Kai-Heng Feng Link: https://lore.kernel.org/r/20200618110842.27238-10-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 54 ++++++++++++++++++--------------- sound/pci/hda/thinkpad_helper.c | 17 ++--------- 2 files changed, 32 insertions(+), 39 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 6831587fc4d6..87f7a1c3558d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -80,7 +80,7 @@ struct alc_spec { unsigned int gpio_data; bool gpio_write_delay; /* add a delay before writing gpio_data */ - /* mute LED for HP laptops, see alc269_fixup_mic_mute_hook() */ + /* mute LED for HP laptops, see vref_mute_led_set() */ int mute_led_polarity; int micmute_led_polarity; hda_nid_t mute_led_nid; @@ -3990,23 +3990,25 @@ static void alc269_fixup_x101_headset_mic(struct hda_codec *codec, /* update mute-LED according to the speaker mute state via mic VREF pin */ -static void alc269_fixup_mic_mute_hook(void *private_data, int enabled) +static int vref_mute_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) { - struct hda_codec *codec = private_data; + struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); struct alc_spec *spec = codec->spec; unsigned int pinval; if (spec->mute_led_polarity) - enabled = !enabled; + brightness = !brightness; pinval = snd_hda_codec_get_pin_target(codec, spec->mute_led_nid); pinval &= ~AC_PINCTL_VREFEN; - pinval |= enabled ? AC_PINCTL_VREF_HIZ : AC_PINCTL_VREF_80; + pinval |= brightness ? AC_PINCTL_VREF_80 : AC_PINCTL_VREF_HIZ; if (spec->mute_led_nid) { /* temporarily power up/down for setting VREF */ snd_hda_power_up_pm(codec); snd_hda_set_pin_ctl_cache(codec, spec->mute_led_nid, pinval); snd_hda_power_down_pm(codec); } + return 0; } /* Make sure the led works even in runtime suspend */ @@ -4044,8 +4046,7 @@ static void alc269_fixup_hp_mute_led(struct hda_codec *codec, break; spec->mute_led_polarity = pol; spec->mute_led_nid = pin - 0x0a + 0x18; - spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook; - spec->gen.vmaster_mute_enum = 1; + snd_hda_gen_add_mute_led_cdev(codec, vref_mute_led_set); codec->power_filter = led_power_filter; codec_dbg(codec, "Detected mute LED for %x:%d\n", spec->mute_led_nid, @@ -4063,8 +4064,7 @@ static void alc269_fixup_hp_mute_led_micx(struct hda_codec *codec, if (action == HDA_FIXUP_ACT_PRE_PROBE) { spec->mute_led_polarity = 0; spec->mute_led_nid = pin; - spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook; - spec->gen.vmaster_mute_enum = 1; + snd_hda_gen_add_mute_led_cdev(codec, vref_mute_led_set); codec->power_filter = led_power_filter; } } @@ -4097,13 +4097,15 @@ static void alc_update_gpio_led(struct hda_codec *codec, unsigned int mask, } /* turn on/off mute LED via GPIO per vmaster hook */ -static void alc_fixup_gpio_mute_hook(void *private_data, int enabled) +static int gpio_mute_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) { - struct hda_codec *codec = private_data; + struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); struct alc_spec *spec = codec->spec; alc_update_gpio_led(codec, spec->gpio_mute_led_mask, - spec->mute_led_polarity, enabled); + spec->mute_led_polarity, !brightness); + return 0; } /* turn on/off mic-mute LED via GPIO per capture hook */ @@ -4132,7 +4134,7 @@ static void alc_fixup_hp_gpio_led(struct hda_codec *codec, return; if (mute_mask) { spec->gpio_mute_led_mask = mute_mask; - spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook; + snd_hda_gen_add_mute_led_cdev(codec, gpio_mute_led_set); } if (micmute_mask) { spec->gpio_mic_led_mask = micmute_mask; @@ -4214,19 +4216,25 @@ static void alc280_fixup_hp_gpio4(struct hda_codec *codec, } /* update mute-LED according to the speaker mute state via COEF bit */ -static void alc_fixup_mute_led_coefbit_hook(void *private_data, int enabled) +static int coef_mute_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) { - struct hda_codec *codec = private_data; + struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); struct alc_spec *spec = codec->spec; if (spec->mute_led_polarity) - enabled = !enabled; + brightness = !brightness; /* temporarily power up/down for setting COEF bit */ - enabled ? alc_update_coef_idx(codec, spec->mute_led_coef_idx, - spec->mute_led_coefbit_mask, spec->mute_led_coefbit_off) : - alc_update_coef_idx(codec, spec->mute_led_coef_idx, - spec->mute_led_coefbit_mask, spec->mute_led_coefbit_on); + if (brightness) + alc_update_coef_idx(codec, spec->mute_led_coef_idx, + spec->mute_led_coefbit_mask, + spec->mute_led_coefbit_on); + else + alc_update_coef_idx(codec, spec->mute_led_coef_idx, + spec->mute_led_coefbit_mask, + spec->mute_led_coefbit_off); + return 0; } static void alc285_fixup_hp_mute_led_coefbit(struct hda_codec *codec, @@ -4241,8 +4249,7 @@ static void alc285_fixup_hp_mute_led_coefbit(struct hda_codec *codec, spec->mute_led_coefbit_mask = 1<<3; spec->mute_led_coefbit_on = 1<<3; spec->mute_led_coefbit_off = 0; - spec->gen.vmaster_mute.hook = alc_fixup_mute_led_coefbit_hook; - spec->gen.vmaster_mute_enum = 1; + snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set); } } @@ -4258,8 +4265,7 @@ static void alc236_fixup_hp_mute_led_coefbit(struct hda_codec *codec, spec->mute_led_coefbit_mask = 1<<5; spec->mute_led_coefbit_on = 0; spec->mute_led_coefbit_off = 1<<5; - spec->gen.vmaster_mute.hook = alc_fixup_mute_led_coefbit_hook; - spec->gen.vmaster_mute_enum = 1; + snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set); } } diff --git a/sound/pci/hda/thinkpad_helper.c b/sound/pci/hda/thinkpad_helper.c index cd14d5a55b42..6698ae241efc 100644 --- a/sound/pci/hda/thinkpad_helper.c +++ b/sound/pci/hda/thinkpad_helper.c @@ -3,13 +3,11 @@ * to be included from codec driver */ -#if IS_ENABLED(CONFIG_THINKPAD_ACPI) && IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO) +#if IS_ENABLED(CONFIG_THINKPAD_ACPI) #include #include -static void (*old_vmaster_hook)(void *, int); - static bool is_thinkpad(struct hda_codec *codec) { return (codec->core.subsystem_id >> 16 == 0x17aa) && @@ -17,24 +15,13 @@ static bool is_thinkpad(struct hda_codec *codec) acpi_dev_found("IBM0068")); } -static void update_tpacpi_mute_led(void *private_data, int enabled) -{ - if (old_vmaster_hook) - old_vmaster_hook(private_data, enabled); - - ledtrig_audio_set(LED_AUDIO_MUTE, enabled ? LED_OFF : LED_ON); -} - static void hda_fixup_thinkpad_acpi(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - struct hda_gen_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PROBE) { if (!is_thinkpad(codec)) return; - old_vmaster_hook = spec->vmaster_mute.hook; - spec->vmaster_mute.hook = update_tpacpi_mute_led; + snd_hda_gen_add_mute_led_cdev(codec, NULL); snd_hda_gen_add_micmute_led_cdev(codec, NULL); } } From 929f718cb3901ad50f4bc9ddbca73b03345cca48 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 18 Jun 2020 13:08:39 +0200 Subject: [PATCH 10/13] ALSA: hda/conexant: Use the new vmaster mute LED helper Convert the mute LED handling in Conexant codec to the new vmaster mute helper. A point to be cautiously handled is that the value passed to the callback is inverted; the vmaster passes "enabled" (0 = mute), while LED classdev passes "brightness" (1 = mute). Also the assignment of the default vmaster hook is moved at a later point after the mute hook is set up. This assures no nested hook. Finally, since we enable the mute-LED kcontrols always in the helper side, the extra vmaster_mute_enum flag set up is dropped. Tested-by: Kai-Heng Feng Link: https://lore.kernel.org/r/20200618110842.27238-11-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 37 ++++++++++++++-------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 5a7c16e39d7a..d796963b80d7 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -137,14 +137,16 @@ static void cx_auto_vmaster_hook(void *private_data, int enabled) } /* turn on/off EAPD according to Master switch (inversely!) for mute LED */ -static void cx_auto_vmaster_hook_mute_led(void *private_data, int enabled) +static int cx_auto_vmaster_mute_led(struct led_classdev *led_cdev, + enum led_brightness brightness) { - struct hda_codec *codec = private_data; + struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); struct conexant_spec *spec = codec->spec; snd_hda_codec_write(codec, spec->mute_led_eapd, 0, AC_VERB_SET_EAPD_BTLENABLE, - enabled ? 0x00 : 0x02); + brightness ? 0x02 : 0x00); + return 0; } static int cx_auto_init(struct hda_codec *codec) @@ -566,7 +568,7 @@ static void cxt_fixup_mute_led_eapd(struct hda_codec *codec, if (action == HDA_FIXUP_ACT_PRE_PROBE) { spec->mute_led_eapd = 0x1b; spec->dynamic_eapd = 1; - spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook_mute_led; + snd_hda_gen_add_mute_led_cdev(codec, cx_auto_vmaster_mute_led); } } @@ -631,12 +633,14 @@ static void cxt_update_gpio_led(struct hda_codec *codec, unsigned int mask, } /* turn on/off mute LED via GPIO per vmaster hook */ -static void cxt_fixup_gpio_mute_hook(void *private_data, int enabled) +static int cxt_gpio_mute_update(struct led_classdev *led_cdev, + enum led_brightness brightness) { - struct hda_codec *codec = private_data; + struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); struct conexant_spec *spec = codec->spec; - /* muted -> LED on */ - cxt_update_gpio_led(codec, spec->gpio_mute_led_mask, !enabled); + + cxt_update_gpio_led(codec, spec->gpio_mute_led_mask, brightness); + return 0; } /* turn on/off mic-mute LED via GPIO per capture hook */ @@ -662,7 +666,7 @@ static void cxt_fixup_mute_led_gpio(struct hda_codec *codec, }; if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.vmaster_mute.hook = cxt_fixup_gpio_mute_hook; + snd_hda_gen_add_mute_led_cdev(codec, cxt_gpio_mute_update); spec->gpio_led = 0; spec->mute_led_polarity = 0; spec->gpio_mute_led_mask = 0x01; @@ -990,8 +994,6 @@ static int patch_conexant_auto(struct hda_codec *codec) cx_auto_parse_eapd(codec); spec->gen.own_eapd_ctl = 1; - if (spec->dynamic_eapd) - spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook; switch (codec->core.vendor_id) { case 0x14f15045: @@ -1024,17 +1026,8 @@ static int patch_conexant_auto(struct hda_codec *codec) break; } - /* Show mute-led control only on HP laptops - * This is a sort of white-list: on HP laptops, EAPD corresponds - * only to the mute-LED without actualy amp function. Meanwhile, - * others may use EAPD really as an amp switch, so it might be - * not good to expose it blindly. - */ - switch (codec->core.subsystem_id >> 16) { - case 0x103c: - spec->gen.vmaster_mute_enum = 1; - break; - } + if (!spec->gen.vmaster_mute.hook && spec->dynamic_eapd) + spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook; snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); From d1d37c572cf0a10a3046e6df76f65486dc2a8ee8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 18 Jun 2020 13:08:40 +0200 Subject: [PATCH 11/13] ALSA: hda/sigmatel: Use the new vmaster mute LED helper Convert the mute LED handling in Sigmatel/IDT codec to the new vmaster mute helper. A point to be cautiously handled is that the value passed to the callback is inverted; the vmaster passes "enabled" (0 = mute), while LED classdev passes "brightness" (1 = mute). A positive side-effect by this change is that the driver gets also the enum controls for the mute behavior like other drivers already had. Tested-by: Kai-Heng Feng Link: https://lore.kernel.org/r/20200618110842.27238-12-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 3b796d0d7a67..d8ed69cb9df1 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -369,10 +369,9 @@ static unsigned int stac_vref_led_power_filter(struct hda_codec *codec, } /* update mute-LED accoring to the master switch */ -static void stac_update_led_status(struct hda_codec *codec, int enabled) +static void stac_update_led_status(struct hda_codec *codec, bool muted) { struct sigmatel_spec *spec = codec->spec; - int muted = !enabled; if (!spec->gpio_led) return; @@ -396,9 +395,13 @@ static void stac_update_led_status(struct hda_codec *codec, int enabled) } /* vmaster hook to update mute LED */ -static void stac_vmaster_hook(void *private_data, int val) +static int stac_vmaster_hook(struct led_classdev *led_cdev, + enum led_brightness brightness) { - stac_update_led_status(private_data, val); + struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); + + stac_update_led_status(codec, brightness); + return 0; } /* automute hook to handle GPIO mute and EAPD updates */ @@ -4316,7 +4319,7 @@ static int stac_parse_auto_config(struct hda_codec *codec) #endif if (spec->gpio_led) - spec->gen.vmaster_mute.hook = stac_vmaster_hook; + snd_hda_gen_add_mute_led_cdev(codec, stac_vmaster_hook); if (spec->aloopback_ctl && snd_hda_get_bool_hint(codec, "loopback") == 1) { From 766538ac4af8c833990a563ed98a04a6ea5f6407 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 18 Jun 2020 13:08:41 +0200 Subject: [PATCH 12/13] ALSA: hda/realtek: Unify LED helper code Both mute and mic-mute LED callbacks do almost similar tasks with just different bits. Factor out the common code and use them from the callbacks for simplification. This ended up with covering the forgotten stuff, too; e.g. VREF LED handling required the temporary power up/down that was missing for the mute LED, or some forgotten polarity checks are added. Tested-by: Kai-Heng Feng Link: https://lore.kernel.org/r/20200618110842.27238-13-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 125 +++++++++++++++++----------------- 1 file changed, 61 insertions(+), 64 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 87f7a1c3558d..53e0eef8b042 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -67,6 +67,13 @@ struct alc_customize_define { unsigned int fixup:1; /* Means that this sku is set by driver, not read from hw */ }; +struct alc_coef_led { + unsigned int idx; + unsigned int mask; + unsigned int on; + unsigned int off; +}; + struct alc_spec { struct hda_gen_spec gen; /* must be at head */ @@ -88,14 +95,8 @@ struct alc_spec { unsigned int gpio_mute_led_mask; unsigned int gpio_mic_led_mask; - unsigned int mute_led_coef_idx; - unsigned int mute_led_coefbit_mask; - unsigned int mute_led_coefbit_on; - unsigned int mute_led_coefbit_off; - unsigned int mic_led_coef_idx; - unsigned int mic_led_coefbit_mask; - unsigned int mic_led_coefbit_on; - unsigned int mic_led_coefbit_off; + struct alc_coef_led mute_led_coef; + struct alc_coef_led mic_led_coef; hda_nid_t headset_mic_pin; hda_nid_t headphone_mic_pin; @@ -3988,6 +3989,23 @@ static void alc269_fixup_x101_headset_mic(struct hda_codec *codec, } } +static void alc_update_vref_led(struct hda_codec *codec, hda_nid_t pin, + bool polarity, bool on) +{ + unsigned int pinval; + + if (!pin) + return; + if (polarity) + on = !on; + pinval = snd_hda_codec_get_pin_target(codec, pin); + pinval &= ~AC_PINCTL_VREFEN; + pinval |= on ? AC_PINCTL_VREF_80 : AC_PINCTL_VREF_HIZ; + /* temporarily power up/down for setting VREF */ + snd_hda_power_up_pm(codec); + snd_hda_set_pin_ctl_cache(codec, pin, pinval); + snd_hda_power_down_pm(codec); +} /* update mute-LED according to the speaker mute state via mic VREF pin */ static int vref_mute_led_set(struct led_classdev *led_cdev, @@ -3995,19 +4013,9 @@ static int vref_mute_led_set(struct led_classdev *led_cdev, { struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); struct alc_spec *spec = codec->spec; - unsigned int pinval; - if (spec->mute_led_polarity) - brightness = !brightness; - pinval = snd_hda_codec_get_pin_target(codec, spec->mute_led_nid); - pinval &= ~AC_PINCTL_VREFEN; - pinval |= brightness ? AC_PINCTL_VREF_80 : AC_PINCTL_VREF_HIZ; - if (spec->mute_led_nid) { - /* temporarily power up/down for setting VREF */ - snd_hda_power_up_pm(codec); - snd_hda_set_pin_ctl_cache(codec, spec->mute_led_nid, pinval); - snd_hda_power_down_pm(codec); - } + alc_update_vref_led(codec, spec->mute_led_nid, + spec->mute_led_polarity, brightness); return 0; } @@ -4170,17 +4178,9 @@ static int vref_micmute_led_set(struct led_classdev *led_cdev, { struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); struct alc_spec *spec = codec->spec; - unsigned int pinval; - if (!spec->cap_mute_led_nid) - return 0; - pinval = snd_hda_codec_get_pin_target(codec, spec->cap_mute_led_nid); - pinval &= ~AC_PINCTL_VREFEN; - if (brightness) - pinval |= AC_PINCTL_VREF_80; - else - pinval |= AC_PINCTL_VREF_HIZ; - snd_hda_set_pin_ctl_cache(codec, spec->cap_mute_led_nid, pinval); + alc_update_vref_led(codec, spec->cap_mute_led_nid, + spec->micmute_led_polarity, brightness); return 0; } @@ -4215,6 +4215,17 @@ static void alc280_fixup_hp_gpio4(struct hda_codec *codec, } } +static void alc_update_coef_led(struct hda_codec *codec, + struct alc_coef_led *led, + bool polarity, bool on) +{ + if (polarity) + on = !on; + /* temporarily power up/down for setting COEF bit */ + alc_update_coef_idx(codec, led->idx, led->mask, + on ? led->on : led->off); +} + /* update mute-LED according to the speaker mute state via COEF bit */ static int coef_mute_led_set(struct led_classdev *led_cdev, enum led_brightness brightness) @@ -4222,18 +4233,8 @@ static int coef_mute_led_set(struct led_classdev *led_cdev, struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); struct alc_spec *spec = codec->spec; - if (spec->mute_led_polarity) - brightness = !brightness; - - /* temporarily power up/down for setting COEF bit */ - if (brightness) - alc_update_coef_idx(codec, spec->mute_led_coef_idx, - spec->mute_led_coefbit_mask, - spec->mute_led_coefbit_on); - else - alc_update_coef_idx(codec, spec->mute_led_coef_idx, - spec->mute_led_coefbit_mask, - spec->mute_led_coefbit_off); + alc_update_coef_led(codec, &spec->mute_led_coef, + spec->mute_led_polarity, brightness); return 0; } @@ -4245,10 +4246,10 @@ static void alc285_fixup_hp_mute_led_coefbit(struct hda_codec *codec, if (action == HDA_FIXUP_ACT_PRE_PROBE) { spec->mute_led_polarity = 0; - spec->mute_led_coef_idx = 0x0b; - spec->mute_led_coefbit_mask = 1<<3; - spec->mute_led_coefbit_on = 1<<3; - spec->mute_led_coefbit_off = 0; + spec->mute_led_coef.idx = 0x0b; + spec->mute_led_coef.mask = 1 << 3; + spec->mute_led_coef.on = 1 << 3; + spec->mute_led_coef.off = 0; snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set); } } @@ -4261,10 +4262,10 @@ static void alc236_fixup_hp_mute_led_coefbit(struct hda_codec *codec, if (action == HDA_FIXUP_ACT_PRE_PROBE) { spec->mute_led_polarity = 0; - spec->mute_led_coef_idx = 0x34; - spec->mute_led_coefbit_mask = 1<<5; - spec->mute_led_coefbit_on = 0; - spec->mute_led_coefbit_off = 1<<5; + spec->mute_led_coef.idx = 0x34; + spec->mute_led_coef.mask = 1 << 5; + spec->mute_led_coef.on = 0; + spec->mute_led_coef.off = 1 << 5; snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set); } } @@ -4276,12 +4277,8 @@ static int coef_micmute_led_set(struct led_classdev *led_cdev, struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); struct alc_spec *spec = codec->spec; - if (brightness) - alc_update_coef_idx(codec, spec->mic_led_coef_idx, - spec->mic_led_coefbit_mask, spec->mic_led_coefbit_on); - else - alc_update_coef_idx(codec, spec->mic_led_coef_idx, - spec->mic_led_coefbit_mask, spec->mic_led_coefbit_off); + alc_update_coef_led(codec, &spec->mic_led_coef, + spec->micmute_led_polarity, brightness); return 0; } @@ -4291,10 +4288,10 @@ static void alc285_fixup_hp_coef_micmute_led(struct hda_codec *codec, struct alc_spec *spec = codec->spec; if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->mic_led_coef_idx = 0x19; - spec->mic_led_coefbit_mask = 1<<13; - spec->mic_led_coefbit_on = 1<<13; - spec->mic_led_coefbit_off = 0; + spec->mic_led_coef.idx = 0x19; + spec->mic_led_coef.mask = 1 << 13; + spec->mic_led_coef.on = 1 << 13; + spec->mic_led_coef.off = 0; snd_hda_gen_add_micmute_led_cdev(codec, coef_micmute_led_set); } } @@ -4305,10 +4302,10 @@ static void alc236_fixup_hp_coef_micmute_led(struct hda_codec *codec, struct alc_spec *spec = codec->spec; if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->mic_led_coef_idx = 0x35; - spec->mic_led_coefbit_mask = 3<<2; - spec->mic_led_coefbit_on = 2<<2; - spec->mic_led_coefbit_off = 1<<2; + spec->mic_led_coef.idx = 0x35; + spec->mic_led_coef.mask = 3 << 2; + spec->mic_led_coef.on = 2 << 2; + spec->mic_led_coef.off = 1 << 2; snd_hda_gen_add_micmute_led_cdev(codec, coef_micmute_led_set); } } From c9e272f9e03d412073a3ae2c1814efbec10b6278 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 18 Jun 2020 13:08:42 +0200 Subject: [PATCH 13/13] ALSA: hda: Let LED cdev handling suspend/resume Set LED_CORE_SUSPENDRESUME to LED cdev flags, so that the LED core would store and restore the LED status at suspend/resume. In theory, the codec driver should be responsible for all LED bits, but this might be safer and cover the overlooked cases. Tested-by: Kai-Heng Feng Link: https://lore.kernel.org/r/20200618110842.27238-14-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index c74519d7096b..9074265d934a 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3908,6 +3908,7 @@ static int create_mute_led_cdev(struct hda_codec *codec, cdev->default_trigger = micmute ? "audio-micmute" : "audio-mute"; cdev->brightness_set_blocking = callback; cdev->brightness = ledtrig_audio_get(micmute ? LED_AUDIO_MICMUTE : LED_AUDIO_MUTE); + cdev->flags = LED_CORE_SUSPENDRESUME; return devm_led_classdev_register(&codec->core.dev, cdev); }