Merge branch 'topic/hda-micmute-led' into for-next

This is a patch set inspired by the recent patch Kai-Heng posted about
the HD-audio mic-mute LED control.  Currently HD-audio driver deals
with the mute and mic-mute LED in several different ways: primarily
with the direct callback of vmaster hook and capture sync hook, while
another with the LED class device binding.  The latter has been used
for binding with the platform device LEDs like Thinkpad, Dell,
Huawei.  And, yet, recently we added our own LED classdev for the
mic-mute LED on some HP systems although they are controlled directly
with the callback; it's exposed, however, for the DMIC that is
governed by a different ASoC driver.

This patch set is an attempt to sort out and make them consistent:
namely,
* All LEDs are now controlled via LED class device
* The generic driver provides helper functions to easily build up the
  LED class dev and the relevant mixer controls
* Conversion of the existing framework and clean ups

The patches are lightly tested in my side with a couple of machines
and also through hda-emu tests.  Some devices receive new kcontrols
for the mute LED behavior (that have been missing so far), but
anything else look good though my tests.

Link: https://lore.kernel.org/r/20200618110842.27238-1-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2020-06-19 12:14:58 +02:00
commit c7440acc85
7 changed files with 251 additions and 219 deletions

View File

@ -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.

View File

@ -3887,6 +3887,66 @@ static int parse_mic_boost(struct hda_codec *codec)
return 0;
}
#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);
cdev->flags = LED_CORE_SUSPENDRESUME;
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
*/
@ -3921,8 +3981,8 @@ 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);
}
static void update_micmute_led(struct hda_codec *codec,
@ -3994,20 +4054,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;
@ -4015,48 +4063,44 @@ 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);
#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
*
* 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.
*
* snd_dha_gen_add_micmute_led_cdev - Create a LED classdev and enable as mic-mute LED
* @codec: the HDA codec
* @fix: fixup pointer
* @action: only supports HDA_FIXUP_ACT_PROBE value
* @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.
*/
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))
{
#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO)
if (action == HDA_FIXUP_ACT_PROBE)
snd_hda_gen_add_micmute_led(codec, call_ledtrig_micmute);
#endif
int err;
if (callback) {
err = create_mute_led_cdev(codec, callback, true);
if (err) {
codec_warn(codec, "failed to create a mic-mute LED cdev\n");
return err;
}
}
return add_micmute_led_hook(codec);
}
EXPORT_SYMBOL_GPL(snd_hda_gen_fixup_micmute_led);
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

View File

@ -8,6 +8,8 @@
#ifndef __SOUND_HDA_GENERIC_H
#define __SOUND_HDA_GENERIC_H
#include <linux/leds.h>
/* table entry for multi-io paths */
struct hda_multi_io {
hda_nid_t pin; /* multi-io widget pin NID */
@ -86,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);
@ -353,9 +354,11 @@ 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 *));
void snd_hda_gen_fixup_micmute_led(struct hda_codec *codec,
const struct hda_fixup *fix, int action);
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));
#endif /* __SOUND_HDA_GENERIC_H */

View File

@ -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,21 +633,25 @@ 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 */
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;
}
@ -660,12 +666,12 @@ 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;
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)
@ -988,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:
@ -1022,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);

View File

@ -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 */
@ -80,7 +87,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;
@ -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;
@ -287,6 +288,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
@ -3981,25 +3989,34 @@ 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 void alc_update_vref_led(struct hda_codec *codec, hda_nid_t pin,
bool polarity, bool on)
{
struct hda_codec *codec = private_data;
struct alc_spec *spec = codec->spec;
unsigned int pinval;
if (spec->mute_led_polarity)
enabled = !enabled;
pinval = snd_hda_codec_get_pin_target(codec, spec->mute_led_nid);
if (!pin)
return;
if (polarity)
on = !on;
pinval = snd_hda_codec_get_pin_target(codec, pin);
pinval &= ~AC_PINCTL_VREFEN;
pinval |= enabled ? AC_PINCTL_VREF_HIZ : AC_PINCTL_VREF_80;
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);
}
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,
enum led_brightness brightness)
{
struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
struct alc_spec *spec = codec->spec;
alc_update_vref_led(codec, spec->mute_led_nid,
spec->mute_led_polarity, brightness);
return 0;
}
/* Make sure the led works even in runtime suspend */
@ -4037,8 +4054,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,
@ -4056,8 +4072,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;
}
}
@ -4090,26 +4105,18 @@ 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 */
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 +4128,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 +4135,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);
@ -4146,18 +4142,11 @@ 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;
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);
}
}
@ -4183,21 +4172,16 @@ 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;
pinval = snd_hda_codec_get_pin_target(codec, spec->cap_mute_led_nid);
pinval &= ~AC_PINCTL_VREFEN;
if (spec->gen.micmute_led.led_value)
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;
}
static void alc269_fixup_hp_gpio_mic1_led(struct hda_codec *codec,
@ -4213,7 +4197,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;
}
}
@ -4226,25 +4210,32 @@ 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;
}
}
/* 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 void alc_update_coef_led(struct hda_codec *codec,
struct alc_coef_led *led,
bool polarity, bool on)
{
struct hda_codec *codec = private_data;
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)
{
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;
/* 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);
alc_update_coef_led(codec, &spec->mute_led_coef,
spec->mute_led_polarity, brightness);
return 0;
}
static void alc285_fixup_hp_mute_led_coefbit(struct hda_codec *codec,
@ -4255,12 +4246,11 @@ 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->gen.vmaster_mute.hook = alc_fixup_mute_led_coefbit_hook;
spec->gen.vmaster_mute_enum = 1;
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);
}
}
@ -4272,26 +4262,24 @@ 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->gen.vmaster_mute.hook = alc_fixup_mute_led_coefbit_hook;
spec->gen.vmaster_mute_enum = 1;
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);
}
}
/* 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)
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;
}
static void alc285_fixup_hp_coef_micmute_led(struct hda_codec *codec,
@ -4300,11 +4288,11 @@ 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;
snd_hda_gen_add_micmute_led(codec, alc_hp_cap_micmute_update);
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);
}
}
@ -4314,11 +4302,11 @@ 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;
snd_hda_gen_add_micmute_led(codec, alc_hp_cap_micmute_update);
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);
}
}
@ -4458,7 +4446,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);
}
}
@ -6688,7 +6676,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,
@ -6791,7 +6779,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,

View File

@ -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,
@ -366,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;
@ -393,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 */
@ -4313,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) {
@ -4636,7 +4642,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);
}
}

View File

@ -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 <linux/acpi.h>
#include <linux/leds.h>
static void (*old_vmaster_hook)(void *, int);
static bool is_thinkpad(struct hda_codec *codec)
{
return (codec->core.subsystem_id >> 16 == 0x17aa) &&
@ -17,25 +15,14 @@ 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_fixup_micmute_led(codec, fix, action);
snd_hda_gen_add_mute_led_cdev(codec, NULL);
snd_hda_gen_add_micmute_led_cdev(codec, NULL);
}
}