Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d2f344b5 authored by Takashi Iwai's avatar Takashi Iwai
Browse files

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: default avatarDavid Henningsson <david.henningsson@canonical.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 527c73ba
Loading
Loading
Loading
Loading
+94 −0
Original line number Diff line number Diff line
@@ -2450,6 +2450,100 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
}
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
 *
+21 −0
Original line number Diff line number Diff line
@@ -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)
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
+8 −9
Original line number Diff line number Diff line
@@ -70,8 +70,7 @@ struct conexant_spec {
	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
@@ -518,7 +517,7 @@ static int conexant_build_controls(struct hda_codec *codec)
		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;
	}
@@ -4101,7 +4100,7 @@ static int cx_auto_init(struct hda_codec *codec)
	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;
}

@@ -4347,10 +4346,10 @@ static int cx_auto_build_controls(struct hda_codec *codec)
	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;
}
@@ -4481,7 +4480,7 @@ static int patch_conexant_auto(struct hda_codec *codec)
	/* 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)
+5 −7
Original line number Diff line number Diff line
@@ -198,7 +198,7 @@ struct alc_spec {

	/* 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;
@@ -1960,7 +1960,7 @@ static int __alc_build_controls(struct hda_codec *codec)
		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;
	}
@@ -5894,13 +5894,11 @@ static void alc269_fixup_mic2_mute(struct hda_codec *codec,
	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;
	}
}
+7 −6
Original line number Diff line number Diff line
@@ -311,7 +311,7 @@ struct sigmatel_spec {
	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] = {
@@ -1160,14 +1160,15 @@ static int stac92xx_build_controls(struct hda_codec *codec)
	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 &&
@@ -4432,7 +4433,7 @@ static int stac92xx_init(struct hda_codec *codec)
	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;