[alsa-devel] [PATCH 007/112] ALSA: hda/realtek - Simplify the output volume initialization

Takashi Iwai tiwai at suse.de
Tue Jan 8 12:38:00 CET 2013


Simplify the output path initialization using the existing path
information instead of assuming the topology specific to Realtek
codecs.  This is also implicitly a fix for some amp values on output
pins where the old parser missed (e.g. ALC260 output pins).

The same function alc_auto_set_output_and_unmute() can be used now for
the multi-io activation, since the output selection means nothing but
activating the given output path.

And, finally at this stage, we can get rid of alc_go_down_to_selector()
and other functions that are codec really specifically to Realtek
codecs.

Signed-off-by: Takashi Iwai <tiwai at suse.de>
---
 sound/pci/hda/patch_realtek.c | 182 +++++++++++++++++++++---------------------
 1 file changed, 93 insertions(+), 89 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index f8dd753..a7899d1 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -2858,55 +2858,6 @@ static void alc_auto_init_analog_input(struct hda_codec *codec)
 	}
 }
 
-/* convert from MIX nid to DAC */
-static hda_nid_t alc_auto_mix_to_dac(struct hda_codec *codec, hda_nid_t nid)
-{
-	hda_nid_t list[5];
-	int i, num;
-
-	if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_AUD_OUT)
-		return nid;
-	num = snd_hda_get_connections(codec, nid, list, ARRAY_SIZE(list));
-	for (i = 0; i < num; i++) {
-		if (get_wcaps_type(get_wcaps(codec, list[i])) == AC_WID_AUD_OUT)
-			return list[i];
-	}
-	return 0;
-}
-
-/* go down to the selector widget before the mixer */
-static hda_nid_t alc_go_down_to_selector(struct hda_codec *codec, hda_nid_t pin)
-{
-	hda_nid_t srcs[5];
-	int num = snd_hda_get_connections(codec, pin, srcs,
-					  ARRAY_SIZE(srcs));
-	if (num != 1 ||
-	    get_wcaps_type(get_wcaps(codec, srcs[0])) != AC_WID_AUD_SEL)
-		return pin;
-	return srcs[0];
-}
-
-/* select the connection from pin to DAC if needed */
-static int alc_auto_select_dac(struct hda_codec *codec, hda_nid_t pin,
-			       hda_nid_t dac)
-{
-	hda_nid_t mix[5];
-	int i, num;
-
-	pin = alc_go_down_to_selector(codec, pin);
-	num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix));
-	if (num < 2)
-		return 0;
-	for (i = 0; i < num; i++) {
-		if (alc_auto_mix_to_dac(codec, mix[i]) == dac) {
-			snd_hda_codec_update_cache(codec, pin, 0,
-						   AC_VERB_SET_CONNECT_SEL, i);
-			return 0;
-		}
-	}
-	return 0;
-}
-
 static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid)
 {
 	struct alc_spec *spec = codec->spec;
@@ -3825,51 +3776,102 @@ static int alc_auto_create_speaker_out(struct hda_codec *codec)
 					  "Speaker");
 }
 
-static void alc_auto_set_output_and_unmute(struct hda_codec *codec,
-					      hda_nid_t pin, int pin_type,
-					      hda_nid_t dac)
+/* is a volume or mute control already present? */
+static bool __is_out_ctl_present(struct hda_codec *codec,
+				 struct nid_path *exclude_path,
+				 hda_nid_t nid, int dir, int types)
 {
-	int i, num;
-	hda_nid_t nid, mix = 0;
-	hda_nid_t srcs[HDA_MAX_CONNECTIONS];
-	struct nid_path *path;
+	struct alc_spec *spec = codec->spec;
+	int i, type;
 
-	alc_set_pin_output(codec, pin, pin_type);
-	nid = alc_go_down_to_selector(codec, pin);
-	num = snd_hda_get_connections(codec, nid, srcs, ARRAY_SIZE(srcs));
-	for (i = 0; i < num; i++) {
-		if (alc_auto_mix_to_dac(codec, srcs[i]) != dac)
+	for (i = 0; i < spec->out_path.used; i++) {
+		struct nid_path *p = snd_array_elem(&spec->out_path, i);
+		if (p == exclude_path || p->depth <= 0)
 			continue;
-		mix = srcs[i];
-		break;
+		for (type = 0; type < 2; type++) {
+			if (types & (1 << type)) {
+				unsigned int val = p->ctls[type];
+				if (get_amp_nid_(val) == nid &&
+				    get_amp_direction_(val) == dir)
+					return true;
+			}
+		}
 	}
-	if (!mix)
-		return;
+	return false;
+}
+
+#define is_out_ctl_present(codec, path, nid, dir) \
+	__is_out_ctl_present(codec, path, nid, dir, 3) /* check both types */
+#define is_out_vol_ctl_present(codec, nid, dir) \
+	__is_out_ctl_present(codec, NULL, nid, dir, 1 << NID_PATH_VOL_CTL)
+#define is_out_mute_ctl_present(codec, nid, dir) \
+	__is_out_ctl_present(codec, NULL, nid, dir, 1 << NID_PATH_MUTE_CTL)
+
+static int get_default_amp_val(struct hda_codec *codec, hda_nid_t nid, int dir)
+{
+	unsigned int caps, offset;
+	unsigned int val = 0;
+
+	caps = query_amp_caps(codec, nid, dir);
+	if (caps & AC_AMPCAP_NUM_STEPS) {
+		offset = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
+		/* if a volume control is assigned, set the lowest level
+		 * as default; otherwise set to 0dB
+		 */
+		if (is_out_vol_ctl_present(codec, nid, dir))
+			val = 0;
+		else
+			val = offset;
+	}
+	if (caps & AC_AMPCAP_MUTE) {
+		/* if a mute control is assigned, mute as default */
+		if (is_out_mute_ctl_present(codec, nid, dir))
+			val |= HDA_AMP_MUTE;
+	}
+	return val;
+}
+
+/* configure the path from the given dac to the pin as the proper output */
+static void alc_auto_set_output_and_unmute(struct hda_codec *codec,
+					   hda_nid_t pin, int pin_type,
+					   hda_nid_t dac, bool force)
+{
+	int i, val;
+	struct nid_path *path;
 
-	/* need the manual connection? */
-	if (num > 1)
-		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, i);
-	/* unmute mixer widget inputs */
-	if (nid_has_mute(codec, mix, HDA_INPUT)) {
-		snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-			    AMP_IN_UNMUTE(0));
-		snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-			    AMP_IN_UNMUTE(1));
-	}
-	/* initialize volume */
+	alc_set_pin_output(codec, pin, pin_type);
 	path = get_out_path(codec, pin, dac);
 	if (!path)
 		return;
-	nid = alc_look_for_out_vol_nid(codec, path);
-	if (nid)
-		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-				    AMP_OUT_ZERO);
 
-	/* unmute DAC if it's not assigned to a mixer */
-	nid = alc_look_for_out_mute_nid(codec, path);
-	if (nid == mix && nid_has_mute(codec, dac, HDA_OUTPUT))
-		snd_hda_codec_write(codec, dac, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-				    AMP_OUT_ZERO);
+	for (i = path->depth - 1; i >= 0; i--) {
+		hda_nid_t nid = path->path[i];
+		if (i > 0 && path->multi[i - 1])
+			snd_hda_codec_write(codec, nid, 0,
+					    AC_VERB_SET_CONNECT_SEL,
+					    path->idx[i - 1]);
+
+		if (i != 0 && i != path->depth - 1 &&
+		    (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) &&
+		    (force || !is_out_ctl_present(codec, path, nid,
+						  HDA_INPUT))) {
+			val = get_default_amp_val(codec, nid, HDA_INPUT);
+			snd_hda_codec_write(codec, nid, 0,
+					    AC_VERB_SET_AMP_GAIN_MUTE,
+					    AMP_IN_UNMUTE(0) | val);
+			snd_hda_codec_write(codec, nid, 0,
+					    AC_VERB_SET_AMP_GAIN_MUTE,
+					    AMP_IN_UNMUTE(1) | val);
+		}
+		if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) &&
+		    (force || !is_out_ctl_present(codec, path, nid,
+						  HDA_OUTPUT))) {
+			val = get_default_amp_val(codec, nid, HDA_OUTPUT);
+			snd_hda_codec_write(codec, nid, 0,
+					    AC_VERB_SET_AMP_GAIN_MUTE,
+					    AMP_OUT_UNMUTE | val);
+		}
+	}
 }
 
 static void alc_auto_init_multi_out(struct hda_codec *codec)
@@ -3882,7 +3884,8 @@ static void alc_auto_init_multi_out(struct hda_codec *codec)
 		hda_nid_t nid = spec->autocfg.line_out_pins[i];
 		if (nid)
 			alc_auto_set_output_and_unmute(codec, nid, pin_type,
-					spec->multiout.dac_nids[i]);
+					spec->multiout.dac_nids[i], true);
+
 	}
 }
 
@@ -3905,7 +3908,7 @@ static void alc_auto_init_extra_out(struct hda_codec *codec)
 			else
 				dac = spec->multiout.dac_nids[0];
 		}
-		alc_auto_set_output_and_unmute(codec, pin, PIN_HP, dac);
+		alc_auto_set_output_and_unmute(codec, pin, PIN_HP, dac, true);
 	}
 	for (i = 0; i < spec->autocfg.speaker_outs; i++) {
 		if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
@@ -3920,7 +3923,7 @@ static void alc_auto_init_extra_out(struct hda_codec *codec)
 			else
 				dac = spec->multiout.dac_nids[0];
 		}
-		alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac);
+		alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac, true);
 	}
 }
 
@@ -4081,7 +4084,8 @@ static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output)
 		if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
 			snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
 						 HDA_AMP_MUTE, 0);
-		alc_auto_select_dac(codec, nid, spec->multi_io[idx].dac);
+		alc_auto_set_output_and_unmute(codec, nid, PIN_OUT,
+					       spec->multi_io[idx].dac, false);
 	} else {
 		if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
 			snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
-- 
1.8.0.1



More information about the Alsa-devel mailing list