ALSA: hda - Add "Mute-LED Mode" enum control
Create snd_hda_add_vmaster_hook() and snd_hda_sync_vmaster_hook() helper functions to handle the mute-LED in vmaster hook more commonly. In the former function, a new enum control "Mute-LED Mode" is added. This provides user to choose whether the mute-LED should be turned on/off explicitly or to follow the master-mute status. Reviewed-by: David Henningsson <david.henningsson@canonical.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
527c73bada
commit
d2f344b5e0
5 changed files with 135 additions and 22 deletions
|
@ -2450,6 +2450,100 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_HDA(__snd_hda_add_vmaster);
|
EXPORT_SYMBOL_HDA(__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[] = {
|
||||||
|
"Off", "On", "Follow Master"
|
||||||
|
};
|
||||||
|
unsigned int index;
|
||||||
|
|
||||||
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||||
|
uinfo->count = 1;
|
||||||
|
uinfo->value.enumerated.items = 3;
|
||||||
|
index = uinfo->value.enumerated.item;
|
||||||
|
if (index >= 3)
|
||||||
|
index = 2;
|
||||||
|
strcpy(uinfo->value.enumerated.name, texts[index]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a mute-LED hook with the given vmaster switch kctl
|
||||||
|
* "Mute-LED Mode" control is automatically created and associated with
|
||||||
|
* the given hook.
|
||||||
|
*/
|
||||||
|
int snd_hda_add_vmaster_hook(struct hda_codec *codec,
|
||||||
|
struct hda_vmaster_mute_hook *hook)
|
||||||
|
{
|
||||||
|
struct snd_kcontrol *kctl;
|
||||||
|
|
||||||
|
if (!hook->hook || !hook->sw_kctl)
|
||||||
|
return 0;
|
||||||
|
snd_ctl_add_vmaster_hook(hook->sw_kctl, hook->hook, codec);
|
||||||
|
hook->codec = codec;
|
||||||
|
hook->mute_mode = HDA_VMUTE_FOLLOW_MASTER;
|
||||||
|
kctl = snd_ctl_new1(&vmaster_mute_mode, hook);
|
||||||
|
if (!kctl)
|
||||||
|
return -ENOMEM;
|
||||||
|
return snd_hda_ctl_add(codec, 0, kctl);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_HDA(snd_hda_add_vmaster_hook);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call the hook with the current value for synchronization
|
||||||
|
* Should be called in init callback
|
||||||
|
*/
|
||||||
|
void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook)
|
||||||
|
{
|
||||||
|
if (!hook->hook || !hook->codec)
|
||||||
|
return;
|
||||||
|
switch (hook->mute_mode) {
|
||||||
|
case HDA_VMUTE_FOLLOW_MASTER:
|
||||||
|
snd_ctl_sync_vmaster_hook(hook->sw_kctl);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
hook->hook(hook->codec, hook->mute_mode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_HDA(snd_hda_sync_vmaster_hook);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch
|
* snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch
|
||||||
*
|
*
|
||||||
|
|
|
@ -147,6 +147,27 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
|
||||||
__snd_hda_add_vmaster(codec, name, tlv, slaves, suffix, true, NULL)
|
__snd_hda_add_vmaster(codec, name, tlv, slaves, suffix, true, NULL)
|
||||||
int snd_hda_codec_reset(struct hda_codec *codec);
|
int snd_hda_codec_reset(struct hda_codec *codec);
|
||||||
|
|
||||||
|
enum {
|
||||||
|
HDA_VMUTE_OFF,
|
||||||
|
HDA_VMUTE_ON,
|
||||||
|
HDA_VMUTE_FOLLOW_MASTER,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct hda_vmaster_mute_hook {
|
||||||
|
/* below two fields must be filled by the caller of
|
||||||
|
* snd_hda_add_vmaster_hook() beforehand
|
||||||
|
*/
|
||||||
|
struct snd_kcontrol *sw_kctl;
|
||||||
|
void (*hook)(void *, int);
|
||||||
|
/* below are initialized automatically */
|
||||||
|
unsigned int mute_mode; /* HDA_VMUTE_XXX */
|
||||||
|
struct hda_codec *codec;
|
||||||
|
};
|
||||||
|
|
||||||
|
int snd_hda_add_vmaster_hook(struct hda_codec *codec,
|
||||||
|
struct hda_vmaster_mute_hook *hook);
|
||||||
|
void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook);
|
||||||
|
|
||||||
/* amp value bits */
|
/* amp value bits */
|
||||||
#define HDA_AMP_MUTE 0x80
|
#define HDA_AMP_MUTE 0x80
|
||||||
#define HDA_AMP_UNMUTE 0x00
|
#define HDA_AMP_UNMUTE 0x00
|
||||||
|
|
|
@ -70,8 +70,7 @@ struct conexant_spec {
|
||||||
const struct snd_kcontrol_new *mixers[5];
|
const struct snd_kcontrol_new *mixers[5];
|
||||||
int num_mixers;
|
int num_mixers;
|
||||||
hda_nid_t vmaster_nid;
|
hda_nid_t vmaster_nid;
|
||||||
struct snd_kcontrol *vmaster_sw_kctl;
|
struct hda_vmaster_mute_hook vmaster_mute;
|
||||||
void (*vmaster_hook)(struct snd_kcontrol *, int);
|
|
||||||
|
|
||||||
const struct hda_verb *init_verbs[5]; /* initialization verbs
|
const struct hda_verb *init_verbs[5]; /* initialization verbs
|
||||||
* don't forget NULL
|
* don't forget NULL
|
||||||
|
@ -518,7 +517,7 @@ static int conexant_build_controls(struct hda_codec *codec)
|
||||||
err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
|
err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
|
||||||
NULL, slave_pfxs,
|
NULL, slave_pfxs,
|
||||||
"Playback Switch", true,
|
"Playback Switch", true,
|
||||||
&spec->vmaster_sw_kctl);
|
&spec->vmaster_mute.sw_kctl);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -4101,7 +4100,7 @@ static int cx_auto_init(struct hda_codec *codec)
|
||||||
cx_auto_init_input(codec);
|
cx_auto_init_input(codec);
|
||||||
cx_auto_init_digital(codec);
|
cx_auto_init_digital(codec);
|
||||||
snd_hda_jack_report_sync(codec);
|
snd_hda_jack_report_sync(codec);
|
||||||
snd_ctl_sync_vmaster_hook(spec->vmaster_sw_kctl);
|
snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4347,10 +4346,10 @@ static int cx_auto_build_controls(struct hda_codec *codec)
|
||||||
err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
|
err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
if (spec->vmaster_hook && spec->vmaster_sw_kctl) {
|
if (spec->vmaster_mute.hook && spec->vmaster_mute.sw_kctl) {
|
||||||
snd_ctl_add_vmaster_hook(spec->vmaster_sw_kctl,
|
err = snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute);
|
||||||
spec->vmaster_hook, codec);
|
if (err < 0)
|
||||||
snd_ctl_sync_vmaster_hook(spec->vmaster_sw_kctl);
|
return err;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -4481,7 +4480,7 @@ static int patch_conexant_auto(struct hda_codec *codec)
|
||||||
/* NOTE: this should be applied via fixup once when the generic
|
/* NOTE: this should be applied via fixup once when the generic
|
||||||
* fixup code is merged to hda_codec.c
|
* fixup code is merged to hda_codec.c
|
||||||
*/
|
*/
|
||||||
spec->vmaster_hook = cx_auto_vmaster_hook;
|
spec->vmaster_mute.hook = cx_auto_vmaster_hook;
|
||||||
|
|
||||||
err = cx_auto_search_adcs(codec);
|
err = cx_auto_search_adcs(codec);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
|
|
|
@ -198,7 +198,7 @@ struct alc_spec {
|
||||||
|
|
||||||
/* for virtual master */
|
/* for virtual master */
|
||||||
hda_nid_t vmaster_nid;
|
hda_nid_t vmaster_nid;
|
||||||
struct snd_kcontrol *vmaster_sw_kctl;
|
struct hda_vmaster_mute_hook vmaster_mute;
|
||||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||||
struct hda_loopback_check loopback;
|
struct hda_loopback_check loopback;
|
||||||
int num_loopbacks;
|
int num_loopbacks;
|
||||||
|
@ -1960,7 +1960,7 @@ static int __alc_build_controls(struct hda_codec *codec)
|
||||||
err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
|
err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
|
||||||
NULL, alc_slave_pfxs,
|
NULL, alc_slave_pfxs,
|
||||||
"Playback Switch",
|
"Playback Switch",
|
||||||
true, &spec->vmaster_sw_kctl);
|
true, &spec->vmaster_mute.sw_kctl);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -5894,13 +5894,11 @@ static void alc269_fixup_mic2_mute(struct hda_codec *codec,
|
||||||
struct alc_spec *spec = codec->spec;
|
struct alc_spec *spec = codec->spec;
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case ALC_FIXUP_ACT_BUILD:
|
case ALC_FIXUP_ACT_BUILD:
|
||||||
if (!spec->vmaster_sw_kctl)
|
spec->vmaster_mute.hook = alc269_fixup_mic2_mute_hook;
|
||||||
return;
|
snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute);
|
||||||
snd_ctl_add_vmaster_hook(spec->vmaster_sw_kctl,
|
|
||||||
alc269_fixup_mic2_mute_hook, codec);
|
|
||||||
/* fallthru */
|
/* fallthru */
|
||||||
case ALC_FIXUP_ACT_INIT:
|
case ALC_FIXUP_ACT_INIT:
|
||||||
snd_ctl_sync_vmaster_hook(spec->vmaster_sw_kctl);
|
snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -311,7 +311,7 @@ struct sigmatel_spec {
|
||||||
unsigned auto_dmic_cnt;
|
unsigned auto_dmic_cnt;
|
||||||
hda_nid_t auto_dmic_nids[MAX_DMICS_NUM];
|
hda_nid_t auto_dmic_nids[MAX_DMICS_NUM];
|
||||||
|
|
||||||
struct snd_kcontrol *vmaster_sw_kctl;
|
struct hda_vmaster_mute_hook vmaster_mute;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const hda_nid_t stac9200_adc_nids[1] = {
|
static const hda_nid_t stac9200_adc_nids[1] = {
|
||||||
|
@ -1160,14 +1160,15 @@ static int stac92xx_build_controls(struct hda_codec *codec)
|
||||||
err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
|
err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
|
||||||
NULL, slave_pfxs,
|
NULL, slave_pfxs,
|
||||||
"Playback Switch", true,
|
"Playback Switch", true,
|
||||||
&spec->vmaster_sw_kctl);
|
&spec->vmaster_mute.sw_kctl);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
if (spec->gpio_led) {
|
if (spec->gpio_led) {
|
||||||
snd_ctl_add_vmaster_hook(spec->vmaster_sw_kctl,
|
spec->vmaster_mute.hook = stac92xx_vmaster_hook;
|
||||||
stac92xx_vmaster_hook, codec);
|
err = snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute);
|
||||||
snd_ctl_sync_vmaster_hook(spec->vmaster_sw_kctl);
|
if (err < 0)
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spec->aloopback_ctl &&
|
if (spec->aloopback_ctl &&
|
||||||
|
@ -4432,7 +4433,7 @@ static int stac92xx_init(struct hda_codec *codec)
|
||||||
snd_hda_jack_report_sync(codec);
|
snd_hda_jack_report_sync(codec);
|
||||||
|
|
||||||
/* sync mute LED */
|
/* sync mute LED */
|
||||||
snd_ctl_sync_vmaster_hook(spec->vmaster_sw_kctl);
|
snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
|
||||||
if (spec->dac_list)
|
if (spec->dac_list)
|
||||||
stac92xx_power_down(codec);
|
stac92xx_power_down(codec);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Add table
Reference in a new issue