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

Commit 1564b287 authored by Lydia Wang's avatar Lydia Wang Committed by Takashi Iwai
Browse files

ALSA: HDA VIA: Add smart5.1 function.



Smart 5.1 is for 3-jacks model, to reuse input pins as outputs.
While off, they act as "line out" / "line in" / "mic in".
While on, they acts as "line out" / "back left/right" / "center/lfe".

Signed-off-by: default avatarLydia Wang <lydiawang@viatech.com.cn>
Signed-off-by: default avatarLogan Li <loganli@viatech.com.cn>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent cdc1784d
Loading
Loading
Loading
Loading
+173 −4
Original line number Diff line number Diff line
@@ -211,7 +211,7 @@ static struct snd_kcontrol_new vt1708_control_templates[] = {

struct via_spec {
	/* codec parameterization */
	struct snd_kcontrol_new *mixers[3];
	struct snd_kcontrol_new *mixers[4];
	unsigned int num_mixers;

	struct hda_verb *init_verbs[5];
@@ -253,6 +253,7 @@ struct via_spec {
	const struct hda_input_mux *hp_mux;
	unsigned int hp_independent_mode;
	unsigned int hp_independent_mode_index;
	unsigned int smart51_enabled;

	enum VIA_HDA_CODEC codec_type;

@@ -390,6 +391,8 @@ static void via_auto_init_analog_input(struct hda_codec *codec)
	}
}

static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);

static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
				unsigned int *affected_parm)
{
@@ -400,9 +403,10 @@ static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
		& AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
	unsigned present = snd_hda_codec_read(codec, nid, 0,
					      AC_VERB_GET_PIN_SENSE, 0) >> 31;

	if ((no_presence || present) && get_defcfg_connect(def_conf)
	    != AC_JACK_PORT_NONE) {
	struct via_spec *spec = codec->spec;
	if ((spec->smart51_enabled && is_smart51_pins(spec, nid))
	    || ((no_presence || present)
		&& get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
		*affected_parm = AC_PWRST_D0; /* if it's connected */
		parm = AC_PWRST_D0;
	} else
@@ -657,6 +661,167 @@ static struct snd_kcontrol_new via_hp_mixer[] = {
	{ } /* end */
};

static void notify_aa_path_ctls(struct hda_codec *codec)
{
	int i;
	struct snd_ctl_elem_id id;
	const char *labels[] = {"Mic", "Front Mic", "Line"};

	memset(&id, 0, sizeof(id));
	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
	for (i = 0; i < ARRAY_SIZE(labels); i++) {
		sprintf(id.name, "%s Playback Volume", labels[i]);
		snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
			       &id);
	}
}

static void mute_aa_path(struct hda_codec *codec, int mute)
{
	struct via_spec *spec = codec->spec;
	hda_nid_t  nid_mixer;
	int start_idx;
	int end_idx;
	int i;
	/* get nid of MW0 and start & end index */
	switch (spec->codec_type) {
	case VT1708:
		nid_mixer = 0x17;
		start_idx = 2;
		end_idx = 4;
		break;
	case VT1709_10CH:
	case VT1709_6CH:
		nid_mixer = 0x18;
		start_idx = 2;
		end_idx = 4;
		break;
	case VT1708B_8CH:
	case VT1708B_4CH:
	case VT1708S:
		nid_mixer = 0x16;
		start_idx = 2;
		end_idx = 4;
		break;
	default:
		return;
	}
	/* check AA path's mute status */
	for (i = start_idx; i <= end_idx; i++) {
		int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
		snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i,
					 HDA_AMP_MUTE, val);
	}
}
static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin)
{
	int res = 0;
	int index;
	for (index = AUTO_PIN_MIC; index < AUTO_PIN_FRONT_LINE; index++) {
		if (pin == spec->autocfg.input_pins[index]) {
			res = 1;
			break;
		}
	}
	return res;
}

static int via_smart51_info(struct snd_kcontrol *kcontrol,
			    struct snd_ctl_elem_info *uinfo)
{
	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
	uinfo->count = 1;
	uinfo->value.integer.min = 0;
	uinfo->value.integer.max = 1;
	return 0;
}

static int via_smart51_get(struct snd_kcontrol *kcontrol,
			   struct snd_ctl_elem_value *ucontrol)
{
	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
	struct via_spec *spec = codec->spec;
	int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
	int on = 1;
	int i;

	for (i = 0; i < ARRAY_SIZE(index); i++) {
		hda_nid_t nid = spec->autocfg.input_pins[index[i]];
		if (nid) {
			int ctl =
			    snd_hda_codec_read(codec, nid, 0,
					       AC_VERB_GET_PIN_WIDGET_CONTROL,
					       0);
			if (i == AUTO_PIN_FRONT_MIC
			    && spec->hp_independent_mode)
				continue; /* ignore FMic for independent HP */
			if (ctl & AC_PINCTL_IN_EN
			    && !(ctl & AC_PINCTL_OUT_EN))
				on = 0;
		}
	}
	*ucontrol->value.integer.value = on;
	return 0;
}

static int via_smart51_put(struct snd_kcontrol *kcontrol,
			   struct snd_ctl_elem_value *ucontrol)
{
	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
	struct via_spec *spec = codec->spec;
	int out_in = *ucontrol->value.integer.value
		? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
	int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
	int i;

	for (i = 0; i < ARRAY_SIZE(index); i++) {
		hda_nid_t nid = spec->autocfg.input_pins[index[i]];
		if (i == AUTO_PIN_FRONT_MIC
		    && spec->hp_independent_mode)
			continue; /* don't retask FMic for independent HP */
		if (nid) {
			unsigned int parm = snd_hda_codec_read(
				codec, nid, 0,
				AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
			parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
			parm |= out_in;
			snd_hda_codec_write(codec, nid, 0,
					    AC_VERB_SET_PIN_WIDGET_CONTROL,
					    parm);
			if (out_in == AC_PINCTL_OUT_EN) {
				mute_aa_path(codec, 1);
				notify_aa_path_ctls(codec);
			}
		}
		if (i == AUTO_PIN_FRONT_MIC) {
			if (spec->codec_type == VT1708S) {
				/* input = index 1 (AOW3) */
				snd_hda_codec_write(
					codec, nid, 0,
					AC_VERB_SET_CONNECT_SEL, 1);
				snd_hda_codec_amp_stereo(
					codec, nid, HDA_OUTPUT,
					0, HDA_AMP_MUTE, HDA_AMP_UNMUTE);
			}
		}
	}
	spec->smart51_enabled = *ucontrol->value.integer.value;
	set_jack_power_state(codec);
	return 1;
}

static struct snd_kcontrol_new via_smart51_mixer[] = {
	{
	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
	 .name = "Smart 5.1",
	 .count = 1,
	 .info = via_smart51_info,
	 .get = via_smart51_get,
	 .put = via_smart51_put,
	 },
	{}			/* end */
};

/* capture mixer elements */
static struct snd_kcontrol_new vt1708_capture_mixer[] = {
	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
@@ -1587,6 +1752,7 @@ static int vt1708_parse_auto_config(struct hda_codec *codec)
	if (spec->hp_mux)
		spec->mixers[spec->num_mixers++] = via_hp_mixer;

	spec->mixers[spec->num_mixers++] = via_smart51_mixer;
	return 1;
}

@@ -2087,6 +2253,7 @@ static int vt1709_parse_auto_config(struct hda_codec *codec)
	if (spec->hp_mux)
		spec->mixers[spec->num_mixers++] = via_hp_mixer;

	spec->mixers[spec->num_mixers++] = via_smart51_mixer;
	return 1;
}

@@ -2649,6 +2816,7 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec)
	if (spec->hp_mux)
		spec->mixers[spec->num_mixers++] = via_hp_mixer;

	spec->mixers[spec->num_mixers++] = via_smart51_mixer;
	return 1;
}

@@ -3142,6 +3310,7 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec)
	if (spec->hp_mux)
		spec->mixers[spec->num_mixers++] = via_hp_mixer;

	spec->mixers[spec->num_mixers++] = via_smart51_mixer;
	return 1;
}