}
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_add_vmaster(codec, name, tlv, slaves, suffix, true, NULL)
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 */
#define HDA_AMP_MUTE 0x80
#define HDA_AMP_UNMUTE 0x00
const struct snd_kcontrol_new *mixers[5];
int num_mixers;
hda_nid_t vmaster_nid;
- struct snd_kcontrol *vmaster_sw_kctl;
- void (*vmaster_hook)(struct snd_kcontrol *, int);
+ struct hda_vmaster_mute_hook vmaster_mute;
const struct hda_verb *init_verbs[5]; /* initialization verbs
* don't forget NULL
err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
NULL, slave_pfxs,
"Playback Switch", true,
- &spec->vmaster_sw_kctl);
+ &spec->vmaster_mute.sw_kctl);
if (err < 0)
return err;
}
cx_auto_init_input(codec);
cx_auto_init_digital(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;
}
err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
if (err < 0)
return err;
- if (spec->vmaster_hook && spec->vmaster_sw_kctl) {
- snd_ctl_add_vmaster_hook(spec->vmaster_sw_kctl,
- spec->vmaster_hook, codec);
- snd_ctl_sync_vmaster_hook(spec->vmaster_sw_kctl);
+ if (spec->vmaster_mute.hook && spec->vmaster_mute.sw_kctl) {
+ err = snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute);
+ if (err < 0)
+ return err;
}
return 0;
}
/* NOTE: this should be applied via fixup once when the generic
* 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);
if (err < 0)
/* for virtual master */
hda_nid_t vmaster_nid;
- struct snd_kcontrol *vmaster_sw_kctl;
+ struct hda_vmaster_mute_hook vmaster_mute;
#ifdef CONFIG_SND_HDA_POWER_SAVE
struct hda_loopback_check loopback;
int num_loopbacks;
err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
NULL, alc_slave_pfxs,
"Playback Switch",
- true, &spec->vmaster_sw_kctl);
+ true, &spec->vmaster_mute.sw_kctl);
if (err < 0)
return err;
}
struct alc_spec *spec = codec->spec;
switch (action) {
case ALC_FIXUP_ACT_BUILD:
- if (!spec->vmaster_sw_kctl)
- return;
- snd_ctl_add_vmaster_hook(spec->vmaster_sw_kctl,
- alc269_fixup_mic2_mute_hook, codec);
+ spec->vmaster_mute.hook = alc269_fixup_mic2_mute_hook;
+ snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute);
/* fallthru */
case ALC_FIXUP_ACT_INIT:
- snd_ctl_sync_vmaster_hook(spec->vmaster_sw_kctl);
+ snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
break;
}
}
unsigned auto_dmic_cnt;
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] = {
err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
NULL, slave_pfxs,
"Playback Switch", true,
- &spec->vmaster_sw_kctl);
+ &spec->vmaster_mute.sw_kctl);
if (err < 0)
return err;
if (spec->gpio_led) {
- snd_ctl_add_vmaster_hook(spec->vmaster_sw_kctl,
- stac92xx_vmaster_hook, codec);
- snd_ctl_sync_vmaster_hook(spec->vmaster_sw_kctl);
+ spec->vmaster_mute.hook = stac92xx_vmaster_hook;
+ err = snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute);
+ if (err < 0)
+ return err;
}
if (spec->aloopback_ctl &&
snd_hda_jack_report_sync(codec);
/* sync mute LED */
- snd_ctl_sync_vmaster_hook(spec->vmaster_sw_kctl);
+ snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
if (spec->dac_list)
stac92xx_power_down(codec);
return 0;