Skip to content

Commit

Permalink
ALSA: HDA - remove the custom implementation for the audio LED trigger
Browse files Browse the repository at this point in the history
With the new snd-ctl-led module, we have a generic way
to trigger audio LEDs based on the sound control changes.

Remove the custom implementation from the HDA driver.

Move the LED initialization before snd_hda_gen_parse_auto_config()
call in all drivers to create marked controls there.

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20210317172945.842280-5-perex@perex.cz
Signed-off-by: Takashi Iwai <tiwai@suse.de>
  • Loading branch information
Jaroslav Kysela authored and Takashi Iwai committed Mar 30, 2021
1 parent 22d8de6 commit e65bf99
Show file tree
Hide file tree
Showing 9 changed files with 45 additions and 235 deletions.
4 changes: 1 addition & 3 deletions sound/pci/hda/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -221,10 +221,8 @@ comment "Set to Y if you want auto-loading the codec driver"

config SND_HDA_GENERIC
tristate "Enable generic HD-audio codec parser"
select NEW_LEDS if SND_HDA_GENERIC_LEDS
select SND_CTL_LED if SND_HDA_GENERIC_LEDS
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.
Expand Down
69 changes: 6 additions & 63 deletions sound/pci/hda/hda_codec.c
Original file line number Diff line number Diff line change
Expand Up @@ -1952,7 +1952,7 @@ static int add_follower(struct hda_codec *codec,
int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
unsigned int *tlv, const char * const *followers,
const char *suffix, bool init_follower_vol,
struct snd_kcontrol **ctl_ret)
unsigned int access, struct snd_kcontrol **ctl_ret)
{
struct snd_kcontrol *kctl;
int err;
Expand All @@ -1968,6 +1968,7 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
kctl = snd_ctl_make_virtual_master(name, tlv);
if (!kctl)
return -ENOMEM;
kctl->vd[0].access |= access;
err = snd_hda_ctl_add(codec, 0, kctl);
if (err < 0)
return err;
Expand All @@ -1994,87 +1995,29 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
}
EXPORT_SYMBOL_GPL(__snd_hda_add_vmaster);

/*
* mute-LED control using vmaster
*/
static int vmaster_mute_mode_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static const char * const texts[] = {
"On", "Off", "Follow Master"
};

return snd_ctl_enum_info(uinfo, 1, 3, texts);
}

static int vmaster_mute_mode_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_vmaster_mute_hook *hook = snd_kcontrol_chip(kcontrol);
ucontrol->value.enumerated.item[0] = hook->mute_mode;
return 0;
}

static int vmaster_mute_mode_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_vmaster_mute_hook *hook = snd_kcontrol_chip(kcontrol);
unsigned int old_mode = hook->mute_mode;

hook->mute_mode = ucontrol->value.enumerated.item[0];
if (hook->mute_mode > HDA_VMUTE_FOLLOW_MASTER)
hook->mute_mode = HDA_VMUTE_FOLLOW_MASTER;
if (old_mode == hook->mute_mode)
return 0;
snd_hda_sync_vmaster_hook(hook);
return 1;
}

static const struct snd_kcontrol_new vmaster_mute_mode = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Mute-LED Mode",
.info = vmaster_mute_mode_info,
.get = vmaster_mute_mode_get,
.put = vmaster_mute_mode_put,
};

/* meta hook to call each driver's vmaster hook */
static void vmaster_hook(void *private_data, int enabled)
{
struct hda_vmaster_mute_hook *hook = private_data;

if (hook->mute_mode != HDA_VMUTE_FOLLOW_MASTER)
enabled = hook->mute_mode;
hook->hook(hook->codec, enabled);
}

/**
* snd_hda_add_vmaster_hook - Add a vmaster hook for mute-LED
* snd_hda_add_vmaster_hook - Add a vmaster hw specific hook
* @codec: the HDA codec
* @hook: the vmaster hook object
* @expose_enum_ctl: flag to create an enum ctl
*
* Add a mute-LED hook with the given vmaster switch kctl.
* When @expose_enum_ctl is set, "Mute-LED Mode" control is automatically
* created and associated with the given hook.
* Add a hw specific hook (like EAPD) with the given vmaster switch kctl.
*/
int snd_hda_add_vmaster_hook(struct hda_codec *codec,
struct hda_vmaster_mute_hook *hook,
bool expose_enum_ctl)
struct hda_vmaster_mute_hook *hook)
{
struct snd_kcontrol *kctl;

if (!hook->hook || !hook->sw_kctl)
return 0;
hook->codec = codec;
hook->mute_mode = HDA_VMUTE_FOLLOW_MASTER;
snd_ctl_add_vmaster_hook(hook->sw_kctl, vmaster_hook, hook);
if (!expose_enum_ctl)
return 0;
kctl = snd_ctl_new1(&vmaster_mute_mode, hook);
if (!kctl)
return -ENOMEM;
return snd_hda_ctl_add(codec, 0, kctl);
return 0;
}
EXPORT_SYMBOL_GPL(snd_hda_add_vmaster_hook);

Expand Down
162 changes: 25 additions & 137 deletions sound/pci/hda/hda_generic.c
Original file line number Diff line number Diff line change
Expand Up @@ -981,6 +981,8 @@ add_control(struct hda_gen_spec *spec, int type, const char *name,
knew->index = cidx;
if (get_amp_nid_(val))
knew->subdevice = HDA_SUBDEV_AMP_FLAG;
if (knew->access == 0)
knew->access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
knew->private_value = val;
return knew;
}
Expand Down Expand Up @@ -3618,8 +3620,11 @@ static int add_single_cap_ctl(struct hda_codec *codec, const char *label,
amp_val_replace_channels(ctl, chs));
if (!knew)
return -ENOMEM;
if (is_switch)
if (is_switch) {
knew->put = cap_single_sw_put;
if (spec->mic_mute_led)
knew->access |= SNDRV_CTL_ELEM_ACCESS_MIC_LED;
}
if (!inv_dmic)
return 0;

Expand All @@ -3634,8 +3639,11 @@ static int add_single_cap_ctl(struct hda_codec *codec, const char *label,
amp_val_replace_channels(ctl, 2));
if (!knew)
return -ENOMEM;
if (is_switch)
if (is_switch) {
knew->put = cap_single_sw_put;
if (spec->mic_mute_led)
knew->access |= SNDRV_CTL_ELEM_ACCESS_MIC_LED;
}
return 0;
}

Expand Down Expand Up @@ -3676,6 +3684,8 @@ static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx,
knew->index = idx;
knew->private_value = sw_ctl;
knew->subdevice = HDA_SUBDEV_AMP_FLAG;
if (spec->mic_mute_led)
knew->access |= SNDRV_CTL_ELEM_ACCESS_MIC_LED;
}
return 0;
}
Expand Down Expand Up @@ -3917,11 +3927,6 @@ static int create_mute_led_cdev(struct hda_codec *codec,
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_hda_gen_add_mute_led_cdev - Create a LED classdev and enable as vmaster mute LED
* @codec: the HDA codec
Expand All @@ -3945,134 +3950,11 @@ int snd_hda_gen_add_mute_led_cdev(struct hda_codec *codec,
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;
spec->vmaster_mute_led = 1;
return 0;
}
EXPORT_SYMBOL_GPL(snd_hda_gen_add_mute_led_cdev);

/*
* mic mute LED hook helpers
*/
enum {
MICMUTE_LED_ON,
MICMUTE_LED_OFF,
MICMUTE_LED_FOLLOW_CAPTURE,
MICMUTE_LED_FOLLOW_MUTE,
};

static void call_micmute_led_update(struct hda_codec *codec)
{
struct hda_gen_spec *spec = codec->spec;
unsigned int val;

switch (spec->micmute_led.led_mode) {
case MICMUTE_LED_ON:
val = 1;
break;
case MICMUTE_LED_OFF:
val = 0;
break;
case MICMUTE_LED_FOLLOW_CAPTURE:
val = !!spec->micmute_led.capture;
break;
case MICMUTE_LED_FOLLOW_MUTE:
default:
val = !spec->micmute_led.capture;
break;
}

if (val == spec->micmute_led.led_value)
return;
spec->micmute_led.led_value = val;
ledtrig_audio_set(LED_AUDIO_MICMUTE,
spec->micmute_led.led_value ? LED_ON : LED_OFF);
}

static void update_micmute_led(struct hda_codec *codec,
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_gen_spec *spec = codec->spec;
unsigned int mask;

if (spec->micmute_led.old_hook)
spec->micmute_led.old_hook(codec, kcontrol, ucontrol);

if (!ucontrol)
return;
mask = 1U << snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
if (!strcmp("Capture Switch", ucontrol->id.name)) {
/* TODO: How do I verify if it's a mono or stereo here? */
if (ucontrol->value.integer.value[0] ||
ucontrol->value.integer.value[1])
spec->micmute_led.capture |= mask;
else
spec->micmute_led.capture &= ~mask;
call_micmute_led_update(codec);
}
}

static int micmute_led_mode_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static const char * const texts[] = {
"On", "Off", "Follow Capture", "Follow Mute",
};

return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
}

static int micmute_led_mode_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct hda_gen_spec *spec = codec->spec;

ucontrol->value.enumerated.item[0] = spec->micmute_led.led_mode;
return 0;
}

static int micmute_led_mode_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct hda_gen_spec *spec = codec->spec;
unsigned int mode;

mode = ucontrol->value.enumerated.item[0];
if (mode > MICMUTE_LED_FOLLOW_MUTE)
mode = MICMUTE_LED_FOLLOW_MUTE;
if (mode == spec->micmute_led.led_mode)
return 0;
spec->micmute_led.led_mode = mode;
call_micmute_led_update(codec);
return 1;
}

static const struct snd_kcontrol_new micmute_led_mode_ctl = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Mic Mute-LED Mode",
.info = micmute_led_mode_info,
.get = micmute_led_mode_get,
.put = micmute_led_mode_put,
};

/* 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;

spec->micmute_led.led_mode = MICMUTE_LED_FOLLOW_MUTE;
spec->micmute_led.capture = 0;
spec->micmute_led.led_value = -1;
spec->micmute_led.old_hook = spec->cap_sync_hook;
spec->cap_sync_hook = update_micmute_led;
if (!snd_hda_gen_add_kctl(spec, NULL, &micmute_led_mode_ctl))
return -ENOMEM;
return 0;
}

/**
* snd_hda_gen_add_micmute_led_cdev - Create a LED classdev and enable as mic-mute LED
* @codec: the HDA codec
Expand All @@ -4091,6 +3973,7 @@ int snd_hda_gen_add_micmute_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) {
Expand All @@ -4101,7 +3984,8 @@ int snd_hda_gen_add_micmute_led_cdev(struct hda_codec *codec,
}
}

return add_micmute_led_hook(codec);
spec->mic_mute_led = 1;
return 0;
}
EXPORT_SYMBOL_GPL(snd_hda_gen_add_micmute_led_cdev);
#endif /* CONFIG_SND_HDA_GENERIC_LEDS */
Expand Down Expand Up @@ -5060,6 +4944,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,

parse_user_hints(codec);

if (spec->vmaster_mute_led || spec->mic_mute_led)
snd_ctl_led_request();

if (spec->mixer_nid && !spec->mixer_merge_nid)
spec->mixer_merge_nid = spec->mixer_nid;

Expand Down Expand Up @@ -5291,21 +5178,22 @@ int snd_hda_gen_build_controls(struct hda_codec *codec)
!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
err = snd_hda_add_vmaster(codec, "Master Playback Volume",
spec->vmaster_tlv, follower_pfxs,
"Playback Volume");
"Playback Volume", 0);
if (err < 0)
return err;
}
if (!spec->no_analog && !spec->suppress_vmaster &&
!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
NULL, follower_pfxs,
"Playback Switch",
true, &spec->vmaster_mute.sw_kctl);
"Playback Switch", true,
spec->vmaster_mute_led ?
SNDRV_CTL_ELEM_ACCESS_SPK_LED : 0,
&spec->vmaster_mute.sw_kctl);
if (err < 0)
return err;
if (spec->vmaster_mute.hook) {
snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute,
spec->vmaster_mute_enum);
snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute);
snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
}
}
Expand Down
Loading

0 comments on commit e65bf99

Please sign in to comment.