alsa-devel.alsa-project.org archive mirror
 help / color / mirror / Atom feed
* [PATCH RFC 000/112] HD-audio generic parser improvements
@ 2013-01-08 11:37 Takashi Iwai
  2013-01-08 11:37 ` [PATCH 001/112] ALSA: hda/realtek - Simplify alc_auto_is_dac_reachable() Takashi Iwai
                   ` (114 more replies)
  0 siblings, 115 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:37 UTC (permalink / raw)
  To: alsa-devel

Hi,

this is a large series of patches to implement the really generic
HD-audio codec parser usable for various codecs.  It's based on the
Realtek codec parser, so I started hacking patch_realtek.c for more
generalization and improvements.  Then it's merged to the existing
generic parser code, hda_generic.c, and let Realtek parser just uses
this new parser code.  In the end, patch_realtek.c contains only
the device specific fixups and the call of generic parser functions.

The series contains a few improvements in the HD-audio core code,
some of which aren't really dependent on the generic parser code.
They are just there as I've developed this for a couple of weeks.

After this patchset, most of other codecs can be migrated as well.
Currently analog, ca0110, cirrus, cmedia, conexant and via codecs
have been already rewritten (i.e. the own parser codes dropped).
patch_sigmatel.c is still not converted yet, but can be done soon
later.  I'm going to send the conversion patches for these codecs
right after this series.

I don't expect to merge all conversions of codec parsers in 3.9
kernel, but at least, the conversion of Realtek and this generic
parser code addition can be merged to 3.9.  Maybe minor and easy ones
like ca0110 and cmedia can be merged as well.


Takashi

^ permalink raw reply	[flat|nested] 123+ messages in thread

* [PATCH 001/112] ALSA: hda/realtek - Simplify alc_auto_is_dac_reachable()
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
@ 2013-01-08 11:37 ` Takashi Iwai
  2013-01-08 11:37 ` [PATCH 002/112] ALSA: hda/realtek - List up all available DACs Takashi Iwai
                   ` (113 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:37 UTC (permalink / raw)
  To: alsa-devel

Use the helper function snd_hda_get_conn_index() instead of open
codes.  This also improves the detection of some routes to DAC on
ALC260 (although the difference doesn't influence on the end
results of the mapping).

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 12 +-----------
 1 file changed, 1 insertion(+), 11 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 71ae23d..d8118c9 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -2938,19 +2938,9 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
 static bool alc_auto_is_dac_reachable(struct hda_codec *codec,
 				      hda_nid_t pin, hda_nid_t dac)
 {
-	hda_nid_t srcs[5];
-	int i, num;
-
 	if (!pin || !dac)
 		return false;
-	pin = alc_go_down_to_selector(codec, pin);
-	num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs));
-	for (i = 0; i < num; i++) {
-		hda_nid_t nid = alc_auto_mix_to_dac(codec, srcs[i]);
-		if (nid == dac)
-			return true;
-	}
-	return false;
+	return snd_hda_get_conn_index(codec, pin, dac, true) >= 0;
 }
 
 static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin)
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 002/112] ALSA: hda/realtek - List up all available DACs
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
  2013-01-08 11:37 ` [PATCH 001/112] ALSA: hda/realtek - Simplify alc_auto_is_dac_reachable() Takashi Iwai
@ 2013-01-08 11:37 ` Takashi Iwai
  2013-01-08 11:37 ` [PATCH 003/112] ALSA: hda/realtek - Add output path parser Takashi Iwai
                   ` (112 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:37 UTC (permalink / raw)
  To: alsa-devel

In the probing phase, create a list of all available DACs in the codec
and use it for checking the single DAC connections.
This list will be used in more other places in the later commits, too.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 76 +++++++++++++++++++++++++++----------------
 1 file changed, 48 insertions(+), 28 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index d8118c9..50e079f 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -172,6 +172,10 @@ struct alc_spec {
 	int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */
 	hda_nid_t inv_dmic_pin;
 
+	/* DAC list */
+	int num_all_dacs;
+	hda_nid_t all_dacs[16];
+
 	/* hooks */
 	void (*init_hook)(struct hda_codec *codec);
 #ifdef CONFIG_PM
@@ -2916,48 +2920,42 @@ static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid)
 	return false;
 }
 
+/* check whether the DAC is reachable from the pin */
+static bool alc_auto_is_dac_reachable(struct hda_codec *codec,
+				      hda_nid_t pin, hda_nid_t dac)
+{
+	if (!pin || !dac)
+		return false;
+	return snd_hda_get_conn_index(codec, pin, dac, true) >= 0;
+}
+
 /* look for an empty DAC slot */
 static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
 {
-	hda_nid_t srcs[5];
-	int i, num;
+	struct alc_spec *spec = codec->spec;
+	int i;
 
-	pin = alc_go_down_to_selector(codec, pin);
-	num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs));
-	for (i = 0; i < num; i++) {
-		hda_nid_t nid = alc_auto_mix_to_dac(codec, srcs[i]);
-		if (!nid)
+	for (i = 0; i < spec->num_all_dacs; i++) {
+		hda_nid_t nid = spec->all_dacs[i];
+		if (!nid || alc_is_dac_already_used(codec, nid))
 			continue;
-		if (!alc_is_dac_already_used(codec, nid))
+		if (alc_auto_is_dac_reachable(codec, pin, nid))
 			return nid;
 	}
 	return 0;
 }
 
-/* check whether the DAC is reachable from the pin */
-static bool alc_auto_is_dac_reachable(struct hda_codec *codec,
-				      hda_nid_t pin, hda_nid_t dac)
-{
-	if (!pin || !dac)
-		return false;
-	return snd_hda_get_conn_index(codec, pin, dac, true) >= 0;
-}
-
 static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin)
 {
 	struct alc_spec *spec = codec->spec;
-	hda_nid_t sel = alc_go_down_to_selector(codec, pin);
-	hda_nid_t nid, nid_found, srcs[5];
-	int i, num = snd_hda_get_connections(codec, sel, srcs,
-					  ARRAY_SIZE(srcs));
-	if (num == 1)
-		return alc_auto_look_for_dac(codec, pin);
-	nid_found = 0;
-	for (i = 0; i < num; i++) {
-		if (srcs[i] == spec->mixer_nid)
+	int i;
+	hda_nid_t nid_found = 0;
+
+	for (i = 0; i < spec->num_all_dacs; i++) {
+		hda_nid_t nid = spec->all_dacs[i];
+		if (!nid || alc_is_dac_already_used(codec, nid))
 			continue;
-		nid = alc_auto_mix_to_dac(codec, srcs[i]);
-		if (nid && !alc_is_dac_already_used(codec, nid)) {
+		if (alc_auto_is_dac_reachable(codec, pin, nid)) {
 			if (nid_found)
 				return 0;
 			nid_found = nid;
@@ -3308,6 +3306,26 @@ static void debug_show_configs(struct alc_spec *spec, struct auto_pin_cfg *cfg)
 		      spec->multiout.extra_out_nid[3]);
 }
 
+/* find all available DACs of the codec */
+static void alc_fill_all_nids(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int i;
+	hda_nid_t nid = codec->start_nid;
+
+	spec->num_all_dacs = 0;
+	memset(spec->all_dacs, 0, sizeof(spec->all_dacs));
+	for (i = 0; i < codec->num_nodes; i++, nid++) {
+		if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_OUT)
+			continue;
+		if (spec->num_all_dacs >= ARRAY_SIZE(spec->all_dacs)) {
+			snd_printk(KERN_ERR "hda: Too many DACs!\n");
+			break;
+		}
+		spec->all_dacs[spec->num_all_dacs++] = nid;
+	}
+}
+
 static int alc_auto_fill_dac_nids(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
@@ -3319,6 +3337,8 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec)
 	bool best_wired = true, best_mio = true;
 	bool hp_spk_swapped = false;
 
+	alc_fill_all_nids(codec);
+
 	best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL);
 	if (!best_cfg)
 		return -ENOMEM;
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 003/112] ALSA: hda/realtek - Add output path parser
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
  2013-01-08 11:37 ` [PATCH 001/112] ALSA: hda/realtek - Simplify alc_auto_is_dac_reachable() Takashi Iwai
  2013-01-08 11:37 ` [PATCH 002/112] ALSA: hda/realtek - List up all available DACs Takashi Iwai
@ 2013-01-08 11:37 ` Takashi Iwai
  2013-01-08 11:37 ` [PATCH 004/112] ALSA: hda/realtek - Manage mixer controls in out_path list Takashi Iwai
                   ` (111 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:37 UTC (permalink / raw)
  To: alsa-devel

Add the output path parser to Realtek codec driver as we already have
in patch_via.c.  The nid_path struct represents the complete output
path from a DAC to a pin.  The alc_spec contains an array of these
paths, and a new path is added at each time when a new DAC is
assigned.

So far, this path list is used only in limited codes: namely in this
patch, only alc_is_dac_already_used() checks the list instead of dac
arrays in all possible outputs.  In the later development, the path
list will be referred from more places, such as the mixer control
assignment / check, the mute/unmute of active routes, etc.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 139 ++++++++++++++++++++++++++++++++++++++----
 1 file changed, 128 insertions(+), 11 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 50e079f..9e0516d 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -99,6 +99,23 @@ enum {
 #define ALC_FIXUP_ACT_BUILD	HDA_FIXUP_ACT_BUILD
 
 
+#define MAX_NID_PATH_DEPTH	5
+
+/* output-path: DAC -> ... -> pin
+ * idx[] contains the source index number of the next widget;
+ * e.g. idx[0] is the index of the DAC selected by path[1] widget
+ * multi[] indicates whether it's a selector widget with multi-connectors
+ * (i.e. the connection selection is mandatory)
+ * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
+ */
+struct nid_path {
+	int depth;
+	hda_nid_t path[MAX_NID_PATH_DEPTH];
+	unsigned char idx[MAX_NID_PATH_DEPTH];
+	unsigned char multi[MAX_NID_PATH_DEPTH];
+	unsigned int ctls[2]; /* 0 = volume, 1 = mute */
+};
+
 struct alc_spec {
 	struct hda_gen_spec gen;
 
@@ -176,6 +193,9 @@ struct alc_spec {
 	int num_all_dacs;
 	hda_nid_t all_dacs[16];
 
+	/* output paths */
+	struct snd_array out_path;
+
 	/* hooks */
 	void (*init_hook)(struct hda_codec *codec);
 #ifdef CONFIG_PM
@@ -2407,6 +2427,7 @@ static void alc_free(struct hda_codec *codec)
 
 	alc_free_kctls(codec);
 	alc_free_bind_ctls(codec);
+	snd_array_free(&spec->out_path);
 	snd_hda_gen_free(&spec->gen);
 	kfree(spec);
 	snd_hda_detach_beep_device(codec);
@@ -2906,15 +2927,10 @@ static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid)
 {
 	struct alc_spec *spec = codec->spec;
 	int i;
-	if (found_in_nid_list(nid, spec->multiout.dac_nids,
-			      ARRAY_SIZE(spec->private_dac_nids)) ||
-	    found_in_nid_list(nid, spec->multiout.hp_out_nid,
-			      ARRAY_SIZE(spec->multiout.hp_out_nid)) ||
-	    found_in_nid_list(nid, spec->multiout.extra_out_nid,
-			      ARRAY_SIZE(spec->multiout.extra_out_nid)))
-		return true;
-	for (i = 0; i < spec->multi_ios; i++) {
-		if (spec->multi_io[i].dac == nid)
+
+	for (i = 0; i < spec->out_path.used; i++) {
+		struct nid_path *path = snd_array_elem(&spec->out_path, i);
+		if (path->path[0] == nid)
 			return true;
 	}
 	return false;
@@ -2945,6 +2961,75 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
 	return 0;
 }
 
+/* called recursively */
+static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
+				hda_nid_t target_dac, int with_aa_mix,
+				struct nid_path *path, int depth)
+{
+	struct alc_spec *spec = codec->spec;
+	hda_nid_t conn[8];
+	int i, nums;
+
+	if (nid == spec->mixer_nid) {
+		if (!with_aa_mix)
+			return false;
+		with_aa_mix = 2; /* mark aa-mix is included */
+	}
+
+	nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
+	for (i = 0; i < nums; i++) {
+		if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
+			continue;
+		if (conn[i] == target_dac ||
+		    (!target_dac && !alc_is_dac_already_used(codec, conn[i]))) {
+			/* aa-mix is requested but not included? */
+			if (!(spec->mixer_nid && with_aa_mix == 1))
+				goto found;
+		}
+	}
+	if (depth >= MAX_NID_PATH_DEPTH)
+		return false;
+	for (i = 0; i < nums; i++) {
+		unsigned int type;
+		type = get_wcaps_type(get_wcaps(codec, conn[i]));
+		if (type == AC_WID_AUD_OUT)
+			continue;
+		if (__parse_output_path(codec, conn[i], target_dac,
+					with_aa_mix, path, depth + 1))
+			goto found;
+	}
+	return false;
+
+ found:
+	path->path[path->depth] = conn[i];
+	path->idx[path->depth] = i;
+	if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
+		path->multi[path->depth] = 1;
+	path->depth++;
+	return true;
+}
+
+/* parse the output path from the given nid to the target DAC;
+ * when target_dac is 0, try to find an empty DAC;
+ * when with_aa_mix is 0, paths with spec->mixer_nid are excluded
+ */
+static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
+			      hda_nid_t target_dac, int with_aa_mix,
+			      struct nid_path *path)
+{
+	if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) {
+		path->path[path->depth] = nid;
+		path->depth++;
+#if 0
+		snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
+			    path->depth, path->path[0], path->path[1],
+			    path->path[2], path->path[3], path->path[4]);
+#endif
+		return true;
+	}
+	return false;
+}
+
 static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin)
 {
 	struct alc_spec *spec = codec->spec;
@@ -3016,6 +3101,23 @@ static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec,
 static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec,
 					  hda_nid_t pin, hda_nid_t dac);
 
+static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin,
+			     hda_nid_t dac)
+{
+	struct alc_spec *spec = codec->spec;
+	struct nid_path *path;
+
+	path = snd_array_new(&spec->out_path);
+	if (!path)
+		return false;
+	memset(path, 0, sizeof(*path));
+	if (parse_output_path(codec, pin, dac, 0, path))
+		return true;
+	/* push back */
+	spec->out_path.used--;
+	return false;
+}
+
 static int eval_shared_vol_badness(struct hda_codec *codec, hda_nid_t pin,
 				   hda_nid_t dac)
 {
@@ -3127,6 +3229,8 @@ static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs,
 			else
 				badness += bad->no_dac;
 		}
+		if (!add_new_out_path(codec, pin, dac))
+			dac = dacs[i] = 0;
 		if (dac)
 			badness += eval_shared_vol_badness(codec, pin, dac);
 	}
@@ -3144,11 +3248,16 @@ static bool alc_map_singles(struct hda_codec *codec, int outs,
 	int i;
 	bool found = false;
 	for (i = 0; i < outs; i++) {
+		hda_nid_t dac;
 		if (dacs[i])
 			continue;
-		dacs[i] = get_dac_if_single(codec, pins[i]);
-		if (dacs[i])
+		dac = get_dac_if_single(codec, pins[i]);
+		if (!dac)
+			continue;
+		if (add_new_out_path(codec, pins[i], dac)) {
+			dacs[i] = dac;
 			found = true;
+		}
 	}
 	return found;
 }
@@ -3169,6 +3278,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
 	memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid));
 	memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid));
 	spec->multi_ios = 0;
+	snd_array_free(&spec->out_path);
 	clear_vol_marks(codec);
 	badness = 0;
 
@@ -3882,6 +3992,10 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec,
 				badness++;
 				continue;
 			}
+			if (!add_new_out_path(codec, nid, dac)) {
+				badness++;
+				continue;
+			}
 			spec->multi_io[spec->multi_ios].pin = nid;
 			spec->multi_io[spec->multi_ios].dac = dac;
 			spec->multi_ios++;
@@ -3899,6 +4013,8 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec,
 			return badness; /* no badness if nothing found */
 	}
 	if (!hardwired && spec->multi_ios < 2) {
+		/* cancel newly assigned paths */
+		spec->out_path.used -= spec->multi_ios - old_pins;
 		spec->multi_ios = old_pins;
 		return badness;
 	}
@@ -4388,6 +4504,7 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid)
 	snd_hda_gen_init(&spec->gen);
 	snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32);
 	snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8);
+	snd_array_init(&spec->out_path, sizeof(struct nid_path), 8);
 
 	err = alc_codec_rename_from_preset(codec);
 	if (err < 0) {
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 004/112] ALSA: hda/realtek - Manage mixer controls in out_path list
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (2 preceding siblings ...)
  2013-01-08 11:37 ` [PATCH 003/112] ALSA: hda/realtek - Add output path parser Takashi Iwai
@ 2013-01-08 11:37 ` Takashi Iwai
  2013-01-08 11:37 ` [PATCH 005/112] ALSA: hda - Fix mono amp values in proc output Takashi Iwai
                   ` (110 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:37 UTC (permalink / raw)
  To: alsa-devel

As we parse the output paths more precisely now, we can use this path
list for parsing the widgets for volume and mute mixer controls.
The spec->vol_ctls[] and sw_ctls[] bitmasks are replaced with the
ctls[] in each output path instance.

Interestingly, this move alone automagically fixes some bugs that the
conflicting volume or mute NIDs weren't properly detected.
Also, by parsing the whole path, there are more chances to get a free
widget for volume/mute controls.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 230 ++++++++++++++++++++++++------------------
 1 file changed, 131 insertions(+), 99 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 9e0516d..298e046 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -116,6 +116,8 @@ struct nid_path {
 	unsigned int ctls[2]; /* 0 = volume, 1 = mute */
 };
 
+enum { NID_PATH_VOL_CTL = 0, NID_PATH_MUTE_CTL = 1 };
+
 struct alc_spec {
 	struct hda_gen_spec gen;
 
@@ -150,8 +152,6 @@ struct alc_spec {
 	const hda_nid_t *capsrc_nids;
 	hda_nid_t dig_in_nid;		/* digital-in NID; optional */
 	hda_nid_t mixer_nid;		/* analog-mixer NID */
-	DECLARE_BITMAP(vol_ctls, MAX_VOL_NIDS << 1);
-	DECLARE_BITMAP(sw_ctls, MAX_VOL_NIDS << 1);
 
 	/* capture setup for dynamic dual-adc switch */
 	hda_nid_t cur_adc;
@@ -2886,22 +2886,6 @@ static hda_nid_t alc_go_down_to_selector(struct hda_codec *codec, hda_nid_t pin)
 	return srcs[0];
 }
 
-/* get MIX nid connected to the given pin targeted to DAC */
-static hda_nid_t alc_auto_dac_to_mix(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));
-	for (i = 0; i < num; i++) {
-		if (alc_auto_mix_to_dac(codec, mix[i]) == dac)
-			return mix[i];
-	}
-	return 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)
@@ -3049,29 +3033,28 @@ static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin)
 	return nid_found;
 }
 
-/* mark up volume and mute control NIDs: used during badness parsing and
- * at creating actual controls
- */
-static inline unsigned int get_ctl_pos(unsigned int data)
+static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type)
 {
-	hda_nid_t nid = get_amp_nid_(data);
-	unsigned int dir;
-	if (snd_BUG_ON(nid >= MAX_VOL_NIDS))
-		return 0;
-	dir = get_amp_direction_(data);
-	return (nid << 1) | dir;
-}
+	struct alc_spec *spec = codec->spec;
+	int i;
 
-#define is_ctl_used(bits, data) \
-	test_bit(get_ctl_pos(data), bits)
-#define mark_ctl_usage(bits, data) \
-	set_bit(get_ctl_pos(data), bits)
+	for (i = 0; i < spec->out_path.used; i++) {
+		struct nid_path *path = snd_array_elem(&spec->out_path, i);
+		if (path->ctls[type] == val)
+			return true;
+	}
+	return false;
+}
 
 static void clear_vol_marks(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
-	memset(spec->vol_ctls, 0, sizeof(spec->vol_ctls));
-	memset(spec->sw_ctls, 0, sizeof(spec->sw_ctls));
+	int i;
+
+	for (i = 0; i < spec->out_path.used; i++) {
+		struct nid_path *path = snd_array_elem(&spec->out_path, i);
+		path->ctls[0] = path->ctls[1] = 0;
+	}
 }
 
 /* badness definition */
@@ -3097,9 +3080,9 @@ enum {
 };
 
 static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec,
-					   hda_nid_t pin, hda_nid_t dac);
+					   struct nid_path *path);
 static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec,
-					  hda_nid_t pin, hda_nid_t dac);
+					  struct nid_path *path);
 
 static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin,
 			     hda_nid_t dac)
@@ -3118,34 +3101,56 @@ static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin,
 	return false;
 }
 
+/* get the path pointing from the given dac to pin;
+ * passing 0 to either @pin or @dac behaves as a wildcard
+ */
+static struct nid_path *get_out_path(struct hda_codec *codec, hda_nid_t pin,
+				     hda_nid_t dac)
+{
+	struct alc_spec *spec = codec->spec;
+	int i;
+
+	for (i = 0; i < spec->out_path.used; i++) {
+		struct nid_path *path = snd_array_elem(&spec->out_path, i);
+		if (path->depth <= 0)
+			continue;
+		if ((!dac || path->path[0] == dac) &&
+		    (!pin || path->path[path->depth - 1] == pin))
+			return path;
+	}
+	return NULL;
+}
+
 static int eval_shared_vol_badness(struct hda_codec *codec, hda_nid_t pin,
 				   hda_nid_t dac)
 {
-	struct alc_spec *spec = codec->spec;
+	struct nid_path *path = get_out_path(codec, pin, dac);
 	hda_nid_t nid;
 	unsigned int val;
 	int badness = 0;
 
-	nid = alc_look_for_out_vol_nid(codec, pin, dac);
+	if (!path)
+		return BAD_SHARED_VOL * 2;
+	nid = alc_look_for_out_vol_nid(codec, path);
 	if (nid) {
 		val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
-		if (is_ctl_used(spec->vol_ctls, nid))
+		if (is_ctl_used(codec, val, NID_PATH_VOL_CTL))
 			badness += BAD_SHARED_VOL;
 		else
-			mark_ctl_usage(spec->vol_ctls, val);
+			path->ctls[NID_PATH_VOL_CTL] = val;
 	} else
 		badness += BAD_SHARED_VOL;
-	nid = alc_look_for_out_mute_nid(codec, pin, dac);
+	nid = alc_look_for_out_mute_nid(codec, path);
 	if (nid) {
 		unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid));
 		if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT)
 			val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
 		else
 			val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT);
-		if (is_ctl_used(spec->sw_ctls, val))
+		if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL))
 			badness += BAD_SHARED_VOL;
 		else
-			mark_ctl_usage(spec->sw_ctls, val);
+			path->ctls[NID_PATH_MUTE_CTL] = val;
 	} else
 		badness += BAD_SHARED_VOL;
 	return badness;
@@ -3279,7 +3284,6 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
 	memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid));
 	spec->multi_ios = 0;
 	snd_array_free(&spec->out_path);
-	clear_vol_marks(codec);
 	badness = 0;
 
 	/* fill hard-wired DACs first */
@@ -3521,10 +3525,13 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec)
 		      cfg->line_out_type, best_wired, best_mio);
 	debug_show_configs(spec, cfg);
 
-	if (cfg->line_out_pins[0])
-		spec->vmaster_nid =
-			alc_look_for_out_vol_nid(codec, cfg->line_out_pins[0],
-						 spec->multiout.dac_nids[0]);
+	if (cfg->line_out_pins[0]) {
+		struct nid_path *path = get_out_path(codec,
+						     cfg->line_out_pins[0],
+						     spec->multiout.dac_nids[0]);
+		if (path)
+			spec->vmaster_nid = alc_look_for_out_vol_nid(codec, path);
+	}
 
 	/* clear the bitmap flags for creating controls */
 	clear_vol_marks(codec);
@@ -3533,43 +3540,43 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec)
 }
 
 static int alc_auto_add_vol_ctl(struct hda_codec *codec,
-			      const char *pfx, int cidx,
-			      hda_nid_t nid, unsigned int chs)
+				const char *pfx, int cidx,
+				hda_nid_t nid, unsigned int chs,
+				struct nid_path *path)
 {
-	struct alc_spec *spec = codec->spec;
 	unsigned int val;
-	if (!nid)
+	if (!nid || !path)
 		return 0;
 	val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT);
-	if (is_ctl_used(spec->vol_ctls, val) && chs != 2) /* exclude LFE */
+	if (is_ctl_used(codec, val, NID_PATH_VOL_CTL) && chs != 2) /* exclude LFE */
 		return 0;
-	mark_ctl_usage(spec->vol_ctls, val);
+	path->ctls[NID_PATH_VOL_CTL] = val;
 	return __add_pb_vol_ctrl(codec->spec, ALC_CTL_WIDGET_VOL, pfx, cidx,
 				 val);
 }
 
 static int alc_auto_add_stereo_vol(struct hda_codec *codec,
 				   const char *pfx, int cidx,
-				   hda_nid_t nid)
+				   hda_nid_t nid, struct nid_path *path)
 {
 	int chs = 1;
 	if (get_wcaps(codec, nid) & AC_WCAP_STEREO)
 		chs = 3;
-	return alc_auto_add_vol_ctl(codec, pfx, cidx, nid, chs);
+	return alc_auto_add_vol_ctl(codec, pfx, cidx, nid, chs, path);
 }
 
 /* create a mute-switch for the given mixer widget;
  * if it has multiple sources (e.g. DAC and loopback), create a bind-mute
  */
 static int alc_auto_add_sw_ctl(struct hda_codec *codec,
-			     const char *pfx, int cidx,
-			     hda_nid_t nid, unsigned int chs)
+			       const char *pfx, int cidx,
+			       hda_nid_t nid, unsigned int chs,
+			       struct nid_path *path)
 {
-	struct alc_spec *spec = codec->spec;
 	int wid_type;
 	int type;
 	unsigned long val;
-	if (!nid)
+	if (!nid || !path)
 		return 0;
 	wid_type = get_wcaps_type(get_wcaps(codec, nid));
 	if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) {
@@ -3582,44 +3589,46 @@ static int alc_auto_add_sw_ctl(struct hda_codec *codec,
 		type = ALC_CTL_BIND_MUTE;
 		val = HDA_COMPOSE_AMP_VAL(nid, chs, 2, HDA_INPUT);
 	}
-	if (is_ctl_used(spec->sw_ctls, val) && chs != 2) /* exclude LFE */
+	if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL) && chs != 2) /* exclude LFE */
 		return 0;
-	mark_ctl_usage(spec->sw_ctls, val);
+	path->ctls[NID_PATH_MUTE_CTL] = val;
 	return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val);
 }
 
 static int alc_auto_add_stereo_sw(struct hda_codec *codec, const char *pfx,
-				  int cidx, hda_nid_t nid)
+				  int cidx, hda_nid_t nid,
+				  struct nid_path *path)
 {
 	int chs = 1;
 	if (get_wcaps(codec, nid) & AC_WCAP_STEREO)
 		chs = 3;
-	return alc_auto_add_sw_ctl(codec, pfx, cidx, nid, chs);
+	return alc_auto_add_sw_ctl(codec, pfx, cidx, nid, chs, path);
 }
 
 static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec,
-					   hda_nid_t pin, hda_nid_t dac)
+					   struct nid_path *path)
 {
-	hda_nid_t mix = alc_auto_dac_to_mix(codec, pin, dac);
-	if (nid_has_mute(codec, pin, HDA_OUTPUT))
-		return pin;
-	else if (mix && nid_has_mute(codec, mix, HDA_INPUT))
-		return mix;
-	else if (nid_has_mute(codec, dac, HDA_OUTPUT))
-		return dac;
+	int i;
+
+	for (i = path->depth - 1; i >= 0; i--) {
+		if (nid_has_mute(codec, path->path[i], HDA_OUTPUT))
+			return path->path[i];
+		if (i != path->depth - 1 && i != 0 &&
+		    nid_has_mute(codec, path->path[i], HDA_INPUT))
+			return path->path[i];
+	}
 	return 0;
 }
 
 static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec,
-					  hda_nid_t pin, hda_nid_t dac)
-{
-	hda_nid_t mix = alc_auto_dac_to_mix(codec, pin, dac);
-	if (nid_has_volume(codec, dac, HDA_OUTPUT))
-		return dac;
-	else if (nid_has_volume(codec, mix, HDA_OUTPUT))
-		return mix;
-	else if (nid_has_volume(codec, pin, HDA_OUTPUT))
-		return pin;
+					  struct nid_path *path)
+{
+	int i;
+
+	for (i = path->depth - 1; i >= 0; i--) {
+		if (nid_has_volume(codec, path->path[i], HDA_OUTPUT))
+			return path->path[i];
+	}
 	return 0;
 }
 
@@ -3639,6 +3648,7 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec,
 		int index;
 		hda_nid_t dac, pin;
 		hda_nid_t sw, vol;
+		struct nid_path *path;
 
 		dac = spec->multiout.dac_nids[i];
 		if (!dac)
@@ -3652,27 +3662,36 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec,
 			name = alc_get_line_out_pfx(spec, i, true, &index);
 		}
 
-		sw = alc_look_for_out_mute_nid(codec, pin, dac);
-		vol = alc_look_for_out_vol_nid(codec, pin, dac);
+		path = get_out_path(codec, pin, dac);
+		if (!path)
+			continue;
+		sw = alc_look_for_out_mute_nid(codec, path);
+		vol = alc_look_for_out_vol_nid(codec, path);
 		if (!name || !strcmp(name, "CLFE")) {
 			/* Center/LFE */
-			err = alc_auto_add_vol_ctl(codec, "Center", 0, vol, 1);
+			err = alc_auto_add_vol_ctl(codec, "Center", 0, vol, 1,
+						   path);
 			if (err < 0)
 				return err;
-			err = alc_auto_add_vol_ctl(codec, "LFE", 0, vol, 2);
+			err = alc_auto_add_vol_ctl(codec, "LFE", 0, vol, 2,
+						   path);
 			if (err < 0)
 				return err;
-			err = alc_auto_add_sw_ctl(codec, "Center", 0, sw, 1);
+			err = alc_auto_add_sw_ctl(codec, "Center", 0, sw, 1,
+						  path);
 			if (err < 0)
 				return err;
-			err = alc_auto_add_sw_ctl(codec, "LFE", 0, sw, 2);
+			err = alc_auto_add_sw_ctl(codec, "LFE", 0, sw, 2,
+						  path);
 			if (err < 0)
 				return err;
 		} else {
-			err = alc_auto_add_stereo_vol(codec, name, index, vol);
+			err = alc_auto_add_stereo_vol(codec, name, index, vol,
+						      path);
 			if (err < 0)
 				return err;
-			err = alc_auto_add_stereo_sw(codec, name, index, sw);
+			err = alc_auto_add_stereo_sw(codec, name, index, sw,
+						     path);
 			if (err < 0)
 				return err;
 		}
@@ -3685,9 +3704,14 @@ static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
 				     int cidx)
 {
 	struct alc_spec *spec = codec->spec;
+	struct nid_path *path;
 	hda_nid_t sw, vol;
 	int err;
 
+	path = get_out_path(codec, pin, dac);
+	if (!path)
+		return 0;
+
 	if (!dac) {
 		unsigned int val;
 		/* the corresponding DAC is already occupied */
@@ -3695,18 +3719,18 @@ static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
 			return 0; /* no way */
 		/* create a switch only */
 		val = HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT);
-		if (is_ctl_used(spec->sw_ctls, val))
+		if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL))
 			return 0; /* already created */
-		mark_ctl_usage(spec->sw_ctls, val);
+		path->ctls[NID_PATH_MUTE_CTL] = val;
 		return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, cidx, val);
 	}
 
-	sw = alc_look_for_out_mute_nid(codec, pin, dac);
-	vol = alc_look_for_out_vol_nid(codec, pin, dac);
-	err = alc_auto_add_stereo_vol(codec, pfx, cidx, vol);
+	sw = alc_look_for_out_mute_nid(codec, path);
+	vol = alc_look_for_out_vol_nid(codec, path);
+	err = alc_auto_add_stereo_vol(codec, pfx, cidx, vol, path);
 	if (err < 0)
 		return err;
-	err = alc_auto_add_stereo_sw(codec, pfx, cidx, sw);
+	err = alc_auto_add_stereo_sw(codec, pfx, cidx, sw, path);
 	if (err < 0)
 		return err;
 	return 0;
@@ -3780,9 +3804,13 @@ static int alc_auto_create_extra_outs(struct hda_codec *codec, int num_pins,
 	n = 0;
 	for (i = 0; i < num_pins; i++) {
 		hda_nid_t vol;
+		struct nid_path *path;
 		if (!pins[i] || !dacs[i])
 			continue;
-		vol = alc_look_for_out_vol_nid(codec, pins[i], dacs[i]);
+		path = get_out_path(codec, pins[i], dacs[i]);
+		if (!path)
+			continue;
+		vol = alc_look_for_out_vol_nid(codec, path);
 		if (vol)
 			ctl->values[n++] =
 				HDA_COMPOSE_AMP_VAL(vol, 3, 0, HDA_OUTPUT);
@@ -3821,6 +3849,7 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec,
 	int i, num;
 	hda_nid_t nid, mix = 0;
 	hda_nid_t srcs[HDA_MAX_CONNECTIONS];
+	struct nid_path *path;
 
 	alc_set_pin_output(codec, pin, pin_type);
 	nid = alc_go_down_to_selector(codec, pin);
@@ -3845,13 +3874,16 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec,
 			    AMP_IN_UNMUTE(1));
 	}
 	/* initialize volume */
-	nid = alc_look_for_out_vol_nid(codec, pin, dac);
+	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, pin, dac);
+	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);
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 005/112] ALSA: hda - Fix mono amp values in proc output
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (3 preceding siblings ...)
  2013-01-08 11:37 ` [PATCH 004/112] ALSA: hda/realtek - Manage mixer controls in out_path list Takashi Iwai
@ 2013-01-08 11:37 ` Takashi Iwai
  2013-01-08 11:37 ` [PATCH 006/112] ALSA: hda/realtek - Reduce vol/mute ctl lookups at parsing codec Takashi Iwai
                   ` (109 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:37 UTC (permalink / raw)
  To: alsa-devel

The mono widget is always connected to the left channel, thus the left
channel amp value also should be referred for mono widgets instead of
the right channel.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_proc.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index 045e5d3..740f46a 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -138,16 +138,17 @@ static void print_amp_vals(struct snd_info_buffer *buffer,
 	dir = dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
 	for (i = 0; i < indices; i++) {
 		snd_iprintf(buffer, " [");
+		val = snd_hda_codec_read(codec, nid, 0,
+					 AC_VERB_GET_AMP_GAIN_MUTE,
+					 AC_AMP_GET_LEFT | dir | i);
+		snd_iprintf(buffer, "0x%02x", val);
 		if (stereo) {
 			val = snd_hda_codec_read(codec, nid, 0,
 						 AC_VERB_GET_AMP_GAIN_MUTE,
-						 AC_AMP_GET_LEFT | dir | i);
-			snd_iprintf(buffer, "0x%02x ", val);
+						 AC_AMP_GET_RIGHT | dir | i);
+			snd_iprintf(buffer, " 0x%02x", val);
 		}
-		val = snd_hda_codec_read(codec, nid, 0,
-					 AC_VERB_GET_AMP_GAIN_MUTE,
-					 AC_AMP_GET_RIGHT | dir | i);
-		snd_iprintf(buffer, "0x%02x]", val);
+		snd_iprintf(buffer, "]");
 	}
 	snd_iprintf(buffer, "\n");
 }
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 006/112] ALSA: hda/realtek - Reduce vol/mute ctl lookups at parsing codec
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (4 preceding siblings ...)
  2013-01-08 11:37 ` [PATCH 005/112] ALSA: hda - Fix mono amp values in proc output Takashi Iwai
@ 2013-01-08 11:37 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 007/112] ALSA: hda/realtek - Simplify the output volume initialization Takashi Iwai
                   ` (108 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:37 UTC (permalink / raw)
  To: alsa-devel

So far, Realtek codec driver evaluates the NIDs for volume and mute
controls twice, once while parsing the DACs and evaluating the
assignment, and another while creating the mixer elements.  This is
utterly redundant and even fragile, as it's assuming that the ctl
element evaluation is identical between both parsing DACs and creating
mixer elements.

This patch simplifies the code flow by doing the volume / mute
controls evaluation only once while parsing the DACs.  The patch ended
up in larger changes than expected because of some cleanups became
mandatory.

As a gratis bonus, this patch also fixes some cases where the stereo
channels are used wrongly for mono amps.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 168 ++++++++++++++++++++----------------------
 1 file changed, 78 insertions(+), 90 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 298e046..f8dd753 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -3046,17 +3046,6 @@ static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type)
 	return false;
 }
 
-static void clear_vol_marks(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	int i;
-
-	for (i = 0; i < spec->out_path.used; i++) {
-		struct nid_path *path = snd_array_elem(&spec->out_path, i);
-		path->ctls[0] = path->ctls[1] = 0;
-	}
-}
-
 /* badness definition */
 enum {
 	/* No primary DAC is found for the main output */
@@ -3121,8 +3110,15 @@ static struct nid_path *get_out_path(struct hda_codec *codec, hda_nid_t pin,
 	return NULL;
 }
 
-static int eval_shared_vol_badness(struct hda_codec *codec, hda_nid_t pin,
-				   hda_nid_t dac)
+/* look for widgets in the path between the given NIDs appropriate for
+ * volume and mute controls, and assign the values to ctls[].
+ *
+ * When no appropriate widget is found in the path, the badness value
+ * is incremented depending on the situation.  The function returns the
+ * total badness for both volume and mute controls.
+ */
+static int assign_out_path_ctls(struct hda_codec *codec, hda_nid_t pin,
+				hda_nid_t dac)
 {
 	struct nid_path *path = get_out_path(codec, pin, dac);
 	hda_nid_t nid;
@@ -3143,7 +3139,8 @@ static int eval_shared_vol_badness(struct hda_codec *codec, hda_nid_t pin,
 	nid = alc_look_for_out_mute_nid(codec, path);
 	if (nid) {
 		unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid));
-		if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT)
+		if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT ||
+		    nid_has_mute(codec, nid, HDA_OUTPUT))
 			val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
 		else
 			val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT);
@@ -3237,7 +3234,7 @@ static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs,
 		if (!add_new_out_path(codec, pin, dac))
 			dac = dacs[i] = 0;
 		if (dac)
-			badness += eval_shared_vol_badness(codec, pin, dac);
+			badness += assign_out_path_ctls(codec, pin, dac);
 	}
 
 	return badness;
@@ -3533,36 +3530,52 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec)
 			spec->vmaster_nid = alc_look_for_out_vol_nid(codec, path);
 	}
 
-	/* clear the bitmap flags for creating controls */
-	clear_vol_marks(codec);
 	kfree(best_cfg);
 	return 0;
 }
 
+/* replace the channels in the composed amp value with the given number */
+static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs)
+{
+	val &= ~(0x3U << 16);
+	val |= chs << 16;
+	return val;
+}
+
 static int alc_auto_add_vol_ctl(struct hda_codec *codec,
 				const char *pfx, int cidx,
-				hda_nid_t nid, unsigned int chs,
+				unsigned int chs,
 				struct nid_path *path)
 {
 	unsigned int val;
-	if (!nid || !path)
+	if (!path)
 		return 0;
-	val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT);
-	if (is_ctl_used(codec, val, NID_PATH_VOL_CTL) && chs != 2) /* exclude LFE */
+	val = path->ctls[NID_PATH_VOL_CTL];
+	if (!val)
 		return 0;
-	path->ctls[NID_PATH_VOL_CTL] = val;
-	return __add_pb_vol_ctrl(codec->spec, ALC_CTL_WIDGET_VOL, pfx, cidx,
-				 val);
+	val = amp_val_replace_channels(val, chs);
+	return __add_pb_vol_ctrl(codec->spec, ALC_CTL_WIDGET_VOL, pfx, cidx, val);
+}
+
+/* return the channel bits suitable for the given path->ctls[] */
+static int get_default_ch_nums(struct hda_codec *codec, struct nid_path *path,
+			       int type)
+{
+	int chs = 1; /* mono (left only) */
+	if (path) {
+		hda_nid_t nid = get_amp_nid_(path->ctls[type]);
+		if (nid && (get_wcaps(codec, nid) & AC_WCAP_STEREO))
+			chs = 3; /* stereo */
+	}
+	return chs;
 }
 
 static int alc_auto_add_stereo_vol(struct hda_codec *codec,
 				   const char *pfx, int cidx,
-				   hda_nid_t nid, struct nid_path *path)
+				   struct nid_path *path)
 {
-	int chs = 1;
-	if (get_wcaps(codec, nid) & AC_WCAP_STEREO)
-		chs = 3;
-	return alc_auto_add_vol_ctl(codec, pfx, cidx, nid, chs, path);
+	int chs = get_default_ch_nums(codec, path, NID_PATH_VOL_CTL);
+	return alc_auto_add_vol_ctl(codec, pfx, cidx, chs, path);
 }
 
 /* create a mute-switch for the given mixer widget;
@@ -3570,39 +3583,33 @@ static int alc_auto_add_stereo_vol(struct hda_codec *codec,
  */
 static int alc_auto_add_sw_ctl(struct hda_codec *codec,
 			       const char *pfx, int cidx,
-			       hda_nid_t nid, unsigned int chs,
+			       unsigned int chs,
 			       struct nid_path *path)
 {
-	int wid_type;
-	int type;
-	unsigned long val;
-	if (!nid || !path)
+	unsigned int val;
+	int type = ALC_CTL_WIDGET_MUTE;
+
+	if (!path)
 		return 0;
-	wid_type = get_wcaps_type(get_wcaps(codec, nid));
-	if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) {
-		type = ALC_CTL_WIDGET_MUTE;
-		val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT);
-	} else if (snd_hda_get_num_conns(codec, nid) == 1) {
-		type = ALC_CTL_WIDGET_MUTE;
-		val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT);
-	} else {
-		type = ALC_CTL_BIND_MUTE;
-		val = HDA_COMPOSE_AMP_VAL(nid, chs, 2, HDA_INPUT);
-	}
-	if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL) && chs != 2) /* exclude LFE */
+	val = path->ctls[NID_PATH_MUTE_CTL];
+	if (!val)
 		return 0;
-	path->ctls[NID_PATH_MUTE_CTL] = val;
+	val = amp_val_replace_channels(val, chs);
+	if (get_amp_direction_(val) == HDA_INPUT) {
+		hda_nid_t nid = get_amp_nid_(val);
+		if (snd_hda_get_num_conns(codec, nid) > 1) {
+			type = ALC_CTL_BIND_MUTE;
+			val |= 2 << 19; /* FIXME: fixed two widgets, so far */
+		}
+	}
 	return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val);
 }
 
 static int alc_auto_add_stereo_sw(struct hda_codec *codec, const char *pfx,
-				  int cidx, hda_nid_t nid,
-				  struct nid_path *path)
+				  int cidx, struct nid_path *path)
 {
-	int chs = 1;
-	if (get_wcaps(codec, nid) & AC_WCAP_STEREO)
-		chs = 3;
-	return alc_auto_add_sw_ctl(codec, pfx, cidx, nid, chs, path);
+	int chs = get_default_ch_nums(codec, path, NID_PATH_MUTE_CTL);
+	return alc_auto_add_sw_ctl(codec, pfx, cidx, chs, path);
 }
 
 static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec,
@@ -3647,7 +3654,6 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec,
 		const char *name;
 		int index;
 		hda_nid_t dac, pin;
-		hda_nid_t sw, vol;
 		struct nid_path *path;
 
 		dac = spec->multiout.dac_nids[i];
@@ -3665,33 +3671,25 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec,
 		path = get_out_path(codec, pin, dac);
 		if (!path)
 			continue;
-		sw = alc_look_for_out_mute_nid(codec, path);
-		vol = alc_look_for_out_vol_nid(codec, path);
 		if (!name || !strcmp(name, "CLFE")) {
 			/* Center/LFE */
-			err = alc_auto_add_vol_ctl(codec, "Center", 0, vol, 1,
-						   path);
+			err = alc_auto_add_vol_ctl(codec, "Center", 0, 1, path);
 			if (err < 0)
 				return err;
-			err = alc_auto_add_vol_ctl(codec, "LFE", 0, vol, 2,
-						   path);
+			err = alc_auto_add_vol_ctl(codec, "LFE", 0, 2, path);
 			if (err < 0)
 				return err;
-			err = alc_auto_add_sw_ctl(codec, "Center", 0, sw, 1,
-						  path);
+			err = alc_auto_add_sw_ctl(codec, "Center", 0, 1, path);
 			if (err < 0)
 				return err;
-			err = alc_auto_add_sw_ctl(codec, "LFE", 0, sw, 2,
-						  path);
+			err = alc_auto_add_sw_ctl(codec, "LFE", 0, 2, path);
 			if (err < 0)
 				return err;
 		} else {
-			err = alc_auto_add_stereo_vol(codec, name, index, vol,
-						      path);
+			err = alc_auto_add_stereo_vol(codec, name, index, path);
 			if (err < 0)
 				return err;
-			err = alc_auto_add_stereo_sw(codec, name, index, sw,
-						     path);
+			err = alc_auto_add_stereo_sw(codec, name, index, path);
 			if (err < 0)
 				return err;
 		}
@@ -3703,34 +3701,19 @@ static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
 				     hda_nid_t dac, const char *pfx,
 				     int cidx)
 {
-	struct alc_spec *spec = codec->spec;
 	struct nid_path *path;
-	hda_nid_t sw, vol;
 	int err;
 
 	path = get_out_path(codec, pin, dac);
 	if (!path)
 		return 0;
-
-	if (!dac) {
-		unsigned int val;
-		/* the corresponding DAC is already occupied */
-		if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP))
-			return 0; /* no way */
-		/* create a switch only */
-		val = HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT);
-		if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL))
-			return 0; /* already created */
-		path->ctls[NID_PATH_MUTE_CTL] = val;
-		return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, cidx, val);
+	/* bind volume control will be created in the case of dac = 0 */
+	if (dac) {
+		err = alc_auto_add_stereo_vol(codec, pfx, cidx, path);
+		if (err < 0)
+			return err;
 	}
-
-	sw = alc_look_for_out_mute_nid(codec, path);
-	vol = alc_look_for_out_vol_nid(codec, path);
-	err = alc_auto_add_stereo_vol(codec, pfx, cidx, vol, path);
-	if (err < 0)
-		return err;
-	err = alc_auto_add_stereo_sw(codec, pfx, cidx, sw, path);
+	err = alc_auto_add_stereo_sw(codec, pfx, cidx, path);
 	if (err < 0)
 		return err;
 	return 0;
@@ -4051,7 +4034,12 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec,
 		return badness;
 	}
 
-	return 0;
+	/* assign volume and mute controls */
+	for (i = old_pins; i < spec->multi_ios; i++)
+		badness += assign_out_path_ctls(codec, spec->multi_io[i].pin,
+						spec->multi_io[i].dac);
+
+	return badness;
 }
 
 static int alc_auto_ch_mode_info(struct snd_kcontrol *kcontrol,
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 007/112] ALSA: hda/realtek - Simplify the output volume initialization
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (5 preceding siblings ...)
  2013-01-08 11:37 ` [PATCH 006/112] ALSA: hda/realtek - Reduce vol/mute ctl lookups at parsing codec Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 008/112] ALSA: hda/realtek - Make path->idx[] and path->multi[] consistent Takashi Iwai
                   ` (107 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

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@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

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 008/112] ALSA: hda/realtek - Make path->idx[] and path->multi[] consistent
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (6 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 007/112] ALSA: hda/realtek - Simplify the output volume initialization Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 009/112] ALSA: hda/realtek - Parse input paths Takashi Iwai
                   ` (106 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

So far, idx[i] and multi[i] indicate the attribute of the widget
path[i - 1].  This was just for simplifying the code in
__parse_output_path(), but this is rather confusing for later use.
It's more natural if both idx[i] and multi[i] point to the same widget
of path[i].  This patch changes to that way.

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

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index a7899d1..5b8308d 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -102,8 +102,8 @@ enum {
 #define MAX_NID_PATH_DEPTH	5
 
 /* output-path: DAC -> ... -> pin
- * idx[] contains the source index number of the next widget;
- * e.g. idx[0] is the index of the DAC selected by path[1] widget
+ * idx[i] contains the source index number to select on of the widget path[i];
+ * e.g. idx[1] is the index of the DAC (path[0]) selected by path[1] widget
  * multi[] indicates whether it's a selector widget with multi-connectors
  * (i.e. the connection selection is mandatory)
  * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
@@ -2937,9 +2937,9 @@ static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
 
  found:
 	path->path[path->depth] = conn[i];
-	path->idx[path->depth] = i;
+	path->idx[path->depth + 1] = i;
 	if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
-		path->multi[path->depth] = 1;
+		path->multi[path->depth + 1] = 1;
 	path->depth++;
 	return true;
 }
@@ -3846,10 +3846,10 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec,
 
 	for (i = path->depth - 1; i >= 0; i--) {
 		hda_nid_t nid = path->path[i];
-		if (i > 0 && path->multi[i - 1])
+		if (path->multi[i])
 			snd_hda_codec_write(codec, nid, 0,
 					    AC_VERB_SET_CONNECT_SEL,
-					    path->idx[i - 1]);
+					    path->idx[i]);
 
 		if (i != 0 && i != path->depth - 1 &&
 		    (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) &&
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 009/112] ALSA: hda/realtek - Parse input paths
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (7 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 008/112] ALSA: hda/realtek - Make path->idx[] and path->multi[] consistent Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 010/112] ALSA: hda/realtek - Parse analog loopback paths more generically Takashi Iwai
                   ` (105 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

Just like the output paths, parse the whole paths for inputs as well
and store in a path list.  For that purpose, rewrite the output parser
code to be generically usable.

The input path list is not referred at all in this patch.  It'll be
used to replace the fixed adc/capsrc array in later patches for more
flexible input path selections.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 103 ++++++++++++++++++++++++++++++------------
 1 file changed, 73 insertions(+), 30 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 5b8308d..9d983a2 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -101,7 +101,11 @@ enum {
 
 #define MAX_NID_PATH_DEPTH	5
 
-/* output-path: DAC -> ... -> pin
+/* Widget connection path
+ *
+ * For output, stored in the order of DAC -> ... -> pin,
+ * for input, pin -> ... -> ADC.
+ *
  * idx[i] contains the source index number to select on of the widget path[i];
  * e.g. idx[1] is the index of the DAC (path[0]) selected by path[1] widget
  * multi[] indicates whether it's a selector widget with multi-connectors
@@ -196,6 +200,9 @@ struct alc_spec {
 	/* output paths */
 	struct snd_array out_path;
 
+	/* input paths */
+	struct snd_array in_path;
+
 	/* hooks */
 	void (*init_hook)(struct hda_codec *codec);
 #ifdef CONFIG_PM
@@ -2428,6 +2435,7 @@ static void alc_free(struct hda_codec *codec)
 	alc_free_kctls(codec);
 	alc_free_bind_ctls(codec);
 	snd_array_free(&spec->out_path);
+	snd_array_free(&spec->in_path);
 	snd_hda_gen_free(&spec->gen);
 	kfree(spec);
 	snd_hda_detach_beep_device(codec);
@@ -2628,6 +2636,10 @@ static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch,
 	return channel_name[ch];
 }
 
+static bool parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
+			   hda_nid_t to_nid, int with_aa_mix,
+			   struct nid_path *path);
+
 #ifdef CONFIG_PM
 /* add the powersave loopback-list entry */
 static void add_loopback_list(struct alc_spec *spec, hda_nid_t mix, int idx)
@@ -2666,6 +2678,28 @@ static int new_analog_input(struct alc_spec *spec, hda_nid_t pin,
 	return 0;
 }
 
+static int new_capture_source(struct hda_codec *codec, int adc_idx,
+			      hda_nid_t pin, int idx, const char *label)
+{
+	struct alc_spec *spec = codec->spec;
+	struct hda_input_mux *imux = &spec->private_imux[0];
+	struct nid_path *path;
+
+	path = snd_array_new(&spec->in_path);
+	if (!path)
+		return -ENOMEM;
+	memset(path, 0, sizeof(*path));
+	if (!parse_nid_path(codec, pin, spec->adc_nids[adc_idx], 2, path)) {
+		snd_printd(KERN_ERR "invalid input path 0x%x -> 0x%x\n",
+			   pin, spec->adc_nids[adc_idx]);
+		return -EINVAL;
+	}
+
+	spec->imux_pins[imux->num_items] = pin;
+	snd_hda_add_imux_item(imux, label, idx, NULL);
+	return 0;
+}
+
 static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid)
 {
 	unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
@@ -2767,8 +2801,9 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec)
 			hda_nid_t cap = get_capsrc(spec, c);
 			idx = get_connection_index(codec, cap, pin);
 			if (idx >= 0) {
-				spec->imux_pins[imux->num_items] = pin;
-				snd_hda_add_imux_item(imux, label, idx, NULL);
+				err = new_capture_source(codec, c, pin, idx, label);
+				if (err < 0)
+					return err;
 				break;
 			}
 		}
@@ -2897,40 +2932,45 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
 }
 
 /* called recursively */
-static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
-				hda_nid_t target_dac, int with_aa_mix,
-				struct nid_path *path, int depth)
+static bool __parse_nid_path(struct hda_codec *codec,
+			     hda_nid_t from_nid, hda_nid_t to_nid,
+			     int with_aa_mix, struct nid_path *path, int depth)
 {
 	struct alc_spec *spec = codec->spec;
-	hda_nid_t conn[8];
+	hda_nid_t conn[16];
 	int i, nums;
 
-	if (nid == spec->mixer_nid) {
+	if (to_nid == spec->mixer_nid) {
 		if (!with_aa_mix)
 			return false;
 		with_aa_mix = 2; /* mark aa-mix is included */
 	}
 
-	nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
+	nums = snd_hda_get_connections(codec, to_nid, conn, ARRAY_SIZE(conn));
 	for (i = 0; i < nums; i++) {
-		if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
-			continue;
-		if (conn[i] == target_dac ||
-		    (!target_dac && !alc_is_dac_already_used(codec, conn[i]))) {
-			/* aa-mix is requested but not included? */
-			if (!(spec->mixer_nid && with_aa_mix == 1))
-				goto found;
+		if (conn[i] != from_nid) {
+			/* special case: when from_nid is 0,
+			 * try to find an empty DAC
+			 */
+			if (from_nid ||
+			    get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT ||
+			    alc_is_dac_already_used(codec, conn[i]))
+				continue;
 		}
+		/* aa-mix is requested but not included? */
+		if (!(spec->mixer_nid && with_aa_mix == 1))
+			goto found;
 	}
 	if (depth >= MAX_NID_PATH_DEPTH)
 		return false;
 	for (i = 0; i < nums; i++) {
 		unsigned int type;
 		type = get_wcaps_type(get_wcaps(codec, conn[i]));
-		if (type == AC_WID_AUD_OUT)
+		if (type == AC_WID_AUD_OUT || type == AC_WID_AUD_IN ||
+		    type == AC_WID_PIN)
 			continue;
-		if (__parse_output_path(codec, conn[i], target_dac,
-					with_aa_mix, path, depth + 1))
+		if (__parse_nid_path(codec, from_nid, conn[i],
+				     with_aa_mix, path, depth + 1))
 			goto found;
 	}
 	return false;
@@ -2938,25 +2978,27 @@ static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
  found:
 	path->path[path->depth] = conn[i];
 	path->idx[path->depth + 1] = i;
-	if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
+	if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX)
 		path->multi[path->depth + 1] = 1;
 	path->depth++;
 	return true;
 }
 
-/* parse the output path from the given nid to the target DAC;
- * when target_dac is 0, try to find an empty DAC;
- * when with_aa_mix is 0, paths with spec->mixer_nid are excluded
+/* parse the widget path from the given nid to the target nid;
+ * when @from_nid is 0, try to find an empty DAC;
+ * when @with_aa_mix is 0, paths with spec->mixer_nid are excluded.
+ * when @with_aa_mix is 1, paths without spec->mixer_nid are excluded.
+ * when @with_aa_mix is 2, no special handling about spec->mixer_nid.
  */
-static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
-			      hda_nid_t target_dac, int with_aa_mix,
-			      struct nid_path *path)
+static bool parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
+			   hda_nid_t to_nid, int with_aa_mix,
+			   struct nid_path *path)
 {
-	if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) {
-		path->path[path->depth] = nid;
+	if (__parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path, 1)) {
+		path->path[path->depth] = to_nid;
 		path->depth++;
 #if 0
-		snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
+		snd_printdd("path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
 			    path->depth, path->path[0], path->path[1],
 			    path->path[2], path->path[3], path->path[4]);
 #endif
@@ -3034,7 +3076,7 @@ static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin,
 	if (!path)
 		return false;
 	memset(path, 0, sizeof(*path));
-	if (parse_output_path(codec, pin, dac, 0, path))
+	if (parse_nid_path(codec, dac, pin, 0, path))
 		return true;
 	/* push back */
 	spec->out_path.used--;
@@ -4529,6 +4571,7 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid)
 	snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32);
 	snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8);
 	snd_array_init(&spec->out_path, sizeof(struct nid_path), 8);
+	snd_array_init(&spec->in_path, sizeof(struct nid_path), 8);
 
 	err = alc_codec_rename_from_preset(codec);
 	if (err < 0) {
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 010/112] ALSA: hda/realtek - Parse analog loopback paths more generically
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (8 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 009/112] ALSA: hda/realtek - Parse input paths Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 011/112] ALSA: hda/realtek - Check amp capabilities of aa-mixer widget Takashi Iwai
                   ` (104 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

Improve the parser of analog loopback paths and handle in a more
generic way.  The following changes are included in this patch:

- Instead of assuming direct connections between pins and
  the mixer widget, track the whole path between them.  This fixes
  some missing connections like ALC660.

- Introduce the path list for loopback paths like input and output
  path lists.  Currently it's not used for any real purposes, yet.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 38 ++++++++++++++++++++++++++++++--------
 1 file changed, 30 insertions(+), 8 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 9d983a2..6b3e67c 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -203,6 +203,9 @@ struct alc_spec {
 	/* input paths */
 	struct snd_array in_path;
 
+	/* analog loopback paths */
+	struct snd_array loopback_path;
+
 	/* hooks */
 	void (*init_hook)(struct hda_codec *codec);
 #ifdef CONFIG_PM
@@ -2436,6 +2439,7 @@ static void alc_free(struct hda_codec *codec)
 	alc_free_bind_ctls(codec);
 	snd_array_free(&spec->out_path);
 	snd_array_free(&spec->in_path);
+	snd_array_free(&spec->loopback_path);
 	snd_hda_gen_free(&spec->gen);
 	kfree(spec);
 	snd_hda_detach_beep_device(codec);
@@ -2660,12 +2664,22 @@ static void add_loopback_list(struct alc_spec *spec, hda_nid_t mix, int idx)
 #endif
 
 /* create input playback/capture controls for the given pin */
-static int new_analog_input(struct alc_spec *spec, hda_nid_t pin,
+static int new_analog_input(struct hda_codec *codec, hda_nid_t pin,
 			    const char *ctlname, int ctlidx,
-			    int idx, hda_nid_t mix_nid)
+			    hda_nid_t mix_nid)
 {
-	int err;
+	struct alc_spec *spec = codec->spec;
+	struct nid_path *path;
+	int err, idx;
+
+	path = snd_array_new(&spec->loopback_path);
+	if (!path)
+		return -ENOMEM;
+	memset(path, 0, sizeof(*path));
+	if (!parse_nid_path(codec, pin, mix_nid, 2, path))
+		return -EINVAL;
 
+	idx = path->idx[path->depth - 1];
 	err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx,
 			  HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
 	if (err < 0)
@@ -2706,6 +2720,15 @@ static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid)
 	return (pincap & AC_PINCAP_IN) != 0;
 }
 
+/* check whether the given two widgets can be connected */
+static bool is_reachable_path(struct hda_codec *codec,
+			      hda_nid_t from_nid, hda_nid_t to_nid)
+{
+	if (!from_nid || !to_nid)
+		return false;
+	return snd_hda_get_conn_index(codec, to_nid, from_nid, true) >= 0;
+}
+
 /* Parse the codec tree and retrieve ADCs and corresponding capsrc MUXs */
 static int alc_auto_fill_adc_caps(struct hda_codec *codec)
 {
@@ -2787,11 +2810,9 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec)
 		prev_label = label;
 
 		if (mixer) {
-			idx = get_connection_index(codec, mixer, pin);
-			if (idx >= 0) {
-				err = new_analog_input(spec, pin,
-						       label, type_idx,
-						       idx, mixer);
+			if (is_reachable_path(codec, pin, mixer)) {
+				err = new_analog_input(codec, pin,
+						       label, type_idx, mixer);
 				if (err < 0)
 					return err;
 			}
@@ -4572,6 +4593,7 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid)
 	snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8);
 	snd_array_init(&spec->out_path, sizeof(struct nid_path), 8);
 	snd_array_init(&spec->in_path, sizeof(struct nid_path), 8);
+	snd_array_init(&spec->loopback_path, sizeof(struct nid_path), 8);
 
 	err = alc_codec_rename_from_preset(codec);
 	if (err < 0) {
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 011/112] ALSA: hda/realtek - Check amp capabilities of aa-mixer widget
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (9 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 010/112] ALSA: hda/realtek - Parse analog loopback paths more generically Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 012/112] ALSA: hda/realtek - Fix initialization of input amps in output paths Takashi Iwai
                   ` (103 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

For handling the analog-loopback paths more generically, check the amp
capabilities of the aa-mixer widget, and create only the appropriate
mixer elements.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 22 ++++++++++++++++------
 1 file changed, 16 insertions(+), 6 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 6b3e67c..1abcd8e 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -2672,6 +2672,10 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin,
 	struct nid_path *path;
 	int err, idx;
 
+	if (!nid_has_volume(codec, mix_nid, HDA_INPUT) &&
+	    !nid_has_mute(codec, mix_nid, HDA_INPUT))
+		return 0; /* no need for analog loopback */
+
 	path = snd_array_new(&spec->loopback_path);
 	if (!path)
 		return -ENOMEM;
@@ -2680,14 +2684,20 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin,
 		return -EINVAL;
 
 	idx = path->idx[path->depth - 1];
-	err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx,
+	if (nid_has_volume(codec, mix_nid, HDA_INPUT)) {
+		err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx,
 			  HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
-	if (err < 0)
-		return err;
-	err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx,
+		if (err < 0)
+			return err;
+	}
+
+	if (nid_has_mute(codec, mix_nid, HDA_INPUT)) {
+		err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx,
 			  HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
-	if (err < 0)
-		return err;
+		if (err < 0)
+			return err;
+	}
+
 	add_loopback_list(spec, mix_nid, idx);
 	return 0;
 }
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 012/112] ALSA: hda/realtek - Fix initialization of input amps in output paths
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (10 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 011/112] ALSA: hda/realtek - Check amp capabilities of aa-mixer widget Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 013/112] ALSA: hda - Remove snd_hda_codec_amp_update() call from patch_*.c Takashi Iwai
                   ` (102 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

When initializing the output paths, we assumed the input amps have
almost two inputs blindly.  It's not only generic but even incorrect
for some codecs like ALC268 & co.  Also, the same assumption (two
sources) exists for the bind input-amp controls.

This patch changes the codes in these places to handle the input
connections in a more generic way.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 22 +++++++++++++++-------
 1 file changed, 15 insertions(+), 7 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 1abcd8e..981d505 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -3621,9 +3621,10 @@ static int alc_auto_add_sw_ctl(struct hda_codec *codec,
 	val = amp_val_replace_channels(val, chs);
 	if (get_amp_direction_(val) == HDA_INPUT) {
 		hda_nid_t nid = get_amp_nid_(val);
-		if (snd_hda_get_num_conns(codec, nid) > 1) {
+		int nums = snd_hda_get_num_conns(codec, nid);
+		if (nums > 1) {
 			type = ALC_CTL_BIND_MUTE;
-			val |= 2 << 19; /* FIXME: fixed two widgets, so far */
+			val |= nums << 19;
 		}
 	}
 	return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val);
@@ -3909,6 +3910,7 @@ 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)
 {
+	struct alc_spec *spec = codec->spec;
 	int i, val;
 	struct nid_path *path;
 
@@ -3928,13 +3930,19 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec,
 		    (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) &&
 		    (force || !is_out_ctl_present(codec, path, nid,
 						  HDA_INPUT))) {
+			hda_nid_t conn[16];
+			int n, nums;
+			nums = snd_hda_get_connections(codec, nid, conn,
+						       ARRAY_SIZE(conn));
 			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,
+			for (n = 0; n < nums; n++) {
+				if (n != path->idx[i] &&
+				    conn[n] != spec->mixer_nid)
+					continue;
+				snd_hda_codec_write(codec, nid, 0,
 					    AC_VERB_SET_AMP_GAIN_MUTE,
-					    AMP_IN_UNMUTE(1) | val);
+					    AMP_IN_UNMUTE(n) | val);
+			}
 		}
 		if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) &&
 		    (force || !is_out_ctl_present(codec, path, nid,
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 013/112] ALSA: hda - Remove snd_hda_codec_amp_update() call from patch_*.c
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (11 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 012/112] ALSA: hda/realtek - Fix initialization of input amps in output paths Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 014/112] ALSA: hda - Introduce cache & flush cmd / amp writes Takashi Iwai
                   ` (101 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

It's used only in one place in patch_analog.c, and it can be replaced
with others better.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_analog.c | 10 +---------
 1 file changed, 1 insertion(+), 9 deletions(-)

diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 89fc503..308a5b9 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -995,15 +995,7 @@ static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
 				    struct snd_ctl_elem_value *ucontrol)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	long *valp = ucontrol->value.integer.value;
-	int change;
-
-	change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
-					  HDA_AMP_MUTE,
-					  valp[0] ? 0 : HDA_AMP_MUTE);
-	change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
-					   HDA_AMP_MUTE,
-					   valp[1] ? 0 : HDA_AMP_MUTE);
+	int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
 	if (change)
 		ad1986a_update_hp(codec);
 	return change;
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 014/112] ALSA: hda - Introduce cache & flush cmd / amp writes
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (12 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 013/112] ALSA: hda - Remove snd_hda_codec_amp_update() call from patch_*.c Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 015/112] ALSA: hda - Introduce snd_hda_codec_amp_init*() Takashi Iwai
                   ` (100 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

For optimizing the verb executions, a new mechanism to cache the verbs
and amp update commands is introduced.  With the new "write to cache
and flush" way, you can reduce the same verbs that have been written
multiple times.

When codec->cached_write flag is set, the further
snd_hda_codec_write_cache() and snd_hda_codec_amp_stereo() calls will
be performed only on the command or amp cache table, but not sent to
the hardware yet.  Once after you call all commands and update amps,
call snd_hda_codec_resume_amp() and snd_hda_codec_resume_cache().
Then all cached writes and amp updates will be written to the
hardware, and the dirty flags are cleared.

In this implementation, the existing cache table is reused, so
actually no big code change is seen here.  Each cache entry has a new
dirty flag now (so the cache key is now reduced to 31bit).

As a good side-effect by this change, snd_hda_codec_resume_*() will no
longer execute verbs that have been already issued during the resume
phase by checking the dirty flags.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_codec.c | 74 +++++++++++++++++++++++++++++++++++++----------
 sound/pci/hda/hda_codec.h | 12 +++-----
 sound/pci/hda/hda_local.h |  2 --
 3 files changed, 63 insertions(+), 25 deletions(-)

diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index b8fb0a5..3aa20dc 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1610,6 +1610,7 @@ static struct hda_cache_head  *get_alloc_hash(struct hda_cache_rec *cache,
 		cur = snd_array_index(&cache->buf, info);
 		info->key = key;
 		info->val = 0;
+		info->dirty = 0;
 		idx = key % (u16)ARRAY_SIZE(cache->hash);
 		info->next = cache->hash[idx];
 		cache->hash[idx] = cur;
@@ -1873,8 +1874,11 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
 		return 0;
 	}
 	info->vol[ch] = val;
+	if (codec->cached_write)
+		info->head.dirty = 1;
 	mutex_unlock(&codec->hash_mutex);
-	put_vol_mute(codec, info, nid, ch, direction, idx, val);
+	if (!codec->cached_write)
+		put_vol_mute(codec, info, nid, ch, direction, idx, val);
 	return 1;
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update);
@@ -1905,7 +1909,6 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
 
-#ifdef CONFIG_PM
 /**
  * snd_hda_codec_resume_amp - Resume all AMP commands from the cache
  * @codec: HD-audio codec
@@ -1914,13 +1917,17 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
  */
 void snd_hda_codec_resume_amp(struct hda_codec *codec)
 {
-	struct hda_amp_info *buffer = codec->amp_cache.buf.list;
 	int i;
 
-	for (i = 0; i < codec->amp_cache.buf.used; i++, buffer++) {
-		u32 key = buffer->head.key;
+	mutex_lock(&codec->hash_mutex);
+	for (i = 0; i < codec->amp_cache.buf.used; i++) {
+		struct hda_amp_info *buffer;
+		u32 key;
 		hda_nid_t nid;
 		unsigned int idx, dir, ch;
+
+		buffer = snd_array_elem(&codec->amp_cache.buf, i);
+		key = buffer->head.key;
 		if (!key)
 			continue;
 		nid = key & 0xff;
@@ -1929,13 +1936,18 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec)
 		for (ch = 0; ch < 2; ch++) {
 			if (!(buffer->head.val & INFO_AMP_VOL(ch)))
 				continue;
+			if (!buffer->head.dirty)
+				continue;
+			buffer->head.dirty = 0;
+			mutex_unlock(&codec->hash_mutex);
 			put_vol_mute(codec, buffer, nid, ch, dir, idx,
 				     buffer->vol[ch]);
+			mutex_lock(&codec->hash_mutex);
 		}
 	}
+	mutex_unlock(&codec->hash_mutex);
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp);
-#endif /* CONFIG_PM */
 
 static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir,
 			     unsigned int ofs)
@@ -3375,12 +3387,11 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
 }
 EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
 
-#ifdef CONFIG_PM
 /*
  * command cache
  */
 
-/* build a 32bit cache key with the widget id and the command parameter */
+/* build a 31bit cache key with the widget id and the command parameter */
 #define build_cmd_cache_key(nid, verb)	((verb << 8) | nid)
 #define get_cmd_cache_nid(key)		((key) & 0xff)
 #define get_cmd_cache_cmd(key)		(((key) >> 8) & 0xffff)
@@ -3400,12 +3411,16 @@ EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
 int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
 			      int direct, unsigned int verb, unsigned int parm)
 {
-	int err = snd_hda_codec_write(codec, nid, direct, verb, parm);
+	int err;
 	struct hda_cache_head *c;
 	u32 key;
 
-	if (err < 0)
-		return err;
+	if (!codec->cached_write) {
+		err = snd_hda_codec_write(codec, nid, direct, verb, parm);
+		if (err < 0)
+			return err;
+	}
+
 	/* parm may contain the verb stuff for get/set amp */
 	verb = verb | (parm >> 8);
 	parm &= 0xff;
@@ -3414,6 +3429,8 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
 	c = get_alloc_hash(&codec->cmd_cache, key);
 	if (c)
 		c->val = parm;
+	if (codec->cached_write)
+		c->dirty = 1;
 	mutex_unlock(&codec->bus->cmd_mutex);
 	return 0;
 }
@@ -3462,16 +3479,26 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_update_cache);
  */
 void snd_hda_codec_resume_cache(struct hda_codec *codec)
 {
-	struct hda_cache_head *buffer = codec->cmd_cache.buf.list;
 	int i;
 
-	for (i = 0; i < codec->cmd_cache.buf.used; i++, buffer++) {
-		u32 key = buffer->key;
+	mutex_lock(&codec->hash_mutex);
+	for (i = 0; i < codec->cmd_cache.buf.used; i++) {
+		struct hda_cache_head *buffer;
+		u32 key;
+
+		buffer = snd_array_elem(&codec->cmd_cache.buf, i);
+		key = buffer->key;
 		if (!key)
 			continue;
+		if (!buffer->dirty)
+			continue;
+		buffer->dirty = 0;
+		mutex_unlock(&codec->hash_mutex);
 		snd_hda_codec_write(codec, get_cmd_cache_nid(key), 0,
 				    get_cmd_cache_cmd(key), buffer->val);
+		mutex_lock(&codec->hash_mutex);
 	}
+	mutex_unlock(&codec->hash_mutex);
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_resume_cache);
 
@@ -3492,7 +3519,6 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec,
 					  seq->param);
 }
 EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache);
-#endif /* CONFIG_PM */
 
 void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
 				    unsigned int power_state,
@@ -3640,6 +3666,22 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec, bool in_wq)
 	return state;
 }
 
+/* mark all entries of cmd and amp caches dirty */
+static void hda_mark_cmd_cache_dirty(struct hda_codec *codec)
+{
+	int i;
+	for (i = 0; i < codec->cmd_cache.buf.used; i++) {
+		struct hda_cache_head *cmd;
+		cmd = snd_array_elem(&codec->cmd_cache.buf, i);
+		cmd->dirty = 1;
+	}
+	for (i = 0; i < codec->amp_cache.buf.used; i++) {
+		struct hda_amp_info *amp;
+		amp = snd_array_elem(&codec->cmd_cache.buf, i);
+		amp->head.dirty = 1;
+	}
+}
+
 /*
  * kick up codec; used both from PM and power-save
  */
@@ -3647,6 +3689,8 @@ static void hda_call_codec_resume(struct hda_codec *codec)
 {
 	codec->in_pm = 1;
 
+	hda_mark_cmd_cache_dirty(codec);
+
 	/* set as if powered on for avoiding re-entering the resume
 	 * in the resume / power-save sequence
 	 */
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 8665540..cab39b2 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -719,9 +719,10 @@ struct hda_codec_ops {
 
 /* record for amp information cache */
 struct hda_cache_head {
-	u32 key;		/* hash key */
+	u32 key:31;		/* hash key */
+	u32 dirty:1;
 	u16 val;		/* assigned value */
-	u16 next;		/* next link; -1 = terminal */
+	u16 next;
 };
 
 struct hda_amp_info {
@@ -867,6 +868,7 @@ struct hda_codec {
 	unsigned int no_jack_detect:1;	/* Machine has no jack-detection */
 	unsigned int pcm_format_first:1; /* PCM format must be set first */
 	unsigned int epss:1;		/* supporting EPSS? */
+	unsigned int cached_write:1;	/* write only to caches */
 #ifdef CONFIG_PM
 	unsigned int power_on :1;	/* current (global) power-state */
 	unsigned int d3_stop_clk:1;	/* support D3 operation without BCLK */
@@ -952,7 +954,6 @@ void snd_hda_sequence_write(struct hda_codec *codec,
 int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex);
 
 /* cached write */
-#ifdef CONFIG_PM
 int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
 			      int direct, unsigned int verb, unsigned int parm);
 void snd_hda_sequence_write_cache(struct hda_codec *codec,
@@ -960,11 +961,6 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec,
 int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid,
 			      int direct, unsigned int verb, unsigned int parm);
 void snd_hda_codec_resume_cache(struct hda_codec *codec);
-#else
-#define snd_hda_codec_write_cache	snd_hda_codec_write
-#define snd_hda_codec_update_cache	snd_hda_codec_write
-#define snd_hda_sequence_write_cache	snd_hda_sequence_write
-#endif
 
 /* the struct for codec->pin_configs */
 struct hda_pincfg {
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 4b40a5e..f765296 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -133,9 +133,7 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
 			     int direction, int idx, int mask, int val);
 int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
 			     int dir, int idx, int mask, int val);
-#ifdef CONFIG_PM
 void snd_hda_codec_resume_amp(struct hda_codec *codec);
-#endif
 
 void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
 			     unsigned int *tlv);
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 015/112] ALSA: hda - Introduce snd_hda_codec_amp_init*()
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (13 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 014/112] ALSA: hda - Introduce cache & flush cmd / amp writes Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 016/112] ALSA: hda/realtek - Remove non-standard automute mode Takashi Iwai
                   ` (99 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

The new function snd_hda_codec_amp_init() (and the stereo variant)
initializes the amp value only once at the first access.  If the amp
was already initialized or updated, this won't do anything more.

It's useful for initializing the input amps that are in the part of
the path but never used.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_codec.c | 71 ++++++++++++++++++++++++++++++++++-------------
 sound/pci/hda/hda_local.h |  4 +++
 2 files changed, 56 insertions(+), 19 deletions(-)

diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 3aa20dc..7383746 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1765,7 +1765,7 @@ EXPORT_SYMBOL_HDA(snd_hda_override_pin_caps);
  */
 static struct hda_amp_info *
 update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch,
-		int direction, int index)
+		int direction, int index, bool init_only)
 {
 	struct hda_amp_info *info;
 	unsigned int parm, val = 0;
@@ -1791,7 +1791,8 @@ update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch,
 		}
 		info->vol[ch] = val;
 		info->head.val |= INFO_AMP_VOL(ch);
-	}
+	} else if (init_only)
+		return NULL;
 	return info;
 }
 
@@ -1832,7 +1833,7 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
 	unsigned int val = 0;
 
 	mutex_lock(&codec->hash_mutex);
-	info = update_amp_hash(codec, nid, ch, direction, index);
+	info = update_amp_hash(codec, nid, ch, direction, index, false);
 	if (info)
 		val = info->vol[ch];
 	mutex_unlock(&codec->hash_mutex);
@@ -1840,21 +1841,9 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read);
 
-/**
- * snd_hda_codec_amp_update - update the AMP value
- * @codec: HD-audio codec
- * @nid: NID to read the AMP value
- * @ch: channel (left=0 or right=1)
- * @direction: #HDA_INPUT or #HDA_OUTPUT
- * @idx: the index value (only for input direction)
- * @mask: bit mask to set
- * @val: the bits value to set
- *
- * Update the AMP value with a bit mask.
- * Returns 0 if the value is unchanged, 1 if changed.
- */
-int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
-			     int direction, int idx, int mask, int val)
+static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
+			    int direction, int idx, int mask, int val,
+			    bool init_only)
 {
 	struct hda_amp_info *info;
 
@@ -1863,7 +1852,7 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
 	val &= mask;
 
 	mutex_lock(&codec->hash_mutex);
-	info = update_amp_hash(codec, nid, ch, direction, idx);
+	info = update_amp_hash(codec, nid, ch, direction, idx, init_only);
 	if (!info) {
 		mutex_unlock(&codec->hash_mutex);
 		return 0;
@@ -1881,6 +1870,25 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
 		put_vol_mute(codec, info, nid, ch, direction, idx, val);
 	return 1;
 }
+
+/**
+ * snd_hda_codec_amp_update - update the AMP value
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @ch: channel (left=0 or right=1)
+ * @direction: #HDA_INPUT or #HDA_OUTPUT
+ * @idx: the index value (only for input direction)
+ * @mask: bit mask to set
+ * @val: the bits value to set
+ *
+ * Update the AMP value with a bit mask.
+ * Returns 0 if the value is unchanged, 1 if changed.
+ */
+int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
+			     int direction, int idx, int mask, int val)
+{
+	return codec_amp_update(codec, nid, ch, direction, idx, mask, val, false);
+}
 EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update);
 
 /**
@@ -1909,6 +1917,31 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
 
+/* Works like snd_hda_codec_amp_update() but it writes the value only at
+ * the first access.  If the amp was already initialized / updated beforehand,
+ * this does nothing.
+ */
+int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch,
+			   int dir, int idx, int mask, int val)
+{
+	return codec_amp_update(codec, nid, ch, dir, idx, mask, val, true);
+}
+EXPORT_SYMBOL_HDA(snd_hda_codec_amp_init);
+
+int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid,
+				  int dir, int idx, int mask, int val)
+{
+	int ch, ret = 0;
+
+	if (snd_BUG_ON(mask & ~0xff))
+		mask &= 0xff;
+	for (ch = 0; ch < 2; ch++)
+		ret |= snd_hda_codec_amp_init(codec, nid, ch, dir,
+					      idx, mask, val);
+	return ret;
+}
+EXPORT_SYMBOL_HDA(snd_hda_codec_amp_init_stereo);
+
 /**
  * snd_hda_codec_resume_amp - Resume all AMP commands from the cache
  * @codec: HD-audio codec
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index f765296..e38519b 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -133,6 +133,10 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
 			     int direction, int idx, int mask, int val);
 int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
 			     int dir, int idx, int mask, int val);
+int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch,
+			   int direction, int idx, int mask, int val);
+int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid,
+				  int dir, int idx, int mask, int val);
 void snd_hda_codec_resume_amp(struct hda_codec *codec);
 
 void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 016/112] ALSA: hda/realtek - Remove non-standard automute mode
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (14 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 015/112] ALSA: hda - Introduce snd_hda_codec_amp_init*() Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 017/112] ALSA: hda/realtek - Add path active flag Takashi Iwai
                   ` (98 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

We are using only AUTOMUTE_MODE_PIN in patch_realtek.c and all others
have been already dropped.  Let's remove the old superfluous codes.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 46 ++++++++++---------------------------------
 1 file changed, 10 insertions(+), 36 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 981d505..d964c88 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -73,12 +73,6 @@ struct alc_multi_io {
 	unsigned int ctl_in;	/* cached input-pin control value */
 };
 
-enum {
-	ALC_AUTOMUTE_PIN,	/* change the pin control */
-	ALC_AUTOMUTE_AMP,	/* mute/unmute the pin AMP */
-	ALC_AUTOMUTE_MIXER,	/* mute/unmute mixer widget AMP */
-};
-
 #define MAX_VOL_NIDS	0x40
 
 /* make compatible with old code */
@@ -542,7 +536,6 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
 			bool mute, bool hp_out)
 {
 	struct alc_spec *spec = codec->spec;
-	unsigned int mute_bits = mute ? HDA_AMP_MUTE : 0;
 	unsigned int pin_bits = mute ? 0 : (hp_out ? PIN_HP : PIN_OUT);
 	int i;
 
@@ -551,34 +544,17 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
 		unsigned int val;
 		if (!nid)
 			break;
-		switch (spec->automute_mode) {
-		case ALC_AUTOMUTE_PIN:
-			/* don't reset VREF value in case it's controlling
-			 * the amp (see alc861_fixup_asus_amp_vref_0f())
-			 */
-			if (spec->keep_vref_in_automute) {
-				val = snd_hda_codec_read(codec, nid, 0,
+		/* don't reset VREF value in case it's controlling
+		 * the amp (see alc861_fixup_asus_amp_vref_0f())
+		 */
+		if (spec->keep_vref_in_automute) {
+			val = snd_hda_codec_read(codec, nid, 0,
 					AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-				val &= ~PIN_HP;
-			} else
-				val = 0;
-			val |= pin_bits;
-			snd_hda_set_pin_ctl(codec, nid, val);
-			break;
-		case ALC_AUTOMUTE_AMP:
-			snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
-						 HDA_AMP_MUTE, mute_bits);
-			break;
-		case ALC_AUTOMUTE_MIXER:
-			nid = spec->automute_mixer_nid[i];
-			if (!nid)
-				break;
-			snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0,
-						 HDA_AMP_MUTE, mute_bits);
-			snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 1,
-						 HDA_AMP_MUTE, mute_bits);
-			break;
-		}
+			val &= ~PIN_HP;
+		} else
+			val = 0;
+		val |= pin_bits;
+		snd_hda_set_pin_ctl(codec, nid, val);
 	}
 }
 
@@ -979,8 +955,6 @@ static int alc_init_automute(struct hda_codec *codec)
 		cfg->hp_outs = cfg->line_outs;
 	}
 
-	spec->automute_mode = ALC_AUTOMUTE_PIN;
-
 	for (i = 0; i < cfg->hp_outs; i++) {
 		hda_nid_t nid = cfg->hp_pins[i];
 		if (!is_jack_detectable(codec, nid))
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 017/112] ALSA: hda/realtek - Add path active flag
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (15 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 016/112] ALSA: hda/realtek - Remove non-standard automute mode Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 018/112] ALSA: hda/realtek - Consolidate is_reachable_path() Takashi Iwai
                   ` (97 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

... and rewrite the initialization of output paths as a generic
function that is applicable for both i/o directions.

The new flag, active, is introduced to each nid_path entry.  This
indicates whether the given path is active, and it's used for checking
whether a certain widget can be turned off or changed when a path is
no longer used or newly enabled.

It's still used only in the output paths.  More wider adaption for
input and loopback paths will be achieved in the later patch.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_local.h     |   3 +-
 sound/pci/hda/patch_realtek.c | 261 ++++++++++++++++++++++++++++--------------
 2 files changed, 176 insertions(+), 88 deletions(-)

diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index e38519b..ff56da8 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -598,7 +598,8 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
 #define get_amp_channels(kc)	(((kc)->private_value >> 16) & 0x3)
 #define get_amp_direction_(pv)	(((pv) >> 18) & 0x1)
 #define get_amp_direction(kc)	get_amp_direction_((kc)->private_value)
-#define get_amp_index(kc)	(((kc)->private_value >> 19) & 0xf)
+#define get_amp_index_(pv)	(((pv) >> 19) & 0xf)
+#define get_amp_index(kc)	get_amp_index_((kc)->private_value)
 #define get_amp_offset(kc)	(((kc)->private_value >> 23) & 0x3f)
 #define get_amp_min_mute(kc)	(((kc)->private_value >> 29) & 0x1)
 
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index d964c88..84f6781 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -112,6 +112,7 @@ struct nid_path {
 	unsigned char idx[MAX_NID_PATH_DEPTH];
 	unsigned char multi[MAX_NID_PATH_DEPTH];
 	unsigned int ctls[2]; /* 0 = volume, 1 = mute */
+	bool active;
 };
 
 enum { NID_PATH_VOL_CTL = 0, NID_PATH_MUTE_CTL = 1 };
@@ -2853,16 +2854,6 @@ static int alc_auto_create_shared_input(struct hda_codec *codec)
 	return 0;
 }
 
-static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid,
-			       unsigned int pin_type)
-{
-	snd_hda_set_pin_ctl(codec, nid, pin_type);
-	/* unmute pin */
-	if (nid_has_mute(codec, nid, HDA_OUTPUT))
-		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-			    AMP_OUT_UNMUTE);
-}
-
 static int get_pin_type(int line_out_type)
 {
 	if (line_out_type == AUTO_PIN_HP_OUT)
@@ -3824,23 +3815,21 @@ static int alc_auto_create_speaker_out(struct hda_codec *codec)
 					  "Speaker");
 }
 
-/* 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)
+static bool is_ctl_associated_in_list(struct snd_array *array, hda_nid_t nid,
+				      int dir, int idx, int types)
 {
-	struct alc_spec *spec = codec->spec;
 	int i, type;
 
-	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)
+	for (i = 0; i < array->used; i++) {
+		struct nid_path *p = snd_array_elem(array, i);
+		if (p->depth <= 0)
 			continue;
 		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)
+				    get_amp_direction_(val) == dir &&
+				    get_amp_index_(val) == idx)
 					return true;
 			}
 		}
@@ -3848,85 +3837,183 @@ static bool __is_out_ctl_present(struct hda_codec *codec,
 	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)
+/* check whether a control with the given (nid, dir, idx) was assigned */
+static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid,
+			      int dir, int idx)
+{
+	struct alc_spec *spec = codec->spec;
+	return is_ctl_associated_in_list(&spec->out_path, nid, dir, idx, 3) ||
+		is_ctl_associated_in_list(&spec->in_path, nid, dir, idx, 3) ||
+		is_ctl_associated_in_list(&spec->loopback_path, nid, dir, idx, 3);
+}
 
-static int get_default_amp_val(struct hda_codec *codec, hda_nid_t nid, int dir)
+/* can have the amp-in capability? */
+static bool has_amp_in(struct hda_codec *codec, struct nid_path *path, int idx)
 {
-	unsigned int caps, offset;
+	hda_nid_t nid = path->path[idx];
+	unsigned int caps = get_wcaps(codec, nid);
+	unsigned int type = get_wcaps_type(caps);
+
+	if (!(caps & AC_WCAP_IN_AMP))
+		return false;
+	if (type == AC_WID_PIN && idx > 0) /* only for input pins */
+		return false;
+	return true;
+}
+
+/* can have the amp-out capability? */
+static bool has_amp_out(struct hda_codec *codec, struct nid_path *path, int idx)
+{
+	hda_nid_t nid = path->path[idx];
+	unsigned int caps = get_wcaps(codec, nid);
+	unsigned int type = get_wcaps_type(caps);
+
+	if (!(caps & AC_WCAP_OUT_AMP))
+		return false;
+	if (type == AC_WID_PIN && !idx) /* only for output pins */
+		return false;
+	return true;
+}
+
+static bool is_active_in_list(struct hda_codec *codec, struct snd_array *array,
+			      hda_nid_t nid, int dir, int idx)
+{
+	int i, n;
+
+	for (n = 0; n < array->used; n++) {
+		struct nid_path *path = snd_array_elem(array, n);
+		if (!path->active)
+			continue;
+		for (i = 0; i < path->depth; i++) {
+			if (path->path[i] == nid) {
+				if (dir == HDA_OUTPUT || path->idx[i] == idx)
+					return true;
+				break;
+			}
+		}
+	}
+	return false;
+}
+
+/* check whether the given (nid,dir,idx) is active */
+static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid,
+			  unsigned int idx, unsigned int dir)
+{
+	struct alc_spec *spec = codec->spec;
+	return is_active_in_list(codec, &spec->out_path, nid, idx, dir) ||
+		is_active_in_list(codec, &spec->in_path, nid, idx, dir) ||
+		is_active_in_list(codec, &spec->loopback_path, nid, idx, dir);
+}
+
+/* get the default amp value for the target state */
+static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid,
+				   int dir, bool enable)
+{
+	unsigned int caps;
 	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;
+		/* set to 0dB */
+		if (enable)
+			val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
 	}
 	if (caps & AC_AMPCAP_MUTE) {
-		/* if a mute control is assigned, mute as default */
-		if (is_out_mute_ctl_present(codec, nid, dir))
+		if (!enable)
 			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)
+/* initialize the amp value (only at the first time) */
+static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx)
+{
+	int val = get_amp_val_to_activate(codec, nid, dir, false);
+	snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val);
+}
+
+static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir,
+			 int idx, bool enable)
+{
+	int val;
+	if (is_ctl_associated(codec, nid, dir, idx) ||
+	    is_active_nid(codec, nid, dir, idx))
+		return;
+	val = get_amp_val_to_activate(codec, nid, dir, enable);
+	snd_hda_codec_amp_stereo(codec, nid, dir, idx, 0xff, val);
+}
+
+static void activate_amp_out(struct hda_codec *codec, struct nid_path *path,
+			     int i, bool enable)
+{
+	hda_nid_t nid = path->path[i];
+	init_amp(codec, nid, HDA_OUTPUT, 0);
+	activate_amp(codec, nid, HDA_OUTPUT, 0, enable);
+}
+
+static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
+			    int i, bool enable)
 {
 	struct alc_spec *spec = codec->spec;
-	int i, val;
-	struct nid_path *path;
+	hda_nid_t conn[16];
+	int n, nums;
+	hda_nid_t nid = path->path[i];
 
-	alc_set_pin_output(codec, pin, pin_type);
-	path = get_out_path(codec, pin, dac);
-	if (!path)
+	nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
+	for (n = 0; n < nums; n++)
+		init_amp(codec, nid, HDA_INPUT, n);
+
+	if (is_ctl_associated(codec, nid, HDA_INPUT, path->idx[i]))
 		return;
 
+	/* here is a little bit tricky in comparison with activate_amp_out();
+	 * when aa-mixer is available, we need to enable the path as well
+	 */
+	for (n = 0; n < nums; n++) {
+		if (n != path->idx[i] && conn[n] != spec->mixer_nid)
+			continue;
+		activate_amp(codec, nid, HDA_INPUT, n, enable);
+	}
+}
+
+static void activate_path(struct hda_codec *codec, struct nid_path *path,
+			  bool enable)
+{
+	int i;
+
+	if (path->active == enable)
+		return;
+
+	if (!enable)
+		path->active = false;
+
 	for (i = path->depth - 1; i >= 0; i--) {
-		hda_nid_t nid = path->path[i];
 		if (path->multi[i])
-			snd_hda_codec_write(codec, nid, 0,
+			snd_hda_codec_write_cache(codec, path->path[i], 0,
 					    AC_VERB_SET_CONNECT_SEL,
 					    path->idx[i]);
-
-		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))) {
-			hda_nid_t conn[16];
-			int n, nums;
-			nums = snd_hda_get_connections(codec, nid, conn,
-						       ARRAY_SIZE(conn));
-			val = get_default_amp_val(codec, nid, HDA_INPUT);
-			for (n = 0; n < nums; n++) {
-				if (n != path->idx[i] &&
-				    conn[n] != spec->mixer_nid)
-					continue;
-				snd_hda_codec_write(codec, nid, 0,
-					    AC_VERB_SET_AMP_GAIN_MUTE,
-					    AMP_IN_UNMUTE(n) | 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);
-		}
+		if (has_amp_in(codec, path, i))
+			activate_amp_in(codec, path, i, enable);
+		if (has_amp_out(codec, path, i))
+			activate_amp_out(codec, path, i, enable);
 	}
+
+	if (enable)
+		path->active = true;
+}
+
+/* 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)
+{
+	struct nid_path *path;
+
+	snd_hda_set_pin_ctl_cache(codec, pin, pin_type);
+	path = get_out_path(codec, pin, dac);
+	if (!path)
+		return;
+	activate_path(codec, path, true);
 }
 
 static void alc_auto_init_multi_out(struct hda_codec *codec)
@@ -3939,7 +4026,7 @@ 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], true);
+					spec->multiout.dac_nids[i]);
 
 	}
 }
@@ -3963,7 +4050,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, true);
+		alc_auto_set_output_and_unmute(codec, pin, PIN_HP, dac);
 	}
 	for (i = 0; i < spec->autocfg.speaker_outs; i++) {
 		if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
@@ -3978,7 +4065,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, true);
+		alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac);
 	}
 }
 
@@ -4129,22 +4216,22 @@ static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output)
 {
 	struct alc_spec *spec = codec->spec;
 	hda_nid_t nid = spec->multi_io[idx].pin;
+	struct nid_path *path;
+
+	path = get_out_path(codec, nid, spec->multi_io[idx].dac);
+	if (!path)
+		return -EINVAL;
 
 	if (!spec->multi_io[idx].ctl_in)
 		spec->multi_io[idx].ctl_in =
-			snd_hda_codec_read(codec, nid, 0,
+			snd_hda_codec_update_cache(codec, nid, 0,
 					   AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+
 	if (output) {
 		snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT);
-		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_set_output_and_unmute(codec, nid, PIN_OUT,
-					       spec->multi_io[idx].dac, false);
+		activate_path(codec, path, true);
 	} else {
-		if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
-			snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
-						 HDA_AMP_MUTE, HDA_AMP_MUTE);
+		activate_path(codec, path, false);
 		snd_hda_set_pin_ctl_cache(codec, nid,
 					  spec->multi_io[idx].ctl_in);
 	}
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 018/112] ALSA: hda/realtek - Consolidate is_reachable_path()
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (16 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 017/112] ALSA: hda/realtek - Add path active flag Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 019/112] ALSA: hda/realtek - Consolidate to a single path list Takashi Iwai
                   ` (96 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

alc_auto_is_dac_reachable() can be replaced fully with
is_reachable_path().  The only difference is the order of arguments.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 25 +++++++------------------
 1 file changed, 7 insertions(+), 18 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 84f6781..fc5f80b5 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -2902,15 +2902,6 @@ static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid)
 	return false;
 }
 
-/* check whether the DAC is reachable from the pin */
-static bool alc_auto_is_dac_reachable(struct hda_codec *codec,
-				      hda_nid_t pin, hda_nid_t dac)
-{
-	if (!pin || !dac)
-		return false;
-	return snd_hda_get_conn_index(codec, pin, dac, true) >= 0;
-}
-
 /* look for an empty DAC slot */
 static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
 {
@@ -2921,7 +2912,7 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
 		hda_nid_t nid = spec->all_dacs[i];
 		if (!nid || alc_is_dac_already_used(codec, nid))
 			continue;
-		if (alc_auto_is_dac_reachable(codec, pin, nid))
+		if (is_reachable_path(codec, nid, pin))
 			return nid;
 	}
 	return 0;
@@ -3013,7 +3004,7 @@ static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin)
 		hda_nid_t nid = spec->all_dacs[i];
 		if (!nid || alc_is_dac_already_used(codec, nid))
 			continue;
-		if (alc_auto_is_dac_reachable(codec, pin, nid)) {
+		if (is_reachable_path(codec, nid, pin)) {
 			if (nid_found)
 				return 0;
 			nid_found = nid;
@@ -3189,7 +3180,7 @@ static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs,
 			dacs[i] = alc_auto_look_for_dac(codec, pin);
 		if (!dacs[i] && !i) {
 			for (j = 1; j < num_outs; j++) {
-				if (alc_auto_is_dac_reachable(codec, pin, dacs[j])) {
+				if (is_reachable_path(codec, dacs[j], pin)) {
 					dacs[0] = dacs[j];
 					dacs[j] = 0;
 					break;
@@ -3198,11 +3189,10 @@ static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs,
 		}
 		dac = dacs[i];
 		if (!dac) {
-			if (alc_auto_is_dac_reachable(codec, pin, dacs[0]))
+			if (is_reachable_path(codec, dacs[0], pin))
 				dac = dacs[0];
 			else if (cfg->line_outs > i &&
-				 alc_auto_is_dac_reachable(codec, pin,
-					spec->private_dac_nids[i]))
+				 is_reachable_path(codec, spec->private_dac_nids[i], pin))
 				dac = spec->private_dac_nids[i];
 			if (dac) {
 				if (!i)
@@ -3211,8 +3201,7 @@ static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs,
 					badness += bad->shared_surr;
 				else
 					badness += bad->shared_clfe;
-			} else if (alc_auto_is_dac_reachable(codec, pin,
-					spec->private_dac_nids[0])) {
+			} else if (is_reachable_path(codec, spec->private_dac_nids[0], pin)) {
 				dac = spec->private_dac_nids[0];
 				badness += bad->shared_surr_main;
 			} else if (!i)
@@ -4141,7 +4130,7 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec,
 
 			if (offset && offset + spec->multi_ios < dacs) {
 				dac = spec->private_dac_nids[offset + spec->multi_ios];
-				if (!alc_auto_is_dac_reachable(codec, nid, dac))
+				if (!is_reachable_path(codec, dac, nid))
 					dac = 0;
 			}
 			if (hardwired)
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 019/112] ALSA: hda/realtek - Consolidate to a single path list
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (17 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 018/112] ALSA: hda/realtek - Consolidate is_reachable_path() Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 020/112] ALSA: hda/realtek - Use path-based parser for digital outputs Takashi Iwai
                   ` (95 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

We don't have to keep three individual path lists for input, output
and loopback.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 94 +++++++++++++++----------------------------
 1 file changed, 33 insertions(+), 61 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index fc5f80b5..47f5c4f 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -192,14 +192,8 @@ struct alc_spec {
 	int num_all_dacs;
 	hda_nid_t all_dacs[16];
 
-	/* output paths */
-	struct snd_array out_path;
-
-	/* input paths */
-	struct snd_array in_path;
-
-	/* analog loopback paths */
-	struct snd_array loopback_path;
+	/* path list */
+	struct snd_array paths;
 
 	/* hooks */
 	void (*init_hook)(struct hda_codec *codec);
@@ -2412,9 +2406,7 @@ static void alc_free(struct hda_codec *codec)
 
 	alc_free_kctls(codec);
 	alc_free_bind_ctls(codec);
-	snd_array_free(&spec->out_path);
-	snd_array_free(&spec->in_path);
-	snd_array_free(&spec->loopback_path);
+	snd_array_free(&spec->paths);
 	snd_hda_gen_free(&spec->gen);
 	kfree(spec);
 	snd_hda_detach_beep_device(codec);
@@ -2651,7 +2643,7 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin,
 	    !nid_has_mute(codec, mix_nid, HDA_INPUT))
 		return 0; /* no need for analog loopback */
 
-	path = snd_array_new(&spec->loopback_path);
+	path = snd_array_new(&spec->paths);
 	if (!path)
 		return -ENOMEM;
 	memset(path, 0, sizeof(*path));
@@ -2684,7 +2676,7 @@ static int new_capture_source(struct hda_codec *codec, int adc_idx,
 	struct hda_input_mux *imux = &spec->private_imux[0];
 	struct nid_path *path;
 
-	path = snd_array_new(&spec->in_path);
+	path = snd_array_new(&spec->paths);
 	if (!path)
 		return -ENOMEM;
 	memset(path, 0, sizeof(*path));
@@ -2894,8 +2886,8 @@ static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid)
 	struct alc_spec *spec = codec->spec;
 	int i;
 
-	for (i = 0; i < spec->out_path.used; i++) {
-		struct nid_path *path = snd_array_elem(&spec->out_path, i);
+	for (i = 0; i < spec->paths.used; i++) {
+		struct nid_path *path = snd_array_elem(&spec->paths, i);
 		if (path->path[0] == nid)
 			return true;
 	}
@@ -3018,8 +3010,8 @@ static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type)
 	struct alc_spec *spec = codec->spec;
 	int i;
 
-	for (i = 0; i < spec->out_path.used; i++) {
-		struct nid_path *path = snd_array_elem(&spec->out_path, i);
+	for (i = 0; i < spec->paths.used; i++) {
+		struct nid_path *path = snd_array_elem(&spec->paths, i);
 		if (path->ctls[type] == val)
 			return true;
 	}
@@ -3059,14 +3051,14 @@ static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin,
 	struct alc_spec *spec = codec->spec;
 	struct nid_path *path;
 
-	path = snd_array_new(&spec->out_path);
+	path = snd_array_new(&spec->paths);
 	if (!path)
 		return false;
 	memset(path, 0, sizeof(*path));
 	if (parse_nid_path(codec, dac, pin, 0, path))
 		return true;
 	/* push back */
-	spec->out_path.used--;
+	spec->paths.used--;
 	return false;
 }
 
@@ -3079,8 +3071,8 @@ static struct nid_path *get_out_path(struct hda_codec *codec, hda_nid_t pin,
 	struct alc_spec *spec = codec->spec;
 	int i;
 
-	for (i = 0; i < spec->out_path.used; i++) {
-		struct nid_path *path = snd_array_elem(&spec->out_path, i);
+	for (i = 0; i < spec->paths.used; i++) {
+		struct nid_path *path = snd_array_elem(&spec->paths, i);
 		if (path->depth <= 0)
 			continue;
 		if ((!dac || path->path[0] == dac) &&
@@ -3258,7 +3250,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
 	memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid));
 	memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid));
 	spec->multi_ios = 0;
-	snd_array_free(&spec->out_path);
+	snd_array_free(&spec->paths);
 	badness = 0;
 
 	/* fill hard-wired DACs first */
@@ -3804,38 +3796,28 @@ static int alc_auto_create_speaker_out(struct hda_codec *codec)
 					  "Speaker");
 }
 
-static bool is_ctl_associated_in_list(struct snd_array *array, hda_nid_t nid,
-				      int dir, int idx, int types)
+/* check whether a control with the given (nid, dir, idx) was assigned */
+static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid,
+			      int dir, int idx)
 {
+	struct alc_spec *spec = codec->spec;
 	int i, type;
 
-	for (i = 0; i < array->used; i++) {
-		struct nid_path *p = snd_array_elem(array, i);
+	for (i = 0; i < spec->paths.used; i++) {
+		struct nid_path *p = snd_array_elem(&spec->paths, i);
 		if (p->depth <= 0)
 			continue;
 		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 &&
-				    get_amp_index_(val) == idx)
-					return true;
-			}
+			unsigned int val = p->ctls[type];
+			if (get_amp_nid_(val) == nid &&
+			    get_amp_direction_(val) == dir &&
+			    get_amp_index_(val) == idx)
+				return true;
 		}
 	}
 	return false;
 }
 
-/* check whether a control with the given (nid, dir, idx) was assigned */
-static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid,
-			      int dir, int idx)
-{
-	struct alc_spec *spec = codec->spec;
-	return is_ctl_associated_in_list(&spec->out_path, nid, dir, idx, 3) ||
-		is_ctl_associated_in_list(&spec->in_path, nid, dir, idx, 3) ||
-		is_ctl_associated_in_list(&spec->loopback_path, nid, dir, idx, 3);
-}
-
 /* can have the amp-in capability? */
 static bool has_amp_in(struct hda_codec *codec, struct nid_path *path, int idx)
 {
@@ -3864,13 +3846,15 @@ static bool has_amp_out(struct hda_codec *codec, struct nid_path *path, int idx)
 	return true;
 }
 
-static bool is_active_in_list(struct hda_codec *codec, struct snd_array *array,
-			      hda_nid_t nid, int dir, int idx)
+/* check whether the given (nid,dir,idx) is active */
+static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid,
+			  unsigned int idx, unsigned int dir)
 {
+	struct alc_spec *spec = codec->spec;
 	int i, n;
 
-	for (n = 0; n < array->used; n++) {
-		struct nid_path *path = snd_array_elem(array, n);
+	for (n = 0; n < spec->paths.used; n++) {
+		struct nid_path *path = snd_array_elem(&spec->paths, n);
 		if (!path->active)
 			continue;
 		for (i = 0; i < path->depth; i++) {
@@ -3884,16 +3868,6 @@ static bool is_active_in_list(struct hda_codec *codec, struct snd_array *array,
 	return false;
 }
 
-/* check whether the given (nid,dir,idx) is active */
-static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid,
-			  unsigned int idx, unsigned int dir)
-{
-	struct alc_spec *spec = codec->spec;
-	return is_active_in_list(codec, &spec->out_path, nid, idx, dir) ||
-		is_active_in_list(codec, &spec->in_path, nid, idx, dir) ||
-		is_active_in_list(codec, &spec->loopback_path, nid, idx, dir);
-}
-
 /* get the default amp value for the target state */
 static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid,
 				   int dir, bool enable)
@@ -4163,7 +4137,7 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec,
 	}
 	if (!hardwired && spec->multi_ios < 2) {
 		/* cancel newly assigned paths */
-		spec->out_path.used -= spec->multi_ios - old_pins;
+		spec->paths.used -= spec->multi_ios - old_pins;
 		spec->multi_ios = old_pins;
 		return badness;
 	}
@@ -4659,9 +4633,7 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid)
 	snd_hda_gen_init(&spec->gen);
 	snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32);
 	snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8);
-	snd_array_init(&spec->out_path, sizeof(struct nid_path), 8);
-	snd_array_init(&spec->in_path, sizeof(struct nid_path), 8);
-	snd_array_init(&spec->loopback_path, sizeof(struct nid_path), 8);
+	snd_array_init(&spec->paths, sizeof(struct nid_path), 8);
 
 	err = alc_codec_rename_from_preset(codec);
 	if (err < 0) {
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 020/112] ALSA: hda/realtek - Use path-based parser for digital outputs
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (18 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 019/112] ALSA: hda/realtek - Consolidate to a single path list Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 021/112] ALSA: hda/realtek - Rename get_out_path() to get_nid_path() Takashi Iwai
                   ` (94 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

Similar like analog output paths, use the path list for parsing and
initializing digital outputs as well.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 42 ++++++++++++++++++++++--------------------
 1 file changed, 22 insertions(+), 20 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 47f5c4f..206b983 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -1424,6 +1424,14 @@ static unsigned int alc_get_coef0(struct hda_codec *codec)
 	return spec->coef0;
 }
 
+static void alc_auto_set_output_and_unmute(struct hda_codec *codec,
+					   hda_nid_t pin, int pin_type,
+					   hda_nid_t dac);
+static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin,
+				       bool is_digital);
+static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin,
+			     hda_nid_t dac);
+
 /*
  * Digital I/O handling
  */
@@ -1433,22 +1441,13 @@ static void alc_auto_init_digital(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
 	int i;
-	hda_nid_t pin, dac;
+	hda_nid_t pin;
 
 	for (i = 0; i < spec->autocfg.dig_outs; i++) {
 		pin = spec->autocfg.dig_out_pins[i];
 		if (!pin)
 			continue;
-		snd_hda_set_pin_ctl(codec, pin, PIN_OUT);
-		if (!i)
-			dac = spec->multiout.dig_out_nid;
-		else
-			dac = spec->slave_dig_outs[i - 1];
-		if (!dac || !(get_wcaps(codec, dac) & AC_WCAP_OUT_AMP))
-			continue;
-		snd_hda_codec_write(codec, dac, 0,
-				    AC_VERB_SET_AMP_GAIN_MUTE,
-				    AMP_OUT_UNMUTE);
+		alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
 	}
 	pin = spec->autocfg.dig_in_pin;
 	if (pin)
@@ -1465,13 +1464,10 @@ static void alc_auto_parse_digital(struct hda_codec *codec)
 	/* support multiple SPDIFs; the secondary is set up as a slave */
 	nums = 0;
 	for (i = 0; i < spec->autocfg.dig_outs; i++) {
-		hda_nid_t conn[4];
-		err = snd_hda_get_connections(codec,
-					      spec->autocfg.dig_out_pins[i],
-					      conn, ARRAY_SIZE(conn));
-		if (err <= 0)
+		hda_nid_t pin = spec->autocfg.dig_out_pins[i];
+		dig_nid = alc_auto_look_for_dac(codec, pin, true);
+		if (!dig_nid)
 			continue;
-		dig_nid = conn[0]; /* assume the first element is audio-out */
 		if (!nums) {
 			spec->multiout.dig_out_nid = dig_nid;
 			spec->dig_out_type = spec->autocfg.dig_out_type[0];
@@ -1481,6 +1477,7 @@ static void alc_auto_parse_digital(struct hda_codec *codec)
 				break;
 			spec->slave_dig_outs[nums - 1] = dig_nid;
 		}
+		add_new_out_path(codec, pin, dig_nid);
 		nums++;
 	}
 
@@ -2895,15 +2892,20 @@ static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid)
 }
 
 /* look for an empty DAC slot */
-static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
+static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin,
+				       bool is_digital)
 {
 	struct alc_spec *spec = codec->spec;
+	bool cap_digital;
 	int i;
 
 	for (i = 0; i < spec->num_all_dacs; i++) {
 		hda_nid_t nid = spec->all_dacs[i];
 		if (!nid || alc_is_dac_already_used(codec, nid))
 			continue;
+		cap_digital = !!(get_wcaps(codec, nid) & AC_WCAP_DIGITAL);
+		if (is_digital != cap_digital)
+			continue;
 		if (is_reachable_path(codec, nid, pin))
 			return nid;
 	}
@@ -3169,7 +3171,7 @@ static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs,
 	for (i = 0; i < num_outs; i++) {
 		hda_nid_t pin = pins[i];
 		if (!dacs[i])
-			dacs[i] = alc_auto_look_for_dac(codec, pin);
+			dacs[i] = alc_auto_look_for_dac(codec, pin, false);
 		if (!dacs[i] && !i) {
 			for (j = 1; j < num_outs; j++) {
 				if (is_reachable_path(codec, dacs[j], pin)) {
@@ -4110,7 +4112,7 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec,
 			if (hardwired)
 				dac = get_dac_if_single(codec, nid);
 			else if (!dac)
-				dac = alc_auto_look_for_dac(codec, nid);
+				dac = alc_auto_look_for_dac(codec, nid, false);
 			if (!dac) {
 				badness++;
 				continue;
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 021/112] ALSA: hda/realtek - Rename get_out_path() to get_nid_path()
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (19 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 020/112] ALSA: hda/realtek - Use path-based parser for digital outputs Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 022/112] ALSA: hda/realtek - Fix the initialization of pin amp-in Takashi Iwai
                   ` (93 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

The function can be used not only for output paths but generically.
Also swap the argument order.

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

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 206b983..419979d 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -3064,11 +3064,11 @@ static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin,
 	return false;
 }
 
-/* get the path pointing from the given dac to pin;
+/* get the path between the given NIDs;
  * passing 0 to either @pin or @dac behaves as a wildcard
  */
-static struct nid_path *get_out_path(struct hda_codec *codec, hda_nid_t pin,
-				     hda_nid_t dac)
+static struct nid_path *
+get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid)
 {
 	struct alc_spec *spec = codec->spec;
 	int i;
@@ -3077,8 +3077,8 @@ static struct nid_path *get_out_path(struct hda_codec *codec, hda_nid_t pin,
 		struct nid_path *path = snd_array_elem(&spec->paths, i);
 		if (path->depth <= 0)
 			continue;
-		if ((!dac || path->path[0] == dac) &&
-		    (!pin || path->path[path->depth - 1] == pin))
+		if ((!from_nid || path->path[0] == from_nid) &&
+		    (!to_nid || path->path[path->depth - 1] == to_nid))
 			return path;
 	}
 	return NULL;
@@ -3094,7 +3094,7 @@ static struct nid_path *get_out_path(struct hda_codec *codec, hda_nid_t pin,
 static int assign_out_path_ctls(struct hda_codec *codec, hda_nid_t pin,
 				hda_nid_t dac)
 {
-	struct nid_path *path = get_out_path(codec, pin, dac);
+	struct nid_path *path = get_nid_path(codec, dac, pin);
 	hda_nid_t nid;
 	unsigned int val;
 	int badness = 0;
@@ -3495,9 +3495,9 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec)
 	debug_show_configs(spec, cfg);
 
 	if (cfg->line_out_pins[0]) {
-		struct nid_path *path = get_out_path(codec,
-						     cfg->line_out_pins[0],
-						     spec->multiout.dac_nids[0]);
+		struct nid_path *path = get_nid_path(codec,
+						     spec->multiout.dac_nids[0],
+						     cfg->line_out_pins[0]);
 		if (path)
 			spec->vmaster_nid = alc_look_for_out_vol_nid(codec, path);
 	}
@@ -3641,7 +3641,7 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec,
 			name = alc_get_line_out_pfx(spec, i, true, &index);
 		}
 
-		path = get_out_path(codec, pin, dac);
+		path = get_nid_path(codec, dac, pin);
 		if (!path)
 			continue;
 		if (!name || !strcmp(name, "CLFE")) {
@@ -3677,7 +3677,7 @@ static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
 	struct nid_path *path;
 	int err;
 
-	path = get_out_path(codec, pin, dac);
+	path = get_nid_path(codec, dac, pin);
 	if (!path)
 		return 0;
 	/* bind volume control will be created in the case of dac = 0 */
@@ -3763,7 +3763,7 @@ static int alc_auto_create_extra_outs(struct hda_codec *codec, int num_pins,
 		struct nid_path *path;
 		if (!pins[i] || !dacs[i])
 			continue;
-		path = get_out_path(codec, pins[i], dacs[i]);
+		path = get_nid_path(codec, dacs[i], pins[i]);
 		if (!path)
 			continue;
 		vol = alc_look_for_out_vol_nid(codec, path);
@@ -3975,7 +3975,7 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec,
 	struct nid_path *path;
 
 	snd_hda_set_pin_ctl_cache(codec, pin, pin_type);
-	path = get_out_path(codec, pin, dac);
+	path = get_nid_path(codec, dac, pin);
 	if (!path)
 		return;
 	activate_path(codec, path, true);
@@ -4183,7 +4183,7 @@ static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output)
 	hda_nid_t nid = spec->multi_io[idx].pin;
 	struct nid_path *path;
 
-	path = get_out_path(codec, nid, spec->multi_io[idx].dac);
+	path = get_nid_path(codec, spec->multi_io[idx].dac, nid);
 	if (!path)
 		return -EINVAL;
 
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 022/112] ALSA: hda/realtek - Fix the initialization of pin amp-in
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (20 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 021/112] ALSA: hda/realtek - Rename get_out_path() to get_nid_path() Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 023/112] ALSA: hda/realtek - Add missing initialization of multi-io routes Takashi Iwai
                   ` (92 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

The pin widget has only a single amp value for the input even if it
has multiple "sources".  Handle the situation in activate_path().

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 419979d..3f39ba5 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -3921,21 +3921,27 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
 {
 	struct alc_spec *spec = codec->spec;
 	hda_nid_t conn[16];
-	int n, nums;
+	int n, nums, idx;
 	hda_nid_t nid = path->path[i];
 
 	nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
+	if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_PIN) {
+		nums = 1;
+		idx = 0;
+	} else
+		idx = path->idx[i];
+
 	for (n = 0; n < nums; n++)
 		init_amp(codec, nid, HDA_INPUT, n);
 
-	if (is_ctl_associated(codec, nid, HDA_INPUT, path->idx[i]))
+	if (is_ctl_associated(codec, nid, HDA_INPUT, idx))
 		return;
 
 	/* here is a little bit tricky in comparison with activate_amp_out();
 	 * when aa-mixer is available, we need to enable the path as well
 	 */
 	for (n = 0; n < nums; n++) {
-		if (n != path->idx[i] && conn[n] != spec->mixer_nid)
+		if (n != idx && conn[n] != spec->mixer_nid)
 			continue;
 		activate_amp(codec, nid, HDA_INPUT, n, enable);
 	}
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 023/112] ALSA: hda/realtek - Add missing initialization of multi-io routes
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (21 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 022/112] ALSA: hda/realtek - Fix the initialization of pin amp-in Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 024/112] ALSA: hda/realtek - Add boost volumes to path list Takashi Iwai
                   ` (91 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

The paths used for multi-io haven't been initialized properly, so
far.  It's usually no big matter because the pins are set to input as
default, but it's still cleaner to initialize the paths properly.

Now with the path active/inactive check, we can do it easily.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 31 ++++++++++++++++++++++++-------
 1 file changed, 24 insertions(+), 7 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 3f39ba5..398b25b 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -3952,9 +3952,6 @@ static void activate_path(struct hda_codec *codec, struct nid_path *path,
 {
 	int i;
 
-	if (path->active == enable)
-		return;
-
 	if (!enable)
 		path->active = false;
 
@@ -3984,6 +3981,8 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec,
 	path = get_nid_path(codec, dac, pin);
 	if (!path)
 		return;
+	if (path->active)
+		return;
 	activate_path(codec, path, true);
 }
 
@@ -4193,10 +4192,8 @@ static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output)
 	if (!path)
 		return -EINVAL;
 
-	if (!spec->multi_io[idx].ctl_in)
-		spec->multi_io[idx].ctl_in =
-			snd_hda_codec_update_cache(codec, nid, 0,
-					   AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+	if (path->active == output)
+		return 0;
 
 	if (output) {
 		snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT);
@@ -4251,6 +4248,25 @@ static int alc_auto_add_multi_channel_mode(struct hda_codec *codec)
 	return 0;
 }
 
+static void alc_auto_init_multi_io(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int i;
+
+	for (i = 0; i < spec->multi_ios; i++) {
+		hda_nid_t pin = spec->multi_io[i].pin;
+		struct nid_path *path;
+		path = get_nid_path(codec, spec->multi_io[i].dac, pin);
+		if (!path)
+			continue;
+		if (!spec->multi_io[i].ctl_in)
+			spec->multi_io[i].ctl_in =
+				snd_hda_codec_update_cache(codec, pin, 0,
+					   AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+		activate_path(codec, path, path->active);
+	}
+}
+
 /* filter out invalid adc_nids (and capsrc_nids) that don't give all
  * active input pins
  */
@@ -4489,6 +4505,7 @@ static void alc_auto_init_std(struct hda_codec *codec)
 {
 	alc_auto_init_multi_out(codec);
 	alc_auto_init_extra_out(codec);
+	alc_auto_init_multi_io(codec);
 	alc_auto_init_analog_input(codec);
 	alc_auto_init_input_src(codec);
 	alc_auto_init_digital(codec);
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 024/112] ALSA: hda/realtek - Add boost volumes to path list
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (22 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 023/112] ALSA: hda/realtek - Add missing initialization of multi-io routes Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 025/112] ALSA: hda/realtek - Initialize loopback paths properly Takashi Iwai
                   ` (90 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

Don't forget to take boost volumes into account in the managed path
list.  Since it's an additional volume, we need to extend the ctls[]
array.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 23 +++++++++++++++++------
 1 file changed, 17 insertions(+), 6 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 398b25b..eadf326d 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -95,6 +95,13 @@ struct alc_multi_io {
 
 #define MAX_NID_PATH_DEPTH	5
 
+enum {
+	NID_PATH_VOL_CTL,
+	NID_PATH_MUTE_CTL,
+	NID_PATH_BOOST_CTL,
+	NID_PATH_NUM_CTLS
+};
+
 /* Widget connection path
  *
  * For output, stored in the order of DAC -> ... -> pin,
@@ -111,12 +118,10 @@ struct nid_path {
 	hda_nid_t path[MAX_NID_PATH_DEPTH];
 	unsigned char idx[MAX_NID_PATH_DEPTH];
 	unsigned char multi[MAX_NID_PATH_DEPTH];
-	unsigned int ctls[2]; /* 0 = volume, 1 = mute */
+	unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */
 	bool active;
 };
 
-enum { NID_PATH_VOL_CTL = 0, NID_PATH_MUTE_CTL = 1 };
-
 struct alc_spec {
 	struct hda_gen_spec gen;
 
@@ -3809,7 +3814,7 @@ static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid,
 		struct nid_path *p = snd_array_elem(&spec->paths, i);
 		if (p->depth <= 0)
 			continue;
-		for (type = 0; type < 2; type++) {
+		for (type = 0; type < NID_PATH_NUM_CTLS; type++) {
 			unsigned int val = p->ctls[type];
 			if (get_amp_nid_(val) == nid &&
 			    get_amp_direction_(val) == dir &&
@@ -4388,6 +4393,8 @@ static int alc_auto_add_mic_boost(struct hda_codec *codec)
 		if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) {
 			const char *label;
 			char boost_label[32];
+			struct nid_path *path;
+			unsigned int val;
 
 			label = hda_get_autocfg_input_label(codec, cfg, i);
 			if (spec->shared_mic_hp && !strcmp(label, "Misc"))
@@ -4400,11 +4407,15 @@ static int alc_auto_add_mic_boost(struct hda_codec *codec)
 
 			snprintf(boost_label, sizeof(boost_label),
 				 "%s Boost Volume", label);
+			val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT);
 			err = add_control(spec, ALC_CTL_WIDGET_VOL,
-					  boost_label, type_idx,
-				  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
+					  boost_label, type_idx, val);
 			if (err < 0)
 				return err;
+
+			path = get_nid_path(codec, nid, 0);
+			if (path)
+				path->ctls[NID_PATH_BOOST_CTL] = val;
 		}
 	}
 	return 0;
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 025/112] ALSA: hda/realtek - Initialize loopback paths properly
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (23 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 024/112] ALSA: hda/realtek - Add boost volumes to path list Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 026/112] ALSA: hda/realtek - Don't change connection at path deactivation Takashi Iwai
                   ` (89 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

Now we have a complete list of loopback paths, thus we can initialize
the paths more completely based on it, instead of assuming a direct
connection from pin to mixer.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 32 ++++++++++++++++++++------------
 1 file changed, 20 insertions(+), 12 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index eadf326d..a41f048 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -2639,6 +2639,7 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin,
 {
 	struct alc_spec *spec = codec->spec;
 	struct nid_path *path;
+	unsigned int val;
 	int err, idx;
 
 	if (!nid_has_volume(codec, mix_nid, HDA_INPUT) &&
@@ -2654,19 +2655,22 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin,
 
 	idx = path->idx[path->depth - 1];
 	if (nid_has_volume(codec, mix_nid, HDA_INPUT)) {
-		err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx,
-			  HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
+		val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT);
+		err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx, val);
 		if (err < 0)
 			return err;
+		path->ctls[NID_PATH_VOL_CTL] = val;
 	}
 
 	if (nid_has_mute(codec, mix_nid, HDA_INPUT)) {
-		err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx,
-			  HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
+		val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT);
+		err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx, val);
 		if (err < 0)
 			return err;
+		path->ctls[NID_PATH_MUTE_CTL] = val;
 	}
 
+	path->active = true;
 	add_loopback_list(spec, mix_nid, idx);
 	return 0;
 }
@@ -2848,6 +2852,11 @@ static int alc_auto_create_shared_input(struct hda_codec *codec)
 	return 0;
 }
 
+static struct nid_path *
+get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid);
+static void activate_path(struct hda_codec *codec, struct nid_path *path,
+			  bool enable);
+
 static int get_pin_type(int line_out_type)
 {
 	if (line_out_type == AUTO_PIN_HP_OUT)
@@ -2871,15 +2880,14 @@ static void alc_auto_init_analog_input(struct hda_codec *codec)
 						    AC_VERB_SET_AMP_GAIN_MUTE,
 						    AMP_OUT_MUTE);
 		}
-	}
 
-	/* mute all loopback inputs */
-	if (spec->mixer_nid) {
-		int nums = snd_hda_get_num_conns(codec, spec->mixer_nid);
-		for (i = 0; i < nums; i++)
-			snd_hda_codec_write(codec, spec->mixer_nid, 0,
-					    AC_VERB_SET_AMP_GAIN_MUTE,
-					    AMP_IN_MUTE(i));
+		/* mute loopback inputs */
+		if (spec->mixer_nid) {
+			struct nid_path *path;
+			path = get_nid_path(codec, nid, spec->mixer_nid);
+			if (path)
+				activate_path(codec, path, path->active);
+		}
 	}
 }
 
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 026/112] ALSA: hda/realtek - Don't change connection at path deactivation
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (24 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 025/112] ALSA: hda/realtek - Initialize loopback paths properly Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 027/112] ALSA: hda/realtek - Make input path parser more generic Takashi Iwai
                   ` (88 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

The widget connection selection must be changed only when the path is
enabled.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index a41f048..eccb386 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -3969,7 +3969,7 @@ static void activate_path(struct hda_codec *codec, struct nid_path *path,
 		path->active = false;
 
 	for (i = path->depth - 1; i >= 0; i--) {
-		if (path->multi[i])
+		if (enable && path->multi[i])
 			snd_hda_codec_write_cache(codec, path->path[i], 0,
 					    AC_VERB_SET_CONNECT_SEL,
 					    path->idx[i]);
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 027/112] ALSA: hda/realtek - Make input path parser more generic
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (25 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 026/112] ALSA: hda/realtek - Don't change connection at path deactivation Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 028/112] ALSA: hda/realtek - Clean up some spec fields Takashi Iwai
                   ` (87 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

Now we reached to the final big piece of parser rewrite: the input
paths.  While the old parser code assumes the more-or-less direct and
similar connections from input pin to ADC, the new code handles the
complete input paths.  The capture source is switched by simple calls
of activate_path() function.

The parsing of capture volume and capture switches is, however, not
fully generalized.  It assumes that amps are available in the vicinity
of ADCs (in three depth).  This isn't perfect but it should cover all
codecs I know of.

Also, this commit removes some NID mapping of capture-related controls
temporarily for simplicity.  It'll be restored in later commits.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 997 ++++++++++++++++--------------------------
 1 file changed, 374 insertions(+), 623 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index eccb386..111b82c 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -278,6 +278,11 @@ static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid,
 #define nid_has_volume(codec, nid, dir) \
 	check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS)
 
+static struct nid_path *
+get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid);
+static void activate_path(struct hda_codec *codec, struct nid_path *path,
+			  bool enable, bool add_aamix);
+
 /*
  * input MUX handling
  */
@@ -286,12 +291,7 @@ static int alc_mux_enum_info(struct snd_kcontrol *kcontrol,
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct alc_spec *spec = codec->spec;
-	unsigned int mux_idx = snd_ctl_get_ioffidx(kcontrol, &uinfo->id);
-	if (mux_idx >= spec->num_mux_defs)
-		mux_idx = 0;
-	if (!spec->input_mux[mux_idx].num_items && mux_idx > 0)
-		mux_idx = 0;
-	return snd_hda_input_mux_info(&spec->input_mux[mux_idx], uinfo);
+	return snd_hda_input_mux_info(&spec->input_mux[0], uinfo);
 }
 
 static int alc_mux_enum_get(struct snd_kcontrol *kcontrol,
@@ -305,6 +305,14 @@ static int alc_mux_enum_get(struct snd_kcontrol *kcontrol,
 	return 0;
 }
 
+static hda_nid_t get_adc_nid(struct hda_codec *codec, int adc_idx, int imux_idx)
+{
+	struct alc_spec *spec = codec->spec;
+	if (spec->dyn_adc_switch)
+		adc_idx = spec->dyn_adc_idx[imux_idx];
+	return spec->adc_nids[adc_idx];
+}
+
 static bool alc_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
 {
 	struct alc_spec *spec = codec->spec;
@@ -322,14 +330,9 @@ static bool alc_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
 	return false;
 }
 
-static inline hda_nid_t get_capsrc(struct alc_spec *spec, int idx)
-{
-	return spec->capsrc_nids ?
-		spec->capsrc_nids[idx] : spec->adc_nids[idx];
-}
-
 static void call_update_outputs(struct hda_codec *codec);
 static void alc_inv_dmic_sync(struct hda_codec *codec, bool force);
+static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx);
 
 /* for shared I/O, change the pin-control accordingly */
 static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic)
@@ -369,56 +372,39 @@ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx,
 {
 	struct alc_spec *spec = codec->spec;
 	const struct hda_input_mux *imux;
-	unsigned int mux_idx;
-	int i, type, num_conns;
-	hda_nid_t nid;
-
-	if (!spec->input_mux)
-		return 0;
+	struct nid_path *path;
 
-	mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx;
-	imux = &spec->input_mux[mux_idx];
-	if (!imux->num_items && mux_idx > 0)
-		imux = &spec->input_mux[0];
-	if (!imux->num_items)
+	imux = spec->input_mux;
+	if (!imux || !imux->num_items)
 		return 0;
 
 	if (idx >= imux->num_items)
 		idx = imux->num_items - 1;
 	if (spec->cur_mux[adc_idx] == idx && !force)
 		return 0;
+
+	path = get_nid_path(codec, spec->imux_pins[spec->cur_mux[adc_idx]],
+			    spec->adc_nids[adc_idx]);
+	if (!path)
+		return 0;
+	if (path->active)
+		activate_path(codec, path, false, false);
+
 	spec->cur_mux[adc_idx] = idx;
 
 	if (spec->shared_mic_hp)
 		update_shared_mic_hp(codec, spec->cur_mux[adc_idx]);
 
-	if (spec->dyn_adc_switch) {
+	if (spec->dyn_adc_switch)
 		alc_dyn_adc_pcm_resetup(codec, idx);
-		adc_idx = spec->dyn_adc_idx[idx];
-	}
-
-	nid = get_capsrc(spec, adc_idx);
 
-	/* no selection? */
-	num_conns = snd_hda_get_num_conns(codec, nid);
-	if (num_conns <= 1)
-		return 1;
-
-	type = get_wcaps_type(get_wcaps(codec, nid));
-	if (type == AC_WID_AUD_MIX) {
-		/* Matrix-mixer style (e.g. ALC882) */
-		int active = imux->items[idx].index;
-		for (i = 0; i < num_conns; i++) {
-			unsigned int v = (i == active) ? 0 : HDA_AMP_MUTE;
-			snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, i,
-						 HDA_AMP_MUTE, v);
-		}
-	} else {
-		/* MUX style (e.g. ALC880) */
-		snd_hda_codec_write_cache(codec, nid, 0,
-					  AC_VERB_SET_CONNECT_SEL,
-					  imux->items[idx].index);
-	}
+	path = get_nid_path(codec, spec->imux_pins[idx],
+			    get_adc_nid(codec, adc_idx, idx));
+	if (!path)
+		return 0;
+	if (path->active)
+		return 0;
+	activate_path(codec, path, true, false);
 	alc_inv_dmic_sync(codec, true);
 	return 1;
 }
@@ -1006,65 +992,12 @@ static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
 	return -1;
 }
 
-/* check whether dynamic ADC-switching is available */
-static bool alc_check_dyn_adc_switch(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	struct hda_input_mux *imux = &spec->private_imux[0];
-	int i, n, idx;
-	hda_nid_t cap, pin;
-
-	if (imux != spec->input_mux) /* no dynamic imux? */
-		return false;
-
-	for (n = 0; n < spec->num_adc_nids; n++) {
-		cap = spec->private_capsrc_nids[n];
-		for (i = 0; i < imux->num_items; i++) {
-			pin = spec->imux_pins[i];
-			if (!pin)
-				return false;
-			if (get_connection_index(codec, cap, pin) < 0)
-				break;
-		}
-		if (i >= imux->num_items)
-			return true; /* no ADC-switch is needed */
-	}
-
-	for (i = 0; i < imux->num_items; i++) {
-		pin = spec->imux_pins[i];
-		for (n = 0; n < spec->num_adc_nids; n++) {
-			cap = spec->private_capsrc_nids[n];
-			idx = get_connection_index(codec, cap, pin);
-			if (idx >= 0) {
-				imux->items[i].index = idx;
-				spec->dyn_adc_idx[i] = n;
-				break;
-			}
-		}
-	}
-
-	snd_printdd("realtek: enabling ADC switching\n");
-	spec->dyn_adc_switch = 1;
-	return true;
-}
-
 /* check whether all auto-mic pins are valid; setup indices if OK */
 static bool alc_auto_mic_check_imux(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
 	const struct hda_input_mux *imux;
 
-	if (!spec->auto_mic)
-		return false;
-	if (spec->auto_mic_valid_imux)
-		return true; /* already checked */
-
-	/* fill up imux indices */
-	if (!alc_check_dyn_adc_switch(codec)) {
-		spec->auto_mic = 0;
-		return false;
-	}
-
 	imux = spec->input_mux;
 	spec->ext_mic_idx = find_idx_in_nid_list(spec->ext_mic_pin,
 					spec->imux_pins, imux->num_items);
@@ -1072,10 +1005,8 @@ static bool alc_auto_mic_check_imux(struct hda_codec *codec)
 					spec->imux_pins, imux->num_items);
 	spec->dock_mic_idx = find_idx_in_nid_list(spec->dock_mic_pin,
 					spec->imux_pins, imux->num_items);
-	if (spec->ext_mic_idx < 0 || spec->int_mic_idx < 0) {
-		spec->auto_mic = 0;
+	if (spec->ext_mic_idx < 0 || spec->int_mic_idx < 0)
 		return false; /* no corresponding imux */
-	}
 
 	snd_hda_jack_detect_enable_callback(codec, spec->ext_mic_pin,
 					    ALC_MIC_EVENT, alc_mic_automute);
@@ -1085,7 +1016,6 @@ static bool alc_auto_mic_check_imux(struct hda_codec *codec)
 						    alc_mic_automute);
 
 	spec->auto_mic_valid_imux = 1;
-	spec->auto_mic = 1;
 	return true;
 }
 
@@ -1100,9 +1030,6 @@ static int alc_init_auto_mic(struct hda_codec *codec)
 	hda_nid_t fixed, ext, dock;
 	int i;
 
-	if (spec->shared_mic_hp)
-		return 0; /* no auto-mic for the shared I/O */
-
 	spec->ext_mic_idx = spec->int_mic_idx = spec->dock_mic_idx = -1;
 
 	fixed = ext = dock = 0;
@@ -1152,30 +1079,18 @@ static int alc_init_auto_mic(struct hda_codec *codec)
 	spec->int_mic_pin = fixed;
 	spec->dock_mic_pin = dock;
 
-	spec->auto_mic = 1;
 	if (!alc_auto_mic_check_imux(codec))
 		return 0;
 
+	spec->auto_mic = 1;
+	spec->num_adc_nids = 1;
+	spec->cur_mux[0] = spec->int_mic_idx;
 	snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n",
 		    ext, fixed, dock);
 
 	return 0;
 }
 
-/* check the availabilities of auto-mute and auto-mic switches */
-static int alc_auto_check_switches(struct hda_codec *codec)
-{
-	int err;
-
-	err = alc_init_automute(codec);
-	if (err < 0)
-		return err;
-	err = alc_init_auto_mic(codec);
-	if (err < 0)
-		return err;
-	return 0;
-}
-
 /*
  * Realtek SSID verification
  */
@@ -1509,169 +1424,101 @@ static void alc_auto_parse_digital(struct hda_codec *codec)
 /*
  * capture mixer elements
  */
-static int alc_cap_vol_info(struct snd_kcontrol *kcontrol,
-			    struct snd_ctl_elem_info *uinfo)
-{
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct alc_spec *spec = codec->spec;
-	unsigned long val;
-	int err;
-
-	mutex_lock(&codec->control_mutex);
-	if (spec->vol_in_capsrc)
-		val = HDA_COMPOSE_AMP_VAL(spec->capsrc_nids[0], 3, 0, HDA_OUTPUT);
-	else
-		val = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, HDA_INPUT);
-	kcontrol->private_value = val;
-	err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo);
-	mutex_unlock(&codec->control_mutex);
-	return err;
-}
-
-static int alc_cap_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
-			   unsigned int size, unsigned int __user *tlv)
-{
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct alc_spec *spec = codec->spec;
-	unsigned long val;
-	int err;
-
-	mutex_lock(&codec->control_mutex);
-	if (spec->vol_in_capsrc)
-		val = HDA_COMPOSE_AMP_VAL(spec->capsrc_nids[0], 3, 0, HDA_OUTPUT);
-	else
-		val = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, HDA_INPUT);
-	kcontrol->private_value = val;
-	err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv);
-	mutex_unlock(&codec->control_mutex);
-	return err;
-}
+#define alc_cap_vol_info	snd_hda_mixer_amp_volume_info
+#define alc_cap_vol_get		snd_hda_mixer_amp_volume_get
+#define alc_cap_vol_tlv		snd_hda_mixer_amp_tlv
 
-typedef int (*getput_call_t)(struct snd_kcontrol *kcontrol,
-			     struct snd_ctl_elem_value *ucontrol);
+typedef int (*put_call_t)(struct snd_kcontrol *kcontrol,
+			  struct snd_ctl_elem_value *ucontrol);
 
-static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol,
-				 struct snd_ctl_elem_value *ucontrol,
-				 getput_call_t func, bool is_put)
+static int alc_cap_put_caller(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol,
+			      put_call_t func, int type)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct alc_spec *spec = codec->spec;
-	int i, err = 0;
+	const struct hda_input_mux *imux;
+	struct nid_path *path;
+	int i, adc_idx, err = 0;
 
+	imux = spec->input_mux;
+	adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	mutex_lock(&codec->control_mutex);
-	if (is_put && spec->dyn_adc_switch) {
-		for (i = 0; i < spec->num_adc_nids; i++) {
-			kcontrol->private_value =
-				HDA_COMPOSE_AMP_VAL(spec->adc_nids[i],
-						    3, 0, HDA_INPUT);
-			err = func(kcontrol, ucontrol);
-			if (err < 0)
-				goto error;
-		}
-	} else {
-		i = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-		if (spec->vol_in_capsrc)
-			kcontrol->private_value =
-				HDA_COMPOSE_AMP_VAL(spec->capsrc_nids[i],
-						    3, 0, HDA_OUTPUT);
-		else
-			kcontrol->private_value =
-				HDA_COMPOSE_AMP_VAL(spec->adc_nids[i],
-						    3, 0, HDA_INPUT);
+	codec->cached_write = 1;
+	for (i = 0; i < imux->num_items; i++) {
+		path = get_nid_path(codec, spec->imux_pins[i],
+				    get_adc_nid(codec, adc_idx, i));
+		if (!path->ctls[type])
+			continue;
+		kcontrol->private_value = path->ctls[type];
 		err = func(kcontrol, ucontrol);
+		if (err < 0)
+			goto error;
 	}
-	if (err >= 0 && is_put)
-		alc_inv_dmic_sync(codec, false);
  error:
+	codec->cached_write = 0;
 	mutex_unlock(&codec->control_mutex);
+	snd_hda_codec_resume_amp(codec);
+	if (err >= 0 && type == NID_PATH_MUTE_CTL &&
+	    spec->inv_dmic_fixup && spec->inv_dmic_muted)
+		alc_inv_dmic_sync_adc(codec, adc_idx);
 	return err;
 }
 
-static int alc_cap_vol_get(struct snd_kcontrol *kcontrol,
-			   struct snd_ctl_elem_value *ucontrol)
-{
-	return alc_cap_getput_caller(kcontrol, ucontrol,
-				     snd_hda_mixer_amp_volume_get, false);
-}
-
 static int alc_cap_vol_put(struct snd_kcontrol *kcontrol,
 			   struct snd_ctl_elem_value *ucontrol)
 {
-	return alc_cap_getput_caller(kcontrol, ucontrol,
-				     snd_hda_mixer_amp_volume_put, true);
+	return alc_cap_put_caller(kcontrol, ucontrol,
+				  snd_hda_mixer_amp_volume_put,
+				  NID_PATH_VOL_CTL);
 }
 
 /* capture mixer elements */
 #define alc_cap_sw_info		snd_ctl_boolean_stereo_info
+#define alc_cap_sw_get		snd_hda_mixer_amp_switch_get
 
-static int alc_cap_sw_get(struct snd_kcontrol *kcontrol,
+static int alc_cap_sw_put(struct snd_kcontrol *kcontrol,
 			  struct snd_ctl_elem_value *ucontrol)
 {
-	return alc_cap_getput_caller(kcontrol, ucontrol,
-				     snd_hda_mixer_amp_switch_get, false);
+	return alc_cap_put_caller(kcontrol, ucontrol,
+				  snd_hda_mixer_amp_switch_put,
+				  NID_PATH_MUTE_CTL);
 }
 
-static int alc_cap_sw_put(struct snd_kcontrol *kcontrol,
-			  struct snd_ctl_elem_value *ucontrol)
+static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx)
 {
-	return alc_cap_getput_caller(kcontrol, ucontrol,
-				     snd_hda_mixer_amp_switch_put, true);
-}
-
-#define _DEFINE_CAPMIX(num) \
-	{ \
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
-		.name = "Capture Switch", \
-		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
-		.count = num, \
-		.info = alc_cap_sw_info, \
-		.get = alc_cap_sw_get, \
-		.put = alc_cap_sw_put, \
-	}, \
-	{ \
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
-		.name = "Capture Volume", \
-		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | \
-			   SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
-			   SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK), \
-		.count = num, \
-		.info = alc_cap_vol_info, \
-		.get = alc_cap_vol_get, \
-		.put = alc_cap_vol_put, \
-		.tlv = { .c = alc_cap_vol_tlv }, \
-	}
-
-#define _DEFINE_CAPSRC(num) \
-	{ \
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
-		/* .name = "Capture Source", */ \
-		.name = "Input Source", \
-		.count = num, \
-		.info = alc_mux_enum_info, \
-		.get = alc_mux_enum_get, \
-		.put = alc_mux_enum_put, \
-	}
-
-#define DEFINE_CAPMIX(num) \
-static const struct snd_kcontrol_new alc_capture_mixer ## num[] = { \
-	_DEFINE_CAPMIX(num),				      \
-	_DEFINE_CAPSRC(num),				      \
-	{ } /* end */					      \
-}
-
-#define DEFINE_CAPMIX_NOSRC(num) \
-static const struct snd_kcontrol_new alc_capture_mixer_nosrc ## num[] = { \
-	_DEFINE_CAPMIX(num),					    \
-	{ } /* end */						    \
-}
-
-/* up to three ADCs */
-DEFINE_CAPMIX(1);
-DEFINE_CAPMIX(2);
-DEFINE_CAPMIX(3);
-DEFINE_CAPMIX_NOSRC(1);
-DEFINE_CAPMIX_NOSRC(2);
-DEFINE_CAPMIX_NOSRC(3);
+	struct alc_spec *spec = codec->spec;
+	struct hda_input_mux *imux = &spec->private_imux[0];
+	struct nid_path *path;
+	hda_nid_t nid;
+	int i, dir, parm;
+	unsigned int val;
+
+	for (i = 0; i < imux->num_items; i++) {
+		if (spec->imux_pins[i] == spec->inv_dmic_pin)
+			break;
+	}
+	if (i >= imux->num_items)
+		return;
+
+	path = get_nid_path(codec, spec->inv_dmic_pin,
+			    get_adc_nid(codec, adc_idx, i));
+	val = path->ctls[NID_PATH_MUTE_CTL];
+	if (!val)
+		return;
+	nid = get_amp_nid_(val);
+	dir = get_amp_direction_(val);
+	parm = AC_AMP_SET_RIGHT |
+		(dir == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT);
+
+	/* we care only right channel */
+	val = snd_hda_codec_amp_read(codec, nid, 1, dir, 0);
+	if (val & 0x80) /* if already muted, we don't need to touch */
+		return;
+	val |= 0x80;
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+			    parm | val);
+}
 
 /*
  * Inverted digital-mic handling
@@ -1691,40 +1538,22 @@ DEFINE_CAPMIX_NOSRC(3);
 static void alc_inv_dmic_sync(struct hda_codec *codec, bool force)
 {
 	struct alc_spec *spec = codec->spec;
-	int i;
+	int src, nums;
 
 	if (!spec->inv_dmic_fixup)
 		return;
 	if (!spec->inv_dmic_muted && !force)
 		return;
-	for (i = 0; i < spec->num_adc_nids; i++) {
-		int src = spec->dyn_adc_switch ? 0 : i;
+	nums = spec->dyn_adc_switch ? 1 : spec->num_adc_nids;
+	for (src = 0; src < nums; src++) {
 		bool dmic_fixup = false;
-		hda_nid_t nid;
-		int parm, dir, v;
 
 		if (spec->inv_dmic_muted &&
 		    spec->imux_pins[spec->cur_mux[src]] == spec->inv_dmic_pin)
 			dmic_fixup = true;
 		if (!dmic_fixup && !force)
 			continue;
-		if (spec->vol_in_capsrc) {
-			nid = spec->capsrc_nids[i];
-			parm = AC_AMP_SET_RIGHT | AC_AMP_SET_OUTPUT;
-			dir = HDA_OUTPUT;
-		} else {
-			nid = spec->adc_nids[i];
-			parm = AC_AMP_SET_RIGHT | AC_AMP_SET_INPUT;
-			dir = HDA_INPUT;
-		}
-		/* we care only right channel */
-		v = snd_hda_codec_amp_read(codec, nid, 1, dir, 0);
-		if (v & 0x80) /* if already muted, we don't need to touch */
-			continue;
-		if (dmic_fixup) /* add mute for d-mic */
-			v |= 0x80;
-		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-				    parm | v);
+		alc_inv_dmic_sync_adc(codec, src);
 	}
 }
 
@@ -1800,13 +1629,6 @@ static const char * const alc_slave_pfxs[] = {
 
 #define NID_MAPPING		(-1)
 
-#define SUBDEV_SPEAKER_		(0 << 6)
-#define SUBDEV_HP_		(1 << 6)
-#define SUBDEV_LINE_		(2 << 6)
-#define SUBDEV_SPEAKER(x)	(SUBDEV_SPEAKER_ | ((x) & 0x3f))
-#define SUBDEV_HP(x)		(SUBDEV_HP_ | ((x) & 0x3f))
-#define SUBDEV_LINE(x)		(SUBDEV_LINE_ | ((x) & 0x3f))
-
 static void alc_free_kctls(struct hda_codec *codec);
 
 #ifdef CONFIG_SND_HDA_INPUT_BEEP
@@ -1821,11 +1643,7 @@ static const struct snd_kcontrol_new alc_beep_mixer[] = {
 static int __alc_build_controls(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
-	struct snd_kcontrol *kctl = NULL;
-	const struct snd_kcontrol_new *knew;
-	int i, j, err;
-	unsigned int u;
-	hda_nid_t nid;
+	int i, err;
 
 	for (i = 0; i < spec->num_mixers; i++) {
 		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
@@ -1897,75 +1715,6 @@ static int __alc_build_controls(struct hda_codec *codec)
 			return err;
 	}
 
-	/* assign Capture Source enums to NID */
-	if (spec->capsrc_nids || spec->adc_nids) {
-		kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
-		if (!kctl)
-			kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
-		for (i = 0; kctl && i < kctl->count; i++) {
-			err = snd_hda_add_nid(codec, kctl, i,
-					      get_capsrc(spec, i));
-			if (err < 0)
-				return err;
-		}
-	}
-	if (spec->cap_mixer && spec->adc_nids) {
-		const char *kname = kctl ? kctl->id.name : NULL;
-		for (knew = spec->cap_mixer; knew->name; knew++) {
-			if (kname && strcmp(knew->name, kname) == 0)
-				continue;
-			kctl = snd_hda_find_mixer_ctl(codec, knew->name);
-			for (i = 0; kctl && i < kctl->count; i++) {
-				err = snd_hda_add_nid(codec, kctl, i,
-						      spec->adc_nids[i]);
-				if (err < 0)
-					return err;
-			}
-		}
-	}
-
-	/* other nid->control mapping */
-	for (i = 0; i < spec->num_mixers; i++) {
-		for (knew = spec->mixers[i]; knew->name; knew++) {
-			if (knew->iface != NID_MAPPING)
-				continue;
-			kctl = snd_hda_find_mixer_ctl(codec, knew->name);
-			if (kctl == NULL)
-				continue;
-			u = knew->subdevice;
-			for (j = 0; j < 4; j++, u >>= 8) {
-				nid = u & 0x3f;
-				if (nid == 0)
-					continue;
-				switch (u & 0xc0) {
-				case SUBDEV_SPEAKER_:
-					nid = spec->autocfg.speaker_pins[nid];
-					break;
-				case SUBDEV_LINE_:
-					nid = spec->autocfg.line_out_pins[nid];
-					break;
-				case SUBDEV_HP_:
-					nid = spec->autocfg.hp_pins[nid];
-					break;
-				default:
-					continue;
-				}
-				err = snd_hda_add_nid(codec, kctl, 0, nid);
-				if (err < 0)
-					return err;
-			}
-			u = knew->private_value;
-			for (j = 0; j < 4; j++, u >>= 8) {
-				nid = u & 0xff;
-				if (nid == 0)
-					continue;
-				err = snd_hda_add_nid(codec, kctl, 0, nid);
-				if (err < 0)
-					return err;
-			}
-		}
-	}
-
 	alc_free_kctls(codec); /* no longer needed */
 
 	return 0;
@@ -2007,7 +1756,6 @@ static int alc_build_controls(struct hda_codec *codec)
  * Common callbacks
  */
 
-static void alc_init_special_input_src(struct hda_codec *codec);
 static void alc_auto_init_std(struct hda_codec *codec);
 
 static int alc_init(struct hda_codec *codec)
@@ -2021,7 +1769,6 @@ static int alc_init(struct hda_codec *codec)
 	alc_auto_init_amp(codec, spec->init_amp);
 
 	snd_hda_gen_apply_verbs(codec);
-	alc_init_special_input_src(codec);
 	alc_auto_init_std(codec);
 
 	alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT);
@@ -2675,28 +2422,6 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin,
 	return 0;
 }
 
-static int new_capture_source(struct hda_codec *codec, int adc_idx,
-			      hda_nid_t pin, int idx, const char *label)
-{
-	struct alc_spec *spec = codec->spec;
-	struct hda_input_mux *imux = &spec->private_imux[0];
-	struct nid_path *path;
-
-	path = snd_array_new(&spec->paths);
-	if (!path)
-		return -ENOMEM;
-	memset(path, 0, sizeof(*path));
-	if (!parse_nid_path(codec, pin, spec->adc_nids[adc_idx], 2, path)) {
-		snd_printd(KERN_ERR "invalid input path 0x%x -> 0x%x\n",
-			   pin, spec->adc_nids[adc_idx]);
-		return -EINVAL;
-	}
-
-	spec->imux_pins[imux->num_items] = pin;
-	snd_hda_add_imux_item(imux, label, idx, NULL);
-	return 0;
-}
-
 static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid)
 {
 	unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
@@ -2712,54 +2437,217 @@ static bool is_reachable_path(struct hda_codec *codec,
 	return snd_hda_get_conn_index(codec, to_nid, from_nid, true) >= 0;
 }
 
-/* Parse the codec tree and retrieve ADCs and corresponding capsrc MUXs */
-static int alc_auto_fill_adc_caps(struct hda_codec *codec)
+/* Parse the codec tree and retrieve ADCs */
+static int alc_auto_fill_adc_nids(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
 	hda_nid_t nid;
 	hda_nid_t *adc_nids = spec->private_adc_nids;
-	hda_nid_t *cap_nids = spec->private_capsrc_nids;
 	int max_nums = ARRAY_SIZE(spec->private_adc_nids);
 	int i, nums = 0;
 
 	nid = codec->start_nid;
 	for (i = 0; i < codec->num_nodes; i++, nid++) {
-		hda_nid_t src;
 		unsigned int caps = get_wcaps(codec, nid);
 		int type = get_wcaps_type(caps);
 
 		if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL))
 			continue;
 		adc_nids[nums] = nid;
-		cap_nids[nums] = nid;
-		src = nid;
-		for (;;) {
-			int n;
-			type = get_wcaps_type(get_wcaps(codec, src));
-			if (type == AC_WID_PIN)
-				break;
-			if (type == AC_WID_AUD_SEL) {
-				cap_nids[nums] = src;
-				break;
-			}
-			n = snd_hda_get_num_conns(codec, src);
-			if (n > 1) {
-				cap_nids[nums] = src;
-				break;
-			} else if (n != 1)
-				break;
-			if (snd_hda_get_connections(codec, src, &src, 1) != 1)
-				break;
-		}
 		if (++nums >= max_nums)
 			break;
 	}
 	spec->adc_nids = spec->private_adc_nids;
-	spec->capsrc_nids = spec->private_capsrc_nids;
 	spec->num_adc_nids = nums;
 	return nums;
 }
 
+/* filter out invalid adc_nids that don't give all active input pins;
+ * if needed, check whether dynamic ADC-switching is available
+ */
+static int check_dyn_adc_switch(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	struct hda_input_mux *imux = &spec->private_imux[0];
+	hda_nid_t adc_nids[ARRAY_SIZE(spec->private_adc_nids)];
+	int i, n, nums;
+	hda_nid_t pin, adc;
+
+ again:
+	nums = 0;
+	for (n = 0; n < spec->num_adc_nids; n++) {
+		adc = spec->adc_nids[n];
+		for (i = 0; i < imux->num_items; i++) {
+			pin = spec->imux_pins[i];
+			if (!is_reachable_path(codec, pin, adc))
+				break;
+		}
+		if (i >= imux->num_items)
+			adc_nids[nums++] = adc;
+	}
+
+	if (!nums) {
+		if (spec->shared_mic_hp) {
+			spec->shared_mic_hp = 0;
+			spec->private_imux[0].num_items = 1;
+			goto again;
+		}
+
+		/* check whether ADC-switch is possible */
+		for (i = 0; i < imux->num_items; i++) {
+			pin = spec->imux_pins[i];
+			for (n = 0; n < spec->num_adc_nids; n++) {
+				adc = spec->adc_nids[n];
+				if (is_reachable_path(codec, pin, adc)) {
+					spec->dyn_adc_idx[i] = n;
+					break;
+				}
+			}
+		}
+
+		snd_printdd("realtek: enabling ADC switching\n");
+		spec->dyn_adc_switch = 1;
+	} else if (nums != spec->num_adc_nids) {
+		memcpy(spec->private_adc_nids, adc_nids,
+		       nums * sizeof(hda_nid_t));
+		spec->num_adc_nids = nums;
+	}
+
+	if (spec->input_mux->num_items == 1 || spec->shared_mic_hp) {
+		snd_printdd("realtek: reducing to a single ADC\n");
+		spec->num_adc_nids = 1; /* reduce to a single ADC */
+	}
+
+	return 0;
+}
+
+/* templates for capture controls */
+static const struct snd_kcontrol_new cap_src_temp = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Input Source",
+	.info = alc_mux_enum_info,
+	.get = alc_mux_enum_get,
+	.put = alc_mux_enum_put,
+};
+
+static const struct snd_kcontrol_new cap_vol_temp = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Capture Volume",
+	.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+		   SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+		   SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK),
+	.info = alc_cap_vol_info,
+	.get = alc_cap_vol_get,
+	.put = alc_cap_vol_put,
+	.tlv = { .c = alc_cap_vol_tlv },
+};
+
+static const struct snd_kcontrol_new cap_sw_temp = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Capture Switch",
+	.info = alc_cap_sw_info,
+	.get = alc_cap_sw_get,
+	.put = alc_cap_sw_put,
+};
+
+static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path)
+{
+	hda_nid_t nid;
+	int i, depth;
+
+	path->ctls[NID_PATH_VOL_CTL] = path->ctls[NID_PATH_MUTE_CTL] = 0;
+	for (depth = 0; depth < 3; depth++) {
+		if (depth >= path->depth)
+			return -EINVAL;
+		i = path->depth - depth - 1;
+		nid = path->path[i];
+		if (!path->ctls[NID_PATH_VOL_CTL]) {
+			if (nid_has_volume(codec, nid, HDA_OUTPUT))
+				path->ctls[NID_PATH_VOL_CTL] =
+					HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+			else if (nid_has_volume(codec, nid, HDA_INPUT)) {
+				int idx = path->idx[i];
+				if (!depth && codec->single_adc_amp)
+					idx = 0;
+				path->ctls[NID_PATH_VOL_CTL] =
+					HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT);
+			}
+		}
+		if (!path->ctls[NID_PATH_MUTE_CTL]) {
+			if (nid_has_mute(codec, nid, HDA_OUTPUT))
+				path->ctls[NID_PATH_MUTE_CTL] =
+					HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+			else if (nid_has_mute(codec, nid, HDA_INPUT)) {
+				int idx = path->idx[i];
+				if (!depth && codec->single_adc_amp)
+					idx = 0;
+				path->ctls[NID_PATH_MUTE_CTL] =
+					HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT);
+			}
+		}
+	}
+	return 0;
+}
+
+static int create_capture_mixers(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	struct hda_input_mux *imux = &spec->private_imux[0];
+	struct snd_kcontrol_new *knew;
+	int i, n, nums;
+
+	if (spec->dyn_adc_switch)
+		nums = 1;
+	else
+		nums = spec->num_adc_nids;
+
+	if (!spec->auto_mic && imux->num_items > 1) {
+		knew = alc_kcontrol_new(spec, "Input Source", &cap_src_temp);
+		if (!knew)
+			return -ENOMEM;
+		knew->count = nums;
+	}
+
+	for (n = 0; n < nums; n++) {
+		int vol, sw;
+
+		vol = sw = 0;
+		for (i = 0; i < imux->num_items; i++) {
+			struct nid_path *path;
+			path = get_nid_path(codec, spec->imux_pins[i],
+					    get_adc_nid(codec, n, i));
+			if (!path)
+				continue;
+			parse_capvol_in_path(codec, path);
+			if (!vol)
+				vol = path->ctls[NID_PATH_VOL_CTL];
+			if (!sw)
+				sw = path->ctls[NID_PATH_MUTE_CTL];
+		}
+
+		if (vol) {
+			knew = alc_kcontrol_new(spec, "Capture Volume",
+						&cap_vol_temp);
+			if (!knew)
+				return -ENOMEM;
+			knew->index = n;
+			knew->private_value = vol;
+			knew->subdevice = HDA_SUBDEV_AMP_FLAG;
+		}
+		if (sw) {
+			knew = alc_kcontrol_new(spec, "Capture Switch",
+						&cap_sw_temp);
+			if (!knew)
+				return -ENOMEM;
+			knew->index = n;
+			knew->private_value = sw;
+			knew->subdevice = HDA_SUBDEV_AMP_FLAG;
+		}
+	}
+
+	return 0;
+}
+
 /* create playback/capture controls for input pins */
 static int alc_auto_create_input_ctls(struct hda_codec *codec)
 {
@@ -2768,16 +2656,17 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec)
 	hda_nid_t mixer = spec->mixer_nid;
 	struct hda_input_mux *imux = &spec->private_imux[0];
 	int num_adcs;
-	int i, c, err, idx, type_idx = 0;
+	int i, c, err, type_idx = 0;
 	const char *prev_label = NULL;
 
-	num_adcs = alc_auto_fill_adc_caps(codec);
+	num_adcs = alc_auto_fill_adc_nids(codec);
 	if (num_adcs < 0)
 		return 0;
 
 	for (i = 0; i < cfg->num_inputs; i++) {
 		hda_nid_t pin;
 		const char *label;
+		bool imux_added;
 
 		pin = cfg->inputs[i].pin;
 		if (!alc_is_input_pin(codec, pin))
@@ -2801,21 +2690,35 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec)
 			}
 		}
 
+		imux_added = false;
 		for (c = 0; c < num_adcs; c++) {
-			hda_nid_t cap = get_capsrc(spec, c);
-			idx = get_connection_index(codec, cap, pin);
-			if (idx >= 0) {
-				err = new_capture_source(codec, c, pin, idx, label);
-				if (err < 0)
-					return err;
-				break;
+			struct nid_path *path;
+			hda_nid_t adc = spec->adc_nids[c];
+
+			if (!is_reachable_path(codec, pin, adc))
+				continue;
+			path = snd_array_new(&spec->paths);
+			if (!path)
+				return -ENOMEM;
+			memset(path, 0, sizeof(*path));
+			if (!parse_nid_path(codec, pin, adc, 2, path)) {
+				snd_printd(KERN_ERR
+					   "invalid input path 0x%x -> 0x%x\n",
+					   pin, adc);
+				spec->paths.used--;
+				continue;
+			}
+
+			if (!imux_added) {
+				spec->imux_pins[imux->num_items] = pin;
+				snd_hda_add_imux_item(imux, label,
+						      imux->num_items, NULL);
+				imux_added = true;
 			}
 		}
 	}
 
-	spec->num_mux_defs = 1;
 	spec->input_mux = imux;
-
 	return 0;
 }
 
@@ -2852,11 +2755,6 @@ static int alc_auto_create_shared_input(struct hda_codec *codec)
 	return 0;
 }
 
-static struct nid_path *
-get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid);
-static void activate_path(struct hda_codec *codec, struct nid_path *path,
-			  bool enable);
-
 static int get_pin_type(int line_out_type)
 {
 	if (line_out_type == AUTO_PIN_HP_OUT)
@@ -2886,7 +2784,7 @@ static void alc_auto_init_analog_input(struct hda_codec *codec)
 			struct nid_path *path;
 			path = get_nid_path(codec, nid, spec->mixer_nid);
 			if (path)
-				activate_path(codec, path, path->active);
+				activate_path(codec, path, path->active, false);
 		}
 	}
 }
@@ -3930,15 +3828,18 @@ static void activate_amp_out(struct hda_codec *codec, struct nid_path *path,
 }
 
 static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
-			    int i, bool enable)
+			    int i, bool enable, bool add_aamix)
 {
 	struct alc_spec *spec = codec->spec;
 	hda_nid_t conn[16];
 	int n, nums, idx;
+	int type;
 	hda_nid_t nid = path->path[i];
 
 	nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
-	if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_PIN) {
+	type = get_wcaps_type(get_wcaps(codec, nid));
+	if (type == AC_WID_PIN ||
+	    (type == AC_WID_AUD_IN && codec->single_adc_amp)) {
 		nums = 1;
 		idx = 0;
 	} else
@@ -3954,14 +3855,14 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
 	 * when aa-mixer is available, we need to enable the path as well
 	 */
 	for (n = 0; n < nums; n++) {
-		if (n != idx && conn[n] != spec->mixer_nid)
+		if (n != idx && (!add_aamix || conn[n] != spec->mixer_nid))
 			continue;
 		activate_amp(codec, nid, HDA_INPUT, n, enable);
 	}
 }
 
 static void activate_path(struct hda_codec *codec, struct nid_path *path,
-			  bool enable)
+			  bool enable, bool add_aamix)
 {
 	int i;
 
@@ -3974,7 +3875,7 @@ static void activate_path(struct hda_codec *codec, struct nid_path *path,
 					    AC_VERB_SET_CONNECT_SEL,
 					    path->idx[i]);
 		if (has_amp_in(codec, path, i))
-			activate_amp_in(codec, path, i, enable);
+			activate_amp_in(codec, path, i, enable, add_aamix);
 		if (has_amp_out(codec, path, i))
 			activate_amp_out(codec, path, i, enable);
 	}
@@ -3996,7 +3897,7 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec,
 		return;
 	if (path->active)
 		return;
-	activate_path(codec, path, true);
+	activate_path(codec, path, true, true);
 }
 
 static void alc_auto_init_multi_out(struct hda_codec *codec)
@@ -4210,9 +4111,9 @@ static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output)
 
 	if (output) {
 		snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT);
-		activate_path(codec, path, true);
+		activate_path(codec, path, true, true);
 	} else {
-		activate_path(codec, path, false);
+		activate_path(codec, path, false, true);
 		snd_hda_set_pin_ctl_cache(codec, nid,
 					  spec->multi_io[idx].ctl_in);
 	}
@@ -4276,112 +4177,41 @@ static void alc_auto_init_multi_io(struct hda_codec *codec)
 			spec->multi_io[i].ctl_in =
 				snd_hda_codec_update_cache(codec, pin, 0,
 					   AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-		activate_path(codec, path, path->active);
-	}
-}
-
-/* filter out invalid adc_nids (and capsrc_nids) that don't give all
- * active input pins
- */
-static void alc_remove_invalid_adc_nids(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	const struct hda_input_mux *imux;
-	hda_nid_t adc_nids[ARRAY_SIZE(spec->private_adc_nids)];
-	hda_nid_t capsrc_nids[ARRAY_SIZE(spec->private_adc_nids)];
-	int i, n, nums;
-
-	imux = spec->input_mux;
-	if (!imux)
-		return;
-	if (spec->dyn_adc_switch)
-		return;
-
- again:
-	nums = 0;
-	for (n = 0; n < spec->num_adc_nids; n++) {
-		hda_nid_t cap = spec->private_capsrc_nids[n];
-		int num_conns = snd_hda_get_num_conns(codec, cap);
-		for (i = 0; i < imux->num_items; i++) {
-			hda_nid_t pin = spec->imux_pins[i];
-			if (pin) {
-				if (get_connection_index(codec, cap, pin) < 0)
-					break;
-			} else if (num_conns <= imux->items[i].index)
-				break;
-		}
-		if (i >= imux->num_items) {
-			adc_nids[nums] = spec->private_adc_nids[n];
-			capsrc_nids[nums++] = cap;
-		}
+		activate_path(codec, path, path->active, true);
 	}
-	if (!nums) {
-		/* check whether ADC-switch is possible */
-		if (!alc_check_dyn_adc_switch(codec)) {
-			if (spec->shared_mic_hp) {
-				spec->shared_mic_hp = 0;
-				spec->private_imux[0].num_items = 1;
-				goto again;
-			}
-			printk(KERN_WARNING "hda_codec: %s: no valid ADC found;"
-			       " using fallback 0x%x\n",
-			       codec->chip_name, spec->private_adc_nids[0]);
-			spec->num_adc_nids = 1;
-			spec->auto_mic = 0;
-			return;
-		}
-	} else if (nums != spec->num_adc_nids) {
-		memcpy(spec->private_adc_nids, adc_nids,
-		       nums * sizeof(hda_nid_t));
-		memcpy(spec->private_capsrc_nids, capsrc_nids,
-		       nums * sizeof(hda_nid_t));
-		spec->num_adc_nids = nums;
-	}
-
-	if (spec->auto_mic)
-		alc_auto_mic_check_imux(codec); /* check auto-mic setups */
-	else if (spec->input_mux->num_items == 1 || spec->shared_mic_hp)
-		spec->num_adc_nids = 1; /* reduce to a single ADC */
 }
 
 /*
  * initialize ADC paths
  */
-static void alc_auto_init_adc(struct hda_codec *codec, int adc_idx)
-{
-	struct alc_spec *spec = codec->spec;
-	hda_nid_t nid;
-
-	nid = spec->adc_nids[adc_idx];
-	/* mute ADC */
-	if (nid_has_mute(codec, nid, HDA_INPUT)) {
-		snd_hda_codec_write(codec, nid, 0,
-				    AC_VERB_SET_AMP_GAIN_MUTE,
-				    AMP_IN_MUTE(0));
-		return;
-	}
-	if (!spec->capsrc_nids)
-		return;
-	nid = spec->capsrc_nids[adc_idx];
-	if (nid_has_mute(codec, nid, HDA_OUTPUT))
-		snd_hda_codec_write(codec, nid, 0,
-				    AC_VERB_SET_AMP_GAIN_MUTE,
-				    AMP_OUT_MUTE);
-}
-
 static void alc_auto_init_input_src(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
-	int c, nums;
+	struct hda_input_mux *imux = &spec->private_imux[0];
+	struct nid_path *path;
+	int i, c, nums;
 
-	for (c = 0; c < spec->num_adc_nids; c++)
-		alc_auto_init_adc(codec, c);
 	if (spec->dyn_adc_switch)
 		nums = 1;
 	else
 		nums = spec->num_adc_nids;
-	for (c = 0; c < nums; c++)
-		alc_mux_select(codec, c, spec->cur_mux[c], true);
+
+	for (c = 0; c < nums; c++) {
+		for (i = 0; i < imux->num_items; i++) {
+			path = get_nid_path(codec, spec->imux_pins[i],
+					    get_adc_nid(codec, c, i));
+			if (path) {
+				bool active = path->active;
+				if (i == spec->cur_mux[c])
+					active = true;
+				activate_path(codec, path, active, false);
+			}
+		}
+	}
+
+	alc_inv_dmic_sync(codec, true);
+	if (spec->shared_mic_hp)
+		update_shared_mic_hp(codec, spec->cur_mux[0]);
 }
 
 /* add mic boosts if needed */
@@ -4429,94 +4259,6 @@ static int alc_auto_add_mic_boost(struct hda_codec *codec)
 	return 0;
 }
 
-/* select or unmute the given capsrc route */
-static void select_or_unmute_capsrc(struct hda_codec *codec, hda_nid_t cap,
-				    int idx)
-{
-	if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) {
-		snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx,
-					 HDA_AMP_MUTE, 0);
-	} else if (snd_hda_get_num_conns(codec, cap) > 1) {
-		snd_hda_codec_write_cache(codec, cap, 0,
-					  AC_VERB_SET_CONNECT_SEL, idx);
-	}
-}
-
-/* set the default connection to that pin */
-static int init_capsrc_for_pin(struct hda_codec *codec, hda_nid_t pin)
-{
-	struct alc_spec *spec = codec->spec;
-	int i;
-
-	if (!pin)
-		return 0;
-	for (i = 0; i < spec->num_adc_nids; i++) {
-		hda_nid_t cap = get_capsrc(spec, i);
-		int idx;
-
-		idx = get_connection_index(codec, cap, pin);
-		if (idx < 0)
-			continue;
-		select_or_unmute_capsrc(codec, cap, idx);
-		return i; /* return the found index */
-	}
-	return -1; /* not found */
-}
-
-/* initialize some special cases for input sources */
-static void alc_init_special_input_src(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	int i;
-
-	for (i = 0; i < spec->autocfg.num_inputs; i++)
-		init_capsrc_for_pin(codec, spec->autocfg.inputs[i].pin);
-}
-
-/* assign appropriate capture mixers */
-static void set_capture_mixer(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	static const struct snd_kcontrol_new *caps[2][3] = {
-		{ alc_capture_mixer_nosrc1,
-		  alc_capture_mixer_nosrc2,
-		  alc_capture_mixer_nosrc3 },
-		{ alc_capture_mixer1,
-		  alc_capture_mixer2,
-		  alc_capture_mixer3 },
-	};
-
-	/* check whether either of ADC or MUX has a volume control */
-	if (!nid_has_volume(codec, spec->adc_nids[0], HDA_INPUT)) {
-		if (!spec->capsrc_nids)
-			return; /* no volume */
-		if (!nid_has_volume(codec, spec->capsrc_nids[0], HDA_OUTPUT))
-			return; /* no volume in capsrc, too */
-		spec->vol_in_capsrc = 1;
-	}
-
-	if (spec->num_adc_nids > 0) {
-		int mux = 0;
-		int num_adcs = 0;
-
-		if (spec->input_mux && spec->input_mux->num_items > 1)
-			mux = 1;
-		if (spec->auto_mic) {
-			num_adcs = 1;
-			mux = 0;
-		} else if (spec->dyn_adc_switch)
-			num_adcs = 1;
-		if (!num_adcs) {
-			if (spec->num_adc_nids > 3)
-				spec->num_adc_nids = 3;
-			else if (!spec->num_adc_nids)
-				return;
-			num_adcs = spec->num_adc_nids;
-		}
-		spec->cap_mixer = caps[mux][num_adcs - 1];
-	}
-}
-
 /*
  * standard auto-parser initializations
  */
@@ -4639,16 +4381,28 @@ static int alc_parse_auto_config(struct hda_codec *codec,
  dig_only:
 	alc_auto_parse_digital(codec);
 
-	if (!spec->no_analog)
-		alc_remove_invalid_adc_nids(codec);
-
 	if (ssid_nids)
 		alc_ssid_check(codec, ssid_nids);
 
 	if (!spec->no_analog) {
-		err = alc_auto_check_switches(codec);
+		err = alc_init_automute(codec);
 		if (err < 0)
 			return err;
+
+		err = check_dyn_adc_switch(codec);
+		if (err < 0)
+			return err;
+
+		if (!spec->shared_mic_hp) {
+			err = alc_init_auto_mic(codec);
+			if (err < 0)
+			return err;
+		}
+
+		err = create_capture_mixers(codec);
+		if (err < 0)
+			return err;
+
 		err = alc_auto_add_mic_boost(codec);
 		if (err < 0)
 			return err;
@@ -4657,9 +4411,6 @@ static int alc_parse_auto_config(struct hda_codec *codec,
 	if (spec->kctls.list)
 		add_mixer(spec, spec->kctls.list);
 
-	if (!spec->no_analog && !spec->cap_mixer)
-		set_capture_mixer(codec);
-
 	return 1;
 }
 
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 028/112] ALSA: hda/realtek - Clean up some spec fields
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (26 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 027/112] ALSA: hda/realtek - Make input path parser more generic Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 029/112] ALSA: hda/realtek - Remove superfluous input amp init Takashi Iwai
                   ` (86 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

Remove some fields from struct alc_spec, and clean up the usage.
Namely,
- spec->input_mux becomes a single element, private_imux[] is removed
- spec->adc_nids becomes an array by itself, and private_adc_nids[]
  gets removed, too

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 56 ++++++++++++++++---------------------------
 1 file changed, 20 insertions(+), 36 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 111b82c..77045e9 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -128,7 +128,6 @@ struct alc_spec {
 	/* codec parameterization */
 	const struct snd_kcontrol_new *mixers[5];	/* mixer arrays */
 	unsigned int num_mixers;
-	const struct snd_kcontrol_new *cap_mixer;	/* capture mixer */
 	unsigned int beep_amp;	/* beep amp value, set via set_beep_amp() */
 
 	char stream_name_analog[32];	/* analog PCM stream */
@@ -152,8 +151,7 @@ struct alc_spec {
 
 	/* capture */
 	unsigned int num_adc_nids;
-	const hda_nid_t *adc_nids;
-	const hda_nid_t *capsrc_nids;
+	hda_nid_t adc_nids[AUTO_CFG_MAX_OUTS];
 	hda_nid_t dig_in_nid;		/* digital-in NID; optional */
 	hda_nid_t mixer_nid;		/* analog-mixer NID */
 
@@ -164,7 +162,7 @@ struct alc_spec {
 
 	/* capture source */
 	unsigned int num_mux_defs;
-	const struct hda_input_mux *input_mux;
+	struct hda_input_mux input_mux;
 	unsigned int cur_mux[3];
 	hda_nid_t ext_mic_pin;
 	hda_nid_t dock_mic_pin;
@@ -184,10 +182,7 @@ struct alc_spec {
 	struct auto_pin_cfg autocfg;
 	struct alc_customize_define cdefine;
 	struct snd_array kctls;
-	struct hda_input_mux private_imux[3];
 	hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
-	hda_nid_t private_adc_nids[AUTO_CFG_MAX_OUTS];
-	hda_nid_t private_capsrc_nids[AUTO_CFG_MAX_OUTS];
 	hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS];
 	unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS];
 	int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */
@@ -291,7 +286,7 @@ static int alc_mux_enum_info(struct snd_kcontrol *kcontrol,
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct alc_spec *spec = codec->spec;
-	return snd_hda_input_mux_info(&spec->input_mux[0], uinfo);
+	return snd_hda_input_mux_info(&spec->input_mux, uinfo);
 }
 
 static int alc_mux_enum_get(struct snd_kcontrol *kcontrol,
@@ -374,8 +369,8 @@ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx,
 	const struct hda_input_mux *imux;
 	struct nid_path *path;
 
-	imux = spec->input_mux;
-	if (!imux || !imux->num_items)
+	imux = &spec->input_mux;
+	if (!imux->num_items)
 		return 0;
 
 	if (idx >= imux->num_items)
@@ -632,8 +627,6 @@ static void alc_mic_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
 
 	if (!spec->auto_mic || !spec->auto_mic_valid_imux)
 		return;
-	if (snd_BUG_ON(!spec->adc_nids))
-		return;
 	if (snd_BUG_ON(spec->int_mic_idx < 0 || spec->ext_mic_idx < 0))
 		return;
 
@@ -998,7 +991,7 @@ static bool alc_auto_mic_check_imux(struct hda_codec *codec)
 	struct alc_spec *spec = codec->spec;
 	const struct hda_input_mux *imux;
 
-	imux = spec->input_mux;
+	imux = &spec->input_mux;
 	spec->ext_mic_idx = find_idx_in_nid_list(spec->ext_mic_pin,
 					spec->imux_pins, imux->num_items);
 	spec->int_mic_idx = find_idx_in_nid_list(spec->int_mic_pin,
@@ -1441,7 +1434,7 @@ static int alc_cap_put_caller(struct snd_kcontrol *kcontrol,
 	struct nid_path *path;
 	int i, adc_idx, err = 0;
 
-	imux = spec->input_mux;
+	imux = &spec->input_mux;
 	adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	mutex_lock(&codec->control_mutex);
 	codec->cached_write = 1;
@@ -1488,7 +1481,7 @@ static int alc_cap_sw_put(struct snd_kcontrol *kcontrol,
 static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx)
 {
 	struct alc_spec *spec = codec->spec;
-	struct hda_input_mux *imux = &spec->private_imux[0];
+	struct hda_input_mux *imux = &spec->input_mux;
 	struct nid_path *path;
 	hda_nid_t nid;
 	int i, dir, parm;
@@ -1650,11 +1643,6 @@ static int __alc_build_controls(struct hda_codec *codec)
 		if (err < 0)
 			return err;
 	}
-	if (spec->cap_mixer) {
-		err = snd_hda_add_new_ctls(codec, spec->cap_mixer);
-		if (err < 0)
-			return err;
-	}
 	if (spec->multiout.dig_out_nid) {
 		err = snd_hda_create_dig_out_ctls(codec,
 						  spec->multiout.dig_out_nid,
@@ -2014,7 +2002,7 @@ static int alc_build_pcms(struct hda_codec *codec)
 			info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap =
 				snd_pcm_2_1_chmaps;
 	}
-	if (spec->adc_nids) {
+	if (spec->num_adc_nids) {
 		p = spec->stream_analog_capture;
 		if (!p) {
 			if (spec->dyn_adc_switch)
@@ -2074,8 +2062,7 @@ static int alc_build_pcms(struct hda_codec *codec)
 	 * model, configure a second analog capture-only PCM.
 	 */
 	have_multi_adcs = (spec->num_adc_nids > 1) &&
-		!spec->dyn_adc_switch && !spec->auto_mic &&
-		(!spec->input_mux || spec->input_mux->num_items > 1);
+		!spec->dyn_adc_switch && !spec->auto_mic;
 	/* Additional Analaog capture for index #2 */
 	if (spec->alt_dac_nid || have_multi_adcs) {
 		codec->num_pcms = 3;
@@ -2442,8 +2429,8 @@ static int alc_auto_fill_adc_nids(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
 	hda_nid_t nid;
-	hda_nid_t *adc_nids = spec->private_adc_nids;
-	int max_nums = ARRAY_SIZE(spec->private_adc_nids);
+	hda_nid_t *adc_nids = spec->adc_nids;
+	int max_nums = ARRAY_SIZE(spec->adc_nids);
 	int i, nums = 0;
 
 	nid = codec->start_nid;
@@ -2457,7 +2444,6 @@ static int alc_auto_fill_adc_nids(struct hda_codec *codec)
 		if (++nums >= max_nums)
 			break;
 	}
-	spec->adc_nids = spec->private_adc_nids;
 	spec->num_adc_nids = nums;
 	return nums;
 }
@@ -2468,8 +2454,8 @@ static int alc_auto_fill_adc_nids(struct hda_codec *codec)
 static int check_dyn_adc_switch(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
-	struct hda_input_mux *imux = &spec->private_imux[0];
-	hda_nid_t adc_nids[ARRAY_SIZE(spec->private_adc_nids)];
+	struct hda_input_mux *imux = &spec->input_mux;
+	hda_nid_t adc_nids[ARRAY_SIZE(spec->adc_nids)];
 	int i, n, nums;
 	hda_nid_t pin, adc;
 
@@ -2489,7 +2475,7 @@ static int check_dyn_adc_switch(struct hda_codec *codec)
 	if (!nums) {
 		if (spec->shared_mic_hp) {
 			spec->shared_mic_hp = 0;
-			spec->private_imux[0].num_items = 1;
+			imux->num_items = 1;
 			goto again;
 		}
 
@@ -2508,12 +2494,11 @@ static int check_dyn_adc_switch(struct hda_codec *codec)
 		snd_printdd("realtek: enabling ADC switching\n");
 		spec->dyn_adc_switch = 1;
 	} else if (nums != spec->num_adc_nids) {
-		memcpy(spec->private_adc_nids, adc_nids,
-		       nums * sizeof(hda_nid_t));
+		memcpy(spec->adc_nids, adc_nids, nums * sizeof(hda_nid_t));
 		spec->num_adc_nids = nums;
 	}
 
-	if (spec->input_mux->num_items == 1 || spec->shared_mic_hp) {
+	if (imux->num_items == 1 || spec->shared_mic_hp) {
 		snd_printdd("realtek: reducing to a single ADC\n");
 		spec->num_adc_nids = 1; /* reduce to a single ADC */
 	}
@@ -2592,7 +2577,7 @@ static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path)
 static int create_capture_mixers(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
-	struct hda_input_mux *imux = &spec->private_imux[0];
+	struct hda_input_mux *imux = &spec->input_mux;
 	struct snd_kcontrol_new *knew;
 	int i, n, nums;
 
@@ -2654,7 +2639,7 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec)
 	struct alc_spec *spec = codec->spec;
 	const struct auto_pin_cfg *cfg = &spec->autocfg;
 	hda_nid_t mixer = spec->mixer_nid;
-	struct hda_input_mux *imux = &spec->private_imux[0];
+	struct hda_input_mux *imux = &spec->input_mux;
 	int num_adcs;
 	int i, c, err, type_idx = 0;
 	const char *prev_label = NULL;
@@ -2718,7 +2703,6 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec)
 		}
 	}
 
-	spec->input_mux = imux;
 	return 0;
 }
 
@@ -4187,7 +4171,7 @@ static void alc_auto_init_multi_io(struct hda_codec *codec)
 static void alc_auto_init_input_src(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
-	struct hda_input_mux *imux = &spec->private_imux[0];
+	struct hda_input_mux *imux = &spec->input_mux;
 	struct nid_path *path;
 	int i, c, nums;
 
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 029/112] ALSA: hda/realtek - Remove superfluous input amp init
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (27 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 028/112] ALSA: hda/realtek - Clean up some spec fields Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 030/112] ALSA: hda/realtek - Rename add_new_out_path() with add_new_nid_path() Takashi Iwai
                   ` (85 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

The amps will be initialized via activate_path(), thus it's
superfluous to set in alc_auto_init_analog_input().

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 77045e9..b1222cc 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -2755,13 +2755,8 @@ static void alc_auto_init_analog_input(struct hda_codec *codec)
 
 	for (i = 0; i < cfg->num_inputs; i++) {
 		hda_nid_t nid = cfg->inputs[i].pin;
-		if (alc_is_input_pin(codec, nid)) {
+		if (alc_is_input_pin(codec, nid))
 			alc_set_input_pin(codec, nid, cfg->inputs[i].type);
-			if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
-				snd_hda_codec_write(codec, nid, 0,
-						    AC_VERB_SET_AMP_GAIN_MUTE,
-						    AMP_OUT_MUTE);
-		}
 
 		/* mute loopback inputs */
 		if (spec->mixer_nid) {
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 030/112] ALSA: hda/realtek - Rename add_new_out_path() with add_new_nid_path()
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (28 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 029/112] ALSA: hda/realtek - Remove superfluous input amp init Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 031/112] ALSA: hda/realtek - Parse digital input path Takashi Iwai
                   ` (84 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

Make the function more generic for both input and output directions,
and returns the assigned path pointer.  The argument order is changed
to follow the standard (from, to) way.

Now this new function is used for analog input and loopback path
parser codes, too.

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

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index b1222cc..a8febe4 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -1342,8 +1342,12 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec,
 					   hda_nid_t dac);
 static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin,
 				       bool is_digital);
-static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin,
-			     hda_nid_t dac);
+static bool parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
+			   hda_nid_t to_nid, int with_aa_mix,
+			   struct nid_path *path);
+static struct nid_path *add_new_nid_path(struct hda_codec *codec,
+					 hda_nid_t from_nid, hda_nid_t to_nid,
+					 int with_aa_mix);
 
 /*
  * Digital I/O handling
@@ -1371,7 +1375,7 @@ static void alc_auto_init_digital(struct hda_codec *codec)
 static void alc_auto_parse_digital(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
-	int i, err, nums;
+	int i, nums;
 	hda_nid_t dig_nid;
 
 	/* support multiple SPDIFs; the secondary is set up as a slave */
@@ -1381,6 +1385,8 @@ static void alc_auto_parse_digital(struct hda_codec *codec)
 		dig_nid = alc_auto_look_for_dac(codec, pin, true);
 		if (!dig_nid)
 			continue;
+		if (!add_new_nid_path(codec, dig_nid, pin, 2))
+			continue;
 		if (!nums) {
 			spec->multiout.dig_out_nid = dig_nid;
 			spec->dig_out_type = spec->autocfg.dig_out_type[0];
@@ -1390,7 +1396,6 @@ static void alc_auto_parse_digital(struct hda_codec *codec)
 				break;
 			spec->slave_dig_outs[nums - 1] = dig_nid;
 		}
-		add_new_out_path(codec, pin, dig_nid);
 		nums++;
 	}
 
@@ -1404,8 +1409,6 @@ static void alc_auto_parse_digital(struct hda_codec *codec)
 				continue;
 			if (!(wcaps & AC_WCAP_CONN_LIST))
 				continue;
-			err = get_connection_index(codec, dig_nid,
-						   spec->autocfg.dig_in_pin);
 			if (err >= 0) {
 				spec->dig_in_nid = dig_nid;
 				break;
@@ -2343,10 +2346,6 @@ static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch,
 	return channel_name[ch];
 }
 
-static bool parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
-			   hda_nid_t to_nid, int with_aa_mix,
-			   struct nid_path *path);
-
 #ifdef CONFIG_PM
 /* add the powersave loopback-list entry */
 static void add_loopback_list(struct alc_spec *spec, hda_nid_t mix, int idx)
@@ -2380,11 +2379,8 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin,
 	    !nid_has_mute(codec, mix_nid, HDA_INPUT))
 		return 0; /* no need for analog loopback */
 
-	path = snd_array_new(&spec->paths);
+	path = add_new_nid_path(codec, pin, mix_nid, 2);
 	if (!path)
-		return -ENOMEM;
-	memset(path, 0, sizeof(*path));
-	if (!parse_nid_path(codec, pin, mix_nid, 2, path))
 		return -EINVAL;
 
 	idx = path->idx[path->depth - 1];
@@ -2937,21 +2933,25 @@ static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec,
 static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec,
 					  struct nid_path *path);
 
-static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin,
-			     hda_nid_t dac)
+static struct nid_path *add_new_nid_path(struct hda_codec *codec,
+					 hda_nid_t from_nid, hda_nid_t to_nid,
+					 int with_aa_mix)
 {
 	struct alc_spec *spec = codec->spec;
 	struct nid_path *path;
 
+	if (from_nid && to_nid && !is_reachable_path(codec, from_nid, to_nid))
+		return NULL;
+
 	path = snd_array_new(&spec->paths);
 	if (!path)
-		return false;
+		return NULL;
 	memset(path, 0, sizeof(*path));
-	if (parse_nid_path(codec, dac, pin, 0, path))
-		return true;
+	if (parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path))
+		return path;
 	/* push back */
 	spec->paths.used--;
-	return false;
+	return NULL;
 }
 
 /* get the path between the given NIDs;
@@ -3093,7 +3093,7 @@ static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs,
 			else
 				badness += bad->no_dac;
 		}
-		if (!add_new_out_path(codec, pin, dac))
+		if (!add_new_nid_path(codec, dac, pin, 0))
 			dac = dacs[i] = 0;
 		if (dac)
 			badness += assign_out_path_ctls(codec, pin, dac);
@@ -3118,7 +3118,7 @@ static bool alc_map_singles(struct hda_codec *codec, int outs,
 		dac = get_dac_if_single(codec, pins[i]);
 		if (!dac)
 			continue;
-		if (add_new_out_path(codec, pins[i], dac)) {
+		if (add_new_nid_path(codec, dac, pins[i], 0)) {
 			dacs[i] = dac;
 			found = true;
 		}
@@ -4015,7 +4015,7 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec,
 				badness++;
 				continue;
 			}
-			if (!add_new_out_path(codec, nid, dac)) {
+			if (!add_new_nid_path(codec, dac, nid, 0)) {
 				badness++;
 				continue;
 			}
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 031/112] ALSA: hda/realtek - Parse digital input path
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (29 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 030/112] ALSA: hda/realtek - Rename add_new_out_path() with add_new_nid_path() Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 032/112] ALSA: hda/realtek - Allow different pins for shared hp/mic vref check Takashi Iwai
                   ` (83 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

This was the last forgotten path.  Now it's parsed via the same path
parser.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index a8febe4..60cc1b5 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -1402,14 +1402,16 @@ static void alc_auto_parse_digital(struct hda_codec *codec)
 	if (spec->autocfg.dig_in_pin) {
 		dig_nid = codec->start_nid;
 		for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
+			struct nid_path *path;
 			unsigned int wcaps = get_wcaps(codec, dig_nid);
 			if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
 				continue;
 			if (!(wcaps & AC_WCAP_DIGITAL))
 				continue;
-			if (!(wcaps & AC_WCAP_CONN_LIST))
-				continue;
-			if (err >= 0) {
+			path = add_new_nid_path(codec, spec->autocfg.dig_in_pin,
+						dig_nid, 2);
+			if (path) {
+				path->active = true;
 				spec->dig_in_nid = dig_nid;
 				break;
 			}
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 032/112] ALSA: hda/realtek - Allow different pins for shared hp/mic vref check
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (30 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 031/112] ALSA: hda/realtek - Parse digital input path Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 033/112] ALSA: hda/realtek - Drop auto_mic_valid_imux flag Takashi Iwai
                   ` (82 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

Add a new field to indicate the possible pin NID for alternative vref
setup for the shared hp/mic.  Although 0x18 is valid for all Realtek
codecs, it'll be different on other vendor's codecs.

Also, drop the sanity check in update_shared_mic_hp() since the
reference pin is set explicitly in the caller side.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 17 ++++++++---------
 1 file changed, 8 insertions(+), 9 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 60cc1b5..5c5197b 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -187,6 +187,7 @@ struct alc_spec {
 	unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS];
 	int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */
 	hda_nid_t inv_dmic_pin;
+	hda_nid_t shared_mic_vref_pin;
 
 	/* DAC list */
 	int num_all_dacs;
@@ -343,15 +344,11 @@ static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic)
 
 	/* This pin does not have vref caps - let's enable vref on pin 0x18
 	   instead, as suggested by Realtek */
-	if (val == AC_PINCTL_VREF_HIZ) {
-		const hda_nid_t vref_pin = 0x18;
-		/* Sanity check pin 0x18 */
-		if (get_wcaps_type(get_wcaps(codec, vref_pin)) == AC_WID_PIN &&
-		    get_defcfg_connect(snd_hda_codec_get_pincfg(codec, vref_pin)) == AC_JACK_PORT_NONE) {
-			unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin);
-			if (vref_val != AC_PINCTL_VREF_HIZ)
-				snd_hda_set_pin_ctl(codec, vref_pin, PIN_IN | (set_as_mic ? vref_val : 0));
-		}
+	if (val == AC_PINCTL_VREF_HIZ && spec->shared_mic_vref_pin) {
+		const hda_nid_t vref_pin = spec->shared_mic_vref_pin;
+		unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin);
+		if (vref_val != AC_PINCTL_VREF_HIZ)
+			snd_hda_set_pin_ctl(codec, vref_pin, PIN_IN | (set_as_mic ? vref_val : 0));
 	}
 
 	val = set_as_mic ? val | PIN_IN : PIN_HP;
@@ -5642,6 +5639,7 @@ static int patch_alc262(struct hda_codec *codec)
 		return err;
 
 	spec = codec->spec;
+	spec->shared_mic_vref_pin = 0x18;
 
 #if 0
 	/* pshou 07/11/05  set a zero PCM sample to DAC when FIFO is
@@ -6425,6 +6423,7 @@ static int patch_alc269(struct hda_codec *codec)
 		return err;
 
 	spec = codec->spec;
+	spec->shared_mic_vref_pin = 0x18;
 
 	alc_pick_fixup(codec, alc269_fixup_models,
 		       alc269_fixup_tbl, alc269_fixups);
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 033/112] ALSA: hda/realtek - Drop auto_mic_valid_imux flag
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (31 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 032/112] ALSA: hda/realtek - Allow different pins for shared hp/mic vref check Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 034/112] ALSA: hda/realtek - Remove unused fields and macro definitions Takashi Iwai
                   ` (81 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

This flag is superfluous now and it's always as same as
spec->auto_mic.  Let's drop.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 5c5197b..d55db33 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -209,7 +209,6 @@ struct alc_spec {
 	unsigned int line_jack_present:1;
 	unsigned int master_mute:1;
 	unsigned int auto_mic:1;
-	unsigned int auto_mic_valid_imux:1;	/* valid imux for auto-mic */
 	unsigned int automute_speaker:1; /* automute speaker outputs */
 	unsigned int automute_lo:1; /* automute LO outputs */
 	unsigned int detect_hp:1;	/* Headphone detection enabled */
@@ -622,7 +621,7 @@ static void alc_mic_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
 	struct alc_spec *spec = codec->spec;
 	hda_nid_t *pins = spec->imux_pins;
 
-	if (!spec->auto_mic || !spec->auto_mic_valid_imux)
+	if (!spec->auto_mic)
 		return;
 	if (snd_BUG_ON(spec->int_mic_idx < 0 || spec->ext_mic_idx < 0))
 		return;
@@ -1004,8 +1003,6 @@ static bool alc_auto_mic_check_imux(struct hda_codec *codec)
 		snd_hda_jack_detect_enable_callback(codec, spec->dock_mic_pin,
 						    ALC_MIC_EVENT,
 						    alc_mic_automute);
-
-	spec->auto_mic_valid_imux = 1;
 	return true;
 }
 
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 034/112] ALSA: hda/realtek - Remove unused fields and macro definitions
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (32 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 033/112] ALSA: hda/realtek - Drop auto_mic_valid_imux flag Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 035/112] ALSA: hda/realtek - Handle vmaster hook in the parser side Takashi Iwai
                   ` (80 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

Also arranged alc_spec definitions to optimize bit fields.
Use a bit field for spec->need_dac_fix, too.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 17 ++---------------
 1 file changed, 2 insertions(+), 15 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index d55db33..f5696ab 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -73,8 +73,6 @@ struct alc_multi_io {
 	unsigned int ctl_in;	/* cached input-pin control value */
 };
 
-#define MAX_VOL_NIDS	0x40
-
 /* make compatible with old code */
 #define alc_apply_pincfgs	snd_hda_apply_pincfgs
 #define alc_apply_fixup		snd_hda_apply_fixup
@@ -161,7 +159,6 @@ struct alc_spec {
 	unsigned int cur_adc_format;
 
 	/* capture source */
-	unsigned int num_mux_defs;
 	struct hda_input_mux input_mux;
 	unsigned int cur_mux[3];
 	hda_nid_t ext_mic_pin;
@@ -171,7 +168,6 @@ struct alc_spec {
 	/* channel model */
 	const struct hda_channel_mode *channel_mode;
 	int num_channel_mode;
-	int need_dac_fix;
 	int const_channel_count;	/* min. channel count (for speakers) */
 	int ext_channel_count;		/* current channel count for multi-io */
 
@@ -218,19 +214,15 @@ struct alc_spec {
 	unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */
 
 	/* other flags */
+	unsigned int need_dac_fix:1; /* need to limit DACs for multi channels */
 	unsigned int no_analog :1; /* digital I/O only */
 	unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */
-	unsigned int single_input_src:1;
-	unsigned int vol_in_capsrc:1; /* use capsrc volume (ADC has no vol) */
-	unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */
 	unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */
 	unsigned int inv_dmic_fixup:1; /* has inverted digital-mic workaround */
 	unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */
 	unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */
 
-	/* auto-mute control */
-	int automute_mode;
-	hda_nid_t automute_mixer_nid[AUTO_CFG_MAX_OUTS];
+	unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */
 
 	int init_amp;
 	int codec_variant;	/* flag for other variants */
@@ -612,9 +604,6 @@ static void alc_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack
 	call_update_outputs(codec);
 }
 
-#define get_connection_index(codec, mux, nid) \
-	snd_hda_get_conn_index(codec, mux, nid, 0)
-
 /* standard mic auto-switch helper */
 static void alc_mic_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
 {
@@ -1619,8 +1608,6 @@ static const char * const alc_slave_pfxs[] = {
  * build control elements
  */
 
-#define NID_MAPPING		(-1)
-
 static void alc_free_kctls(struct hda_codec *codec);
 
 #ifdef CONFIG_SND_HDA_INPUT_BEEP
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 035/112] ALSA: hda/realtek - Handle vmaster hook in the parser side
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (33 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 034/112] ALSA: hda/realtek - Remove unused fields and macro definitions Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 036/112] ALSA: hda/realtek - Assign Master mixer when possible Takashi Iwai
                   ` (79 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

... so that the fixup just needs to set the hook function in
FIXUP_ACT_PROBE.  This will make easier to port for other codecs,
too.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 23 +++++++----------------
 1 file changed, 7 insertions(+), 16 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index f5696ab..579a35e 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -1687,6 +1687,8 @@ static int __alc_build_controls(struct hda_codec *codec)
 					    true, &spec->vmaster_mute.sw_kctl);
 		if (err < 0)
 			return err;
+		if (spec->vmaster_mute.hook)
+			snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true);
 	}
 
 	alc_free_kctls(codec); /* no longer needed */
@@ -1745,6 +1747,9 @@ static int alc_init(struct hda_codec *codec)
 	snd_hda_gen_apply_verbs(codec);
 	alc_auto_init_std(codec);
 
+	if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook)
+		snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
+
 	alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT);
 
 	hda_call_check_power_status(codec, 0x01);
@@ -6019,15 +6024,8 @@ static void alc269_fixup_mic1_mute(struct hda_codec *codec,
 				   const struct alc_fixup *fix, int action)
 {
 	struct alc_spec *spec = codec->spec;
-	switch (action) {
-	case ALC_FIXUP_ACT_BUILD:
+	if (action == ALC_FIXUP_ACT_PROBE)
 		spec->vmaster_mute.hook = alc269_fixup_mic1_mute_hook;
-		snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true);
-		/* fallthru */
-	case ALC_FIXUP_ACT_INIT:
-		snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
-		break;
-	}
 }
 
 /* update mute-LED according to the speaker mute state via mic2 VREF pin */
@@ -6042,15 +6040,8 @@ static void alc269_fixup_mic2_mute(struct hda_codec *codec,
 				   const struct alc_fixup *fix, int action)
 {
 	struct alc_spec *spec = codec->spec;
-	switch (action) {
-	case ALC_FIXUP_ACT_BUILD:
+	if (action == ALC_FIXUP_ACT_PROBE)
 		spec->vmaster_mute.hook = alc269_fixup_mic2_mute_hook;
-		snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true);
-		/* fallthru */
-	case ALC_FIXUP_ACT_INIT:
-		snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
-		break;
-	}
 }
 
 static void alc271_hp_gate_mic_jack(struct hda_codec *codec,
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 036/112] ALSA: hda/realtek - Assign Master mixer when possible
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (34 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 035/112] ALSA: hda/realtek - Handle vmaster hook in the parser side Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 037/112] ALSA: hda/realtek - Merge a few split functions Takashi Iwai
                   ` (78 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

There are a few more cases where we can assign "Master" mixer element
safely, e.g. when a single DAC is used in the whole output paths.

Also, when vmaster hook is present, avoid "Master" but assign "PCM"
instead.  Otherwise vmaster hook won't work properly.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 579a35e..c980489 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -2306,7 +2306,14 @@ static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch,
 	*index = 0;
 	if (cfg->line_outs == 1 && !spec->multi_ios &&
 	    !cfg->hp_outs && !cfg->speaker_outs && can_be_master)
-		return "Master";
+		return spec->vmaster_mute.hook ? "PCM" : "Master";
+
+	/* if there is really a single DAC used in the whole output paths,
+	 * use it master (or "PCM" if a vmaster hook is present)
+	 */
+	if (spec->multiout.num_dacs == 1 && !spec->mixer_nid &&
+	    !spec->multiout.hp_out_nid[0] && !spec->multiout.extra_out_nid[0])
+		return spec->vmaster_mute.hook ? "PCM" : "Master";
 
 	switch (cfg->line_out_type) {
 	case AUTO_PIN_SPEAKER_OUT:
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 037/112] ALSA: hda/realtek - Merge a few split functions
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (35 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 036/112] ALSA: hda/realtek - Assign Master mixer when possible Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 038/112] ALSA: hda/realtek - Allow passing name=NULL to alc_kcontrol_new() Takashi Iwai
                   ` (77 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

Merge a few functions that have been split due to historical reasons
to single functions.  Splitting too much (and placing too far away)
actually worsens the readability.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 32 ++++++--------------------------
 1 file changed, 6 insertions(+), 26 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index c980489..7f4ad78 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -653,14 +653,6 @@ static void alc880_unsol_event(struct hda_codec *codec, unsigned int res)
 	snd_hda_jack_unsol_event(codec, res >> 2);
 }
 
-/* call init functions of standard auto-mute helpers */
-static void alc_inithook(struct hda_codec *codec)
-{
-	alc_hp_automute(codec, NULL);
-	alc_line_automute(codec, NULL);
-	alc_mic_automute(codec, NULL);
-}
-
 /* additional initialization for ALC888 variants */
 static void alc888_coef_init(struct hda_codec *codec)
 {
@@ -1619,7 +1611,7 @@ static const struct snd_kcontrol_new alc_beep_mixer[] = {
 };
 #endif
 
-static int __alc_build_controls(struct hda_codec *codec)
+static int alc_build_controls(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
 	int i, err;
@@ -1693,13 +1685,6 @@ static int __alc_build_controls(struct hda_codec *codec)
 
 	alc_free_kctls(codec); /* no longer needed */
 
-	return 0;
-}
-
-static int alc_build_jacks(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-
 	if (spec->shared_mic_hp) {
 		int err;
 		int nid = spec->autocfg.inputs[1].pin;
@@ -1711,18 +1696,10 @@ static int alc_build_jacks(struct hda_codec *codec)
 			return err;
 	}
 
-	return snd_hda_jack_add_kctls(codec, &spec->autocfg);
-}
-
-static int alc_build_controls(struct hda_codec *codec)
-{
-	int err = __alc_build_controls(codec);
+	err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
 	if (err < 0)
 		return err;
 
-	err = alc_build_jacks(codec);
-	if (err < 0)
-		return err;
 	alc_apply_fixup(codec, ALC_FIXUP_ACT_BUILD);
 	return 0;
 }
@@ -4244,7 +4221,10 @@ static void alc_auto_init_std(struct hda_codec *codec)
 	alc_auto_init_analog_input(codec);
 	alc_auto_init_input_src(codec);
 	alc_auto_init_digital(codec);
-	alc_inithook(codec);
+	/* call init functions of standard auto-mute helpers */
+	alc_hp_automute(codec, NULL);
+	alc_line_automute(codec, NULL);
+	alc_mic_automute(codec, NULL);
 }
 
 /*
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 038/112] ALSA: hda/realtek - Allow passing name=NULL to alc_kcontrol_new()
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (36 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 037/112] ALSA: hda/realtek - Merge a few split functions Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 039/112] ALSA: hda/realtek - Allow multiple individual capture volume/switch controls Takashi Iwai
                   ` (76 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

This prevents stupid typos.

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

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 7f4ad78..4253b9a 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -862,7 +862,10 @@ alc_kcontrol_new(struct alc_spec *spec, const char *name,
 	if (!knew)
 		return NULL;
 	*knew = *temp;
-	knew->name = kstrdup(name, GFP_KERNEL);
+	if (name)
+		knew->name = kstrdup(name, GFP_KERNEL);
+	else if (knew->name)
+		knew->name = kstrdup(knew->name, GFP_KERNEL);
 	if (!knew->name)
 		return NULL;
 	return knew;
@@ -872,7 +875,7 @@ static int alc_add_automute_mode_enum(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
 
-	if (!alc_kcontrol_new(spec, "Auto-Mute Mode", &alc_automute_mode_enum))
+	if (!alc_kcontrol_new(spec, NULL, &alc_automute_mode_enum))
 		return -ENOMEM;
 	return 0;
 }
@@ -1556,6 +1559,7 @@ static int alc_inv_dmic_sw_put(struct snd_kcontrol *kcontrol,
 
 static const struct snd_kcontrol_new alc_inv_dmic_sw = {
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Inverted Internal Mic Capture Switch",
 	.info = snd_ctl_boolean_mono_info,
 	.get = alc_inv_dmic_sw_get,
 	.put = alc_inv_dmic_sw_put,
@@ -1565,8 +1569,7 @@ static int alc_add_inv_dmic_mixer(struct hda_codec *codec, hda_nid_t nid)
 {
 	struct alc_spec *spec = codec->spec;
 
-	if (!alc_kcontrol_new(spec, "Inverted Internal Mic Capture Switch",
-			      &alc_inv_dmic_sw))
+	if (!alc_kcontrol_new(spec, NULL, &alc_inv_dmic_sw))
 		return -ENOMEM;
 	spec->inv_dmic_fixup = 1;
 	spec->inv_dmic_muted = 0;
@@ -2555,7 +2558,7 @@ static int create_capture_mixers(struct hda_codec *codec)
 		nums = spec->num_adc_nids;
 
 	if (!spec->auto_mic && imux->num_items > 1) {
-		knew = alc_kcontrol_new(spec, "Input Source", &cap_src_temp);
+		knew = alc_kcontrol_new(spec, NULL, &cap_src_temp);
 		if (!knew)
 			return -ENOMEM;
 		knew->count = nums;
@@ -2579,8 +2582,7 @@ static int create_capture_mixers(struct hda_codec *codec)
 		}
 
 		if (vol) {
-			knew = alc_kcontrol_new(spec, "Capture Volume",
-						&cap_vol_temp);
+			knew = alc_kcontrol_new(spec, NULL, &cap_vol_temp);
 			if (!knew)
 				return -ENOMEM;
 			knew->index = n;
@@ -2588,8 +2590,7 @@ static int create_capture_mixers(struct hda_codec *codec)
 			knew->subdevice = HDA_SUBDEV_AMP_FLAG;
 		}
 		if (sw) {
-			knew = alc_kcontrol_new(spec, "Capture Switch",
-						&cap_sw_temp);
+			knew = alc_kcontrol_new(spec, NULL, &cap_sw_temp);
 			if (!knew)
 				return -ENOMEM;
 			knew->index = n;
@@ -4106,8 +4107,7 @@ static int alc_auto_add_multi_channel_mode(struct hda_codec *codec)
 	struct alc_spec *spec = codec->spec;
 
 	if (spec->multi_ios > 0) {
-		if (!alc_kcontrol_new(spec, "Channel Mode",
-				      &alc_auto_channel_mode_enum))
+		if (!alc_kcontrol_new(spec, NULL, &alc_auto_channel_mode_enum))
 			return -ENOMEM;
 	}
 	return 0;
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 039/112] ALSA: hda/realtek - Allow multiple individual capture volume/switch controls
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (37 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 038/112] ALSA: hda/realtek - Allow passing name=NULL to alc_kcontrol_new() Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 040/112] ALSA: hda/realtek - Add conexant-style inverted dmic handling Takashi Iwai
                   ` (75 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

So far we create only "Capture Volume" and "Capture Switch" controls
for binding all possible amps, but we'd prefer creating individual
capture volume and switch controls per input in some cases
(e.g. conexant parser does it).

Add a new flag, spec->multi_cap_vol, to follow that policy.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 149 +++++++++++++++++++++++++++++++++++++-----
 1 file changed, 131 insertions(+), 18 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 4253b9a..2295389 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -221,6 +221,7 @@ struct alc_spec {
 	unsigned int inv_dmic_fixup:1; /* has inverted digital-mic workaround */
 	unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */
 	unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */
+	unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */
 
 	unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */
 
@@ -2474,6 +2475,10 @@ static int check_dyn_adc_switch(struct hda_codec *codec)
 		spec->num_adc_nids = 1; /* reduce to a single ADC */
 	}
 
+	/* single index for individual volumes ctls */
+	if (!spec->dyn_adc_switch && spec->multi_cap_vol)
+		spec->num_adc_nids = 1;
+
 	return 0;
 }
 
@@ -2545,12 +2550,122 @@ static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path)
 	return 0;
 }
 
+static int add_single_cap_ctl(struct hda_codec *codec, const char *label,
+			      int idx, bool is_switch, unsigned int ctl)
+{
+	struct alc_spec *spec = codec->spec;
+	char tmpname[44];
+	int type = is_switch ? ALC_CTL_WIDGET_MUTE : ALC_CTL_WIDGET_VOL;
+	const char *sfx = is_switch ? "Switch" : "Volume";
+
+	if (!ctl)
+		return 0;
+
+	if (label)
+		snprintf(tmpname, sizeof(tmpname),
+			 "%s Capture %s", label, sfx);
+	else
+		snprintf(tmpname, sizeof(tmpname),
+			 "Capture %s", sfx);
+	return add_control(spec, type, tmpname, idx, ctl);
+}
+
+/* create single (and simple) capture volume and switch controls */
+static int create_single_cap_vol_ctl(struct hda_codec *codec, int idx,
+				     unsigned int vol_ctl, unsigned int sw_ctl)
+{
+	int err;
+	err = add_single_cap_ctl(codec, NULL, idx, false, vol_ctl);
+	if (err < 0)
+		return err;
+	err = add_single_cap_ctl(codec, NULL, idx, true, sw_ctl);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+/* create bound capture volume and switch controls */
+static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx,
+				   unsigned int vol_ctl, unsigned int sw_ctl)
+{
+	struct alc_spec *spec = codec->spec;
+	struct snd_kcontrol_new *knew;
+
+	if (vol_ctl) {
+		knew = alc_kcontrol_new(spec, NULL, &cap_vol_temp);
+		if (!knew)
+			return -ENOMEM;
+		knew->index = idx;
+		knew->private_value = vol_ctl;
+		knew->subdevice = HDA_SUBDEV_AMP_FLAG;
+	}
+	if (sw_ctl) {
+		knew = alc_kcontrol_new(spec, NULL, &cap_sw_temp);
+		if (!knew)
+			return -ENOMEM;
+		knew->index = idx;
+		knew->private_value = sw_ctl;
+		knew->subdevice = HDA_SUBDEV_AMP_FLAG;
+	}
+	return 0;
+}
+
+/* return the vol ctl when used first in the imux list */
+static unsigned int get_first_cap_ctl(struct hda_codec *codec, int idx, int type)
+{
+	struct alc_spec *spec = codec->spec;
+	struct nid_path *path;
+	unsigned int ctl;
+	int i;
+
+	path = get_nid_path(codec, spec->imux_pins[idx],
+			    get_adc_nid(codec, 0, idx));
+	if (!path)
+		return 0;
+	ctl = path->ctls[type];
+	if (!ctl)
+		return 0;
+	for (i = 0; i < idx - 1; i++) {
+		path = get_nid_path(codec, spec->imux_pins[i],
+				    get_adc_nid(codec, 0, i));
+		if (path && path->ctls[type] == ctl)
+			return 0;
+	}
+	return ctl;
+}
+
+/* create individual capture volume and switch controls per input */
+static int create_multi_cap_vol_ctl(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	struct hda_input_mux *imux = &spec->input_mux;
+	int i, err, type, type_idx = 0;
+	const char *prev_label = NULL;
+
+	for (i = 0; i < imux->num_items; i++) {
+		const char *label;
+		label = hda_get_autocfg_input_label(codec, &spec->autocfg, i);
+		if (prev_label && !strcmp(label, prev_label))
+			type_idx++;
+		else
+			type_idx = 0;
+		prev_label = label;
+
+		for (type = 0; type < 2; type++) {
+			err = add_single_cap_ctl(codec, label, type_idx, type,
+						 get_first_cap_ctl(codec, i, type));
+			if (err < 0)
+				return err;
+		}
+	}
+	return 0;
+}
+
 static int create_capture_mixers(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
 	struct hda_input_mux *imux = &spec->input_mux;
-	struct snd_kcontrol_new *knew;
-	int i, n, nums;
+	int i, n, nums, err;
 
 	if (spec->dyn_adc_switch)
 		nums = 1;
@@ -2558,6 +2673,7 @@ static int create_capture_mixers(struct hda_codec *codec)
 		nums = spec->num_adc_nids;
 
 	if (!spec->auto_mic && imux->num_items > 1) {
+		struct snd_kcontrol_new *knew;
 		knew = alc_kcontrol_new(spec, NULL, &cap_src_temp);
 		if (!knew)
 			return -ENOMEM;
@@ -2565,6 +2681,7 @@ static int create_capture_mixers(struct hda_codec *codec)
 	}
 
 	for (n = 0; n < nums; n++) {
+		bool multi = false;
 		int vol, sw;
 
 		vol = sw = 0;
@@ -2577,26 +2694,22 @@ static int create_capture_mixers(struct hda_codec *codec)
 			parse_capvol_in_path(codec, path);
 			if (!vol)
 				vol = path->ctls[NID_PATH_VOL_CTL];
+			else if (vol != path->ctls[NID_PATH_VOL_CTL])
+				multi = true;
 			if (!sw)
 				sw = path->ctls[NID_PATH_MUTE_CTL];
+			else if (sw != path->ctls[NID_PATH_MUTE_CTL])
+				multi = true;
 		}
 
-		if (vol) {
-			knew = alc_kcontrol_new(spec, NULL, &cap_vol_temp);
-			if (!knew)
-				return -ENOMEM;
-			knew->index = n;
-			knew->private_value = vol;
-			knew->subdevice = HDA_SUBDEV_AMP_FLAG;
-		}
-		if (sw) {
-			knew = alc_kcontrol_new(spec, NULL, &cap_sw_temp);
-			if (!knew)
-				return -ENOMEM;
-			knew->index = n;
-			knew->private_value = sw;
-			knew->subdevice = HDA_SUBDEV_AMP_FLAG;
-		}
+		if (!multi)
+			err = create_single_cap_vol_ctl(codec, n, vol, sw);
+		else if (!spec->multi_cap_vol)
+			err = create_bind_cap_vol_ctl(codec, n, vol, sw);
+		else
+			err = create_multi_cap_vol_ctl(codec);
+		if (err < 0)
+			return err;
 	}
 
 	return 0;
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 040/112] ALSA: hda/realtek - Add conexant-style inverted dmic handling
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (38 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 039/112] ALSA: hda/realtek - Allow multiple individual capture volume/switch controls Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 041/112] ALSA: hda - Move fixup code into struct hda_codec Takashi Iwai
                   ` (74 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

To make the parser more generic, a few codes to handle the inverted
stereo dmic in a way Conexant parser does is added in this patch.

The caller should set spec->inv_dmic_split flag appropriately.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 35 ++++++++++++++++++++++++++++++++++-
 1 file changed, 34 insertions(+), 1 deletion(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 2295389..558bd6d 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -184,6 +184,7 @@ struct alc_spec {
 	int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */
 	hda_nid_t inv_dmic_pin;
 	hda_nid_t shared_mic_vref_pin;
+	int inv_dmic_split_idx;	/* used internally for inv_dmic_split */
 
 	/* DAC list */
 	int num_all_dacs;
@@ -222,6 +223,7 @@ struct alc_spec {
 	unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */
 	unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */
 	unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */
+	unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */
 
 	unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */
 
@@ -2550,6 +2552,8 @@ static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path)
 	return 0;
 }
 
+static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs);
+
 static int add_single_cap_ctl(struct hda_codec *codec, const char *label,
 			      int idx, bool is_switch, unsigned int ctl)
 {
@@ -2557,17 +2561,37 @@ static int add_single_cap_ctl(struct hda_codec *codec, const char *label,
 	char tmpname[44];
 	int type = is_switch ? ALC_CTL_WIDGET_MUTE : ALC_CTL_WIDGET_VOL;
 	const char *sfx = is_switch ? "Switch" : "Volume";
+	unsigned int chs;
+	int err;
 
 	if (!ctl)
 		return 0;
 
+	if (idx == spec->inv_dmic_split_idx)
+		chs = 1;
+	else
+		chs = 3;
+
 	if (label)
 		snprintf(tmpname, sizeof(tmpname),
 			 "%s Capture %s", label, sfx);
 	else
 		snprintf(tmpname, sizeof(tmpname),
 			 "Capture %s", sfx);
-	return add_control(spec, type, tmpname, idx, ctl);
+	err = add_control(spec, type, tmpname, idx,
+			  amp_val_replace_channels(ctl, chs));
+	if (err < 0 || chs == 3)
+		return err;
+
+	/* Make independent right kcontrol */
+	if (label)
+		snprintf(tmpname, sizeof(tmpname),
+			 "Inverted %s Capture %s", label, sfx);
+	else
+		snprintf(tmpname, sizeof(tmpname),
+			 "Inverted Capture %s", sfx);
+	return add_control(spec, type, tmpname, idx,
+			   amp_val_replace_channels(ctl, 2));
 }
 
 /* create single (and simple) capture volume and switch controls */
@@ -2730,6 +2754,7 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec)
 	if (num_adcs < 0)
 		return 0;
 
+	spec->inv_dmic_split_idx = -1;
 	for (i = 0; i < cfg->num_inputs; i++) {
 		hda_nid_t pin;
 		const char *label;
@@ -2783,6 +2808,14 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec)
 				imux_added = true;
 			}
 		}
+
+		if (spec->inv_dmic_split) {
+			if (cfg->inputs[i].type == AUTO_PIN_MIC) {
+				unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin);
+				if (snd_hda_get_input_pin_attr(def_conf) == INPUT_PIN_ATTR_INT)
+					spec->inv_dmic_split_idx = i;
+			}
+		}
 	}
 
 	return 0;
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 041/112] ALSA: hda - Move fixup code into struct hda_codec
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (39 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 040/112] ALSA: hda/realtek - Add conexant-style inverted dmic handling Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 042/112] ALSA: hda/realtek - Fix split stereo dmic code Takashi Iwai
                   ` (73 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

Since the fixup code is used commonly, it's worth to move it to the
common place, struct hda_codec, instead of keeping in hda_gen_spec.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_auto_parser.c | 39 ++++++++++-----------
 sound/pci/hda/hda_auto_parser.h | 78 -----------------------------------------
 sound/pci/hda/hda_codec.c       |  2 ++
 sound/pci/hda/hda_codec.h       |  8 +++++
 sound/pci/hda/hda_local.h       | 53 ++++++++++++++++++++++++++++
 sound/pci/hda/patch_cirrus.c    |  8 +----
 sound/pci/hda/patch_conexant.c  |  6 +---
 sound/pci/hda/patch_realtek.c   | 10 ++----
 sound/pci/hda/patch_via.c       |  4 ---
 9 files changed, 86 insertions(+), 122 deletions(-)

diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c
index 7da883a..d460688 100644
--- a/sound/pci/hda/hda_auto_parser.c
+++ b/sound/pci/hda/hda_auto_parser.c
@@ -622,28 +622,27 @@ int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
 }
 EXPORT_SYMBOL_HDA(snd_hda_get_pin_label);
 
-int snd_hda_gen_add_verbs(struct hda_gen_spec *spec,
-			  const struct hda_verb *list)
+int snd_hda_add_verbs(struct hda_codec *codec,
+		      const struct hda_verb *list)
 {
 	const struct hda_verb **v;
-	v = snd_array_new(&spec->verbs);
+	v = snd_array_new(&codec->verbs);
 	if (!v)
 		return -ENOMEM;
 	*v = list;
 	return 0;
 }
-EXPORT_SYMBOL_HDA(snd_hda_gen_add_verbs);
+EXPORT_SYMBOL_HDA(snd_hda_add_verbs);
 
-void snd_hda_gen_apply_verbs(struct hda_codec *codec)
+void snd_hda_apply_verbs(struct hda_codec *codec)
 {
-	struct hda_gen_spec *spec = codec->spec;
 	int i;
-	for (i = 0; i < spec->verbs.used; i++) {
-		struct hda_verb **v = snd_array_elem(&spec->verbs, i);
+	for (i = 0; i < codec->verbs.used; i++) {
+		struct hda_verb **v = snd_array_elem(&codec->verbs, i);
 		snd_hda_sequence_write(codec, *v);
 	}
 }
-EXPORT_SYMBOL_HDA(snd_hda_gen_apply_verbs);
+EXPORT_SYMBOL_HDA(snd_hda_apply_verbs);
 
 void snd_hda_apply_pincfgs(struct hda_codec *codec,
 			   const struct hda_pintbl *cfg)
@@ -655,18 +654,17 @@ EXPORT_SYMBOL_HDA(snd_hda_apply_pincfgs);
 
 void snd_hda_apply_fixup(struct hda_codec *codec, int action)
 {
-	struct hda_gen_spec *spec = codec->spec;
-	int id = spec->fixup_id;
+	int id = codec->fixup_id;
 #ifdef CONFIG_SND_DEBUG_VERBOSE
-	const char *modelname = spec->fixup_name;
+	const char *modelname = codec->fixup_name;
 #endif
 	int depth = 0;
 
-	if (!spec->fixup_list)
+	if (!codec->fixup_list)
 		return;
 
 	while (id >= 0) {
-		const struct hda_fixup *fix = spec->fixup_list + id;
+		const struct hda_fixup *fix = codec->fixup_list + id;
 
 		switch (fix->type) {
 		case HDA_FIXUP_PINS:
@@ -683,7 +681,7 @@ void snd_hda_apply_fixup(struct hda_codec *codec, int action)
 			snd_printdd(KERN_INFO SFX
 				    "%s: Apply fix-verbs for %s\n",
 				    codec->chip_name, modelname);
-			snd_hda_gen_add_verbs(codec->spec, fix->v.verbs);
+			snd_hda_add_verbs(codec, fix->v.verbs);
 			break;
 		case HDA_FIXUP_FUNC:
 			if (!fix->v.func)
@@ -713,15 +711,14 @@ void snd_hda_pick_fixup(struct hda_codec *codec,
 			const struct snd_pci_quirk *quirk,
 			const struct hda_fixup *fixlist)
 {
-	struct hda_gen_spec *spec = codec->spec;
 	const struct snd_pci_quirk *q;
 	int id = -1;
 	const char *name = NULL;
 
 	/* when model=nofixup is given, don't pick up any fixups */
 	if (codec->modelname && !strcmp(codec->modelname, "nofixup")) {
-		spec->fixup_list = NULL;
-		spec->fixup_id = -1;
+		codec->fixup_list = NULL;
+		codec->fixup_id = -1;
 		return;
 	}
 
@@ -759,10 +756,10 @@ void snd_hda_pick_fixup(struct hda_codec *codec,
 		}
 	}
 
-	spec->fixup_id = id;
+	codec->fixup_id = id;
 	if (id >= 0) {
-		spec->fixup_list = fixlist;
-		spec->fixup_name = name;
+		codec->fixup_list = fixlist;
+		codec->fixup_name = name;
 	}
 }
 EXPORT_SYMBOL_HDA(snd_hda_pick_fixup);
diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/pci/hda/hda_auto_parser.h
index 632ad0a..ff11074 100644
--- a/sound/pci/hda/hda_auto_parser.h
+++ b/sound/pci/hda/hda_auto_parser.h
@@ -89,82 +89,4 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
 #define snd_hda_parse_pin_def_config(codec, cfg, ignore) \
 	snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0)
 
-/*
- */
-
-struct hda_gen_spec {
-	/* fix-up list */
-	int fixup_id;
-	const struct hda_fixup *fixup_list;
-	const char *fixup_name;
-
-	/* additional init verbs */
-	struct snd_array verbs;
-};
-
-
-/*
- * Fix-up pin default configurations and add default verbs
- */
-
-struct hda_pintbl {
-	hda_nid_t nid;
-	u32 val;
-};
-
-struct hda_model_fixup {
-	const int id;
-	const char *name;
-};
-
-struct hda_fixup {
-	int type;
-	bool chained;
-	int chain_id;
-	union {
-		const struct hda_pintbl *pins;
-		const struct hda_verb *verbs;
-		void (*func)(struct hda_codec *codec,
-			     const struct hda_fixup *fix,
-			     int action);
-	} v;
-};
-
-/* fixup types */
-enum {
-	HDA_FIXUP_INVALID,
-	HDA_FIXUP_PINS,
-	HDA_FIXUP_VERBS,
-	HDA_FIXUP_FUNC,
-};
-
-/* fixup action definitions */
-enum {
-	HDA_FIXUP_ACT_PRE_PROBE,
-	HDA_FIXUP_ACT_PROBE,
-	HDA_FIXUP_ACT_INIT,
-	HDA_FIXUP_ACT_BUILD,
-};
-
-int snd_hda_gen_add_verbs(struct hda_gen_spec *spec,
-			  const struct hda_verb *list);
-void snd_hda_gen_apply_verbs(struct hda_codec *codec);
-void snd_hda_apply_pincfgs(struct hda_codec *codec,
-			   const struct hda_pintbl *cfg);
-void snd_hda_apply_fixup(struct hda_codec *codec, int action);
-void snd_hda_pick_fixup(struct hda_codec *codec,
-			const struct hda_model_fixup *models,
-			const struct snd_pci_quirk *quirk,
-			const struct hda_fixup *fixlist);
-
-static inline void snd_hda_gen_init(struct hda_gen_spec *spec)
-{
-	snd_array_init(&spec->verbs, sizeof(struct hda_verb *), 8);
-}
-
-static inline void snd_hda_gen_free(struct hda_gen_spec *spec)
-{
-	snd_array_free(&spec->verbs);
-}
-
 #endif /* __SOUND_HDA_AUTO_PARSER_H */
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 7383746..12b88a2 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1253,6 +1253,7 @@ int snd_hda_codec_new(struct hda_bus *bus,
 	snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64);
 	snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16);
 	snd_array_init(&codec->jacktbl, sizeof(struct hda_jack_tbl), 16);
+	snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8);
 	INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work);
 
 #ifdef CONFIG_PM
@@ -2407,6 +2408,7 @@ int snd_hda_codec_reset(struct hda_codec *codec)
 	snd_array_free(&codec->driver_pins);
 	snd_array_free(&codec->cvt_setups);
 	snd_array_free(&codec->spdif_out);
+	snd_array_free(&codec->verbs);
 	codec->num_pcms = 0;
 	codec->pcm_info = NULL;
 	codec->preset = NULL;
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index cab39b2..a1cb28f 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -896,6 +896,14 @@ struct hda_codec {
 	/* jack detection */
 	struct snd_array jacks;
 #endif
+
+	/* fix-up list */
+	int fixup_id;
+	const struct hda_fixup *fixup_list;
+	const char *fixup_name;
+
+	/* additional init verbs */
+	struct snd_array verbs;
 };
 
 /* direction */
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index ff56da8..de12dcc 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -386,6 +386,59 @@ int snd_hda_add_new_ctls(struct hda_codec *codec,
 			 const struct snd_kcontrol_new *knew);
 
 /*
+ * Fix-up pin default configurations and add default verbs
+ */
+
+struct hda_pintbl {
+	hda_nid_t nid;
+	u32 val;
+};
+
+struct hda_model_fixup {
+	const int id;
+	const char *name;
+};
+
+struct hda_fixup {
+	int type;
+	bool chained;
+	int chain_id;
+	union {
+		const struct hda_pintbl *pins;
+		const struct hda_verb *verbs;
+		void (*func)(struct hda_codec *codec,
+			     const struct hda_fixup *fix,
+			     int action);
+	} v;
+};
+
+/* fixup types */
+enum {
+	HDA_FIXUP_INVALID,
+	HDA_FIXUP_PINS,
+	HDA_FIXUP_VERBS,
+	HDA_FIXUP_FUNC,
+};
+
+/* fixup action definitions */
+enum {
+	HDA_FIXUP_ACT_PRE_PROBE,
+	HDA_FIXUP_ACT_PROBE,
+	HDA_FIXUP_ACT_INIT,
+	HDA_FIXUP_ACT_BUILD,
+};
+
+int snd_hda_add_verbs(struct hda_codec *codec, const struct hda_verb *list);
+void snd_hda_apply_verbs(struct hda_codec *codec);
+void snd_hda_apply_pincfgs(struct hda_codec *codec,
+			   const struct hda_pintbl *cfg);
+void snd_hda_apply_fixup(struct hda_codec *codec, int action);
+void snd_hda_pick_fixup(struct hda_codec *codec,
+			const struct hda_model_fixup *models,
+			const struct snd_pci_quirk *quirk,
+			const struct hda_fixup *fixlist);
+
+/*
  * unsolicited event handler
  */
 
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index a2537b2..7b0b8c3 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -34,8 +34,6 @@
  */
 
 struct cs_spec {
-	struct hda_gen_spec gen;
-
 	struct auto_pin_cfg autocfg;
 	struct hda_multi_out multiout;
 	struct snd_kcontrol *vmaster_sw;
@@ -1201,7 +1199,7 @@ static int cs_init(struct hda_codec *codec)
 
 	snd_hda_sequence_write(codec, cs_coef_init_verbs);
 
-	snd_hda_gen_apply_verbs(codec);
+	snd_hda_apply_verbs(codec);
 
 	if (spec->gpio_mask) {
 		snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
@@ -1252,7 +1250,6 @@ static void cs_free(struct hda_codec *codec)
 	struct cs_spec *spec = codec->spec;
 	kfree(spec->capture_bind[0]);
 	kfree(spec->capture_bind[1]);
-	snd_hda_gen_free(&spec->gen);
 	kfree(codec->spec);
 }
 
@@ -1443,7 +1440,6 @@ static int patch_cs420x(struct hda_codec *codec)
 	if (!spec)
 		return -ENOMEM;
 	codec->spec = spec;
-	snd_hda_gen_init(&spec->gen);
 
 	spec->vendor_nid = CS420X_VENDOR_NID;
 
@@ -1981,7 +1977,6 @@ static int patch_cs4210(struct hda_codec *codec)
 	if (!spec)
 		return -ENOMEM;
 	codec->spec = spec;
-	snd_hda_gen_init(&spec->gen);
 
 	spec->vendor_nid = CS4210_VENDOR_NID;
 
@@ -2021,7 +2016,6 @@ static int patch_cs4213(struct hda_codec *codec)
 	if (!spec)
 		return -ENOMEM;
 	codec->spec = spec;
-	snd_hda_gen_init(&spec->gen);
 
 	spec->vendor_nid = CS4213_VENDOR_NID;
 
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 60890bf..727e678 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -67,8 +67,6 @@ struct imux_info {
 };
 
 struct conexant_spec {
-	struct hda_gen_spec gen;
-
 	const struct snd_kcontrol_new *mixers[5];
 	int num_mixers;
 	hda_nid_t vmaster_nid;
@@ -451,7 +449,6 @@ static int conexant_init(struct hda_codec *codec)
 static void conexant_free(struct hda_codec *codec)
 {
 	struct conexant_spec *spec = codec->spec;
-	snd_hda_gen_free(&spec->gen);
 	snd_hda_detach_beep_device(codec);
 	kfree(spec);
 }
@@ -4045,7 +4042,7 @@ static void cx_auto_init_digital(struct hda_codec *codec)
 static int cx_auto_init(struct hda_codec *codec)
 {
 	struct conexant_spec *spec = codec->spec;
-	snd_hda_gen_apply_verbs(codec);
+	snd_hda_apply_verbs(codec);
 	cx_auto_init_output(codec);
 	cx_auto_init_input(codec);
 	cx_auto_init_digital(codec);
@@ -4549,7 +4546,6 @@ static int patch_conexant_auto(struct hda_codec *codec)
 	if (!spec)
 		return -ENOMEM;
 	codec->spec = spec;
-	snd_hda_gen_init(&spec->gen);
 
 	switch (codec->vendor_id) {
 	case 0x14f15045:
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 558bd6d..1beb797 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -121,8 +121,6 @@ struct nid_path {
 };
 
 struct alc_spec {
-	struct hda_gen_spec gen;
-
 	/* codec parameterization */
 	const struct snd_kcontrol_new *mixers[5];	/* mixer arrays */
 	unsigned int num_mixers;
@@ -1727,7 +1725,7 @@ static int alc_init(struct hda_codec *codec)
 	alc_fix_pll(codec);
 	alc_auto_init_amp(codec, spec->init_amp);
 
-	snd_hda_gen_apply_verbs(codec);
+	snd_hda_apply_verbs(codec);
 	alc_auto_init_std(codec);
 
 	if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook)
@@ -2117,7 +2115,6 @@ static void alc_free(struct hda_codec *codec)
 	alc_free_kctls(codec);
 	alc_free_bind_ctls(codec);
 	snd_array_free(&spec->paths);
-	snd_hda_gen_free(&spec->gen);
 	kfree(spec);
 	snd_hda_detach_beep_device(codec);
 }
@@ -4525,7 +4522,6 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid)
 	codec->spec = spec;
 	codec->single_adc_amp = 1;
 	spec->mixer_nid = mixer_nid;
-	snd_hda_gen_init(&spec->gen);
 	snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32);
 	snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8);
 	snd_array_init(&spec->paths, sizeof(struct nid_path), 8);
@@ -5001,7 +4997,7 @@ static void alc260_fixup_gpio1_toggle(struct hda_codec *codec,
 		spec->autocfg.hp_pins[0] = 0x0f; /* copy it for automute */
 		snd_hda_jack_detect_enable_callback(codec, 0x0f, ALC_HP_EVENT,
 						    alc_hp_automute);
-		snd_hda_gen_add_verbs(&spec->gen, alc_gpio1_init_verbs);
+		snd_hda_add_verbs(codec, alc_gpio1_init_verbs);
 	}
 }
 
@@ -5878,7 +5874,7 @@ static int alc268_parse_auto_config(struct hda_codec *codec)
 	if (err > 0) {
 		if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d) {
 			add_mixer(spec, alc268_beep_mixer);
-			snd_hda_gen_add_verbs(&spec->gen, alc268_beep_init_verbs);
+			snd_hda_add_verbs(codec, alc268_beep_init_verbs);
 		}
 	}
 	return err;
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index 09bb649..b224b3d 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -120,8 +120,6 @@ enum {
 };
 
 struct via_spec {
-	struct hda_gen_spec gen;
-
 	/* codec parameterization */
 	const struct snd_kcontrol_new *mixers[6];
 	unsigned int num_mixers;
@@ -252,7 +250,6 @@ static struct via_spec * via_new_spec(struct hda_codec *codec)
 	/* VT1708BCE & VT1708S are almost same */
 	if (spec->codec_type == VT1708BCE)
 		spec->codec_type = VT1708S;
-	snd_hda_gen_init(&spec->gen);
 	return spec;
 }
 
@@ -1657,7 +1654,6 @@ static void via_free(struct hda_codec *codec)
 	vt1708_stop_hp_work(spec);
 	kfree(spec->bind_cap_vol);
 	kfree(spec->bind_cap_sw);
-	snd_hda_gen_free(&spec->gen);
 	kfree(spec);
 }
 
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 042/112] ALSA: hda/realtek - Fix split stereo dmic code
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (40 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 041/112] ALSA: hda - Move fixup code into struct hda_codec Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 043/112] ALSA: hda - Rearrange INPUT_PIN_ATTR_* Takashi Iwai
                   ` (72 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

The previous commit passed an utterly wrong value for checking the
split inv dmic pin.  This patch fixes it and also tries to remove
inv_dmic_split_idx field.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 58 +++++++++++++++++++++++++++----------------
 1 file changed, 36 insertions(+), 22 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 1beb797..fecd89a 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -182,7 +182,6 @@ struct alc_spec {
 	int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */
 	hda_nid_t inv_dmic_pin;
 	hda_nid_t shared_mic_vref_pin;
-	int inv_dmic_split_idx;	/* used internally for inv_dmic_split */
 
 	/* DAC list */
 	int num_all_dacs;
@@ -2552,23 +2551,19 @@ static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path)
 static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs);
 
 static int add_single_cap_ctl(struct hda_codec *codec, const char *label,
-			      int idx, bool is_switch, unsigned int ctl)
+			      int idx, bool is_switch, unsigned int ctl,
+			      bool inv_dmic)
 {
 	struct alc_spec *spec = codec->spec;
 	char tmpname[44];
 	int type = is_switch ? ALC_CTL_WIDGET_MUTE : ALC_CTL_WIDGET_VOL;
 	const char *sfx = is_switch ? "Switch" : "Volume";
-	unsigned int chs;
+	unsigned int chs = inv_dmic ? 1 : 3;
 	int err;
 
 	if (!ctl)
 		return 0;
 
-	if (idx == spec->inv_dmic_split_idx)
-		chs = 1;
-	else
-		chs = 3;
-
 	if (label)
 		snprintf(tmpname, sizeof(tmpname),
 			 "%s Capture %s", label, sfx);
@@ -2591,15 +2586,36 @@ static int add_single_cap_ctl(struct hda_codec *codec, const char *label,
 			   amp_val_replace_channels(ctl, 2));
 }
 
+static bool is_inv_dmic_pin(struct hda_codec *codec, hda_nid_t nid)
+{
+	struct alc_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	unsigned int val;
+	int i;
+
+	if (!spec->inv_dmic_split)
+		return false;
+	for (i = 0; i < cfg->num_inputs; i++) {
+		if (cfg->inputs[i].pin != nid)
+			continue;
+		if (cfg->inputs[i].type != AUTO_PIN_MIC)
+			return false;
+		val = snd_hda_codec_get_pincfg(codec, nid);
+		return snd_hda_get_input_pin_attr(val) == INPUT_PIN_ATTR_INT;
+	}
+	return false;
+}
+
 /* create single (and simple) capture volume and switch controls */
 static int create_single_cap_vol_ctl(struct hda_codec *codec, int idx,
-				     unsigned int vol_ctl, unsigned int sw_ctl)
+				     unsigned int vol_ctl, unsigned int sw_ctl,
+				     bool inv_dmic)
 {
 	int err;
-	err = add_single_cap_ctl(codec, NULL, idx, false, vol_ctl);
+	err = add_single_cap_ctl(codec, NULL, idx, false, vol_ctl, inv_dmic);
 	if (err < 0)
 		return err;
-	err = add_single_cap_ctl(codec, NULL, idx, true, sw_ctl);
+	err = add_single_cap_ctl(codec, NULL, idx, true, sw_ctl, inv_dmic);
 	if (err < 0)
 		return err;
 	return 0;
@@ -2665,16 +2681,19 @@ static int create_multi_cap_vol_ctl(struct hda_codec *codec)
 
 	for (i = 0; i < imux->num_items; i++) {
 		const char *label;
+		bool inv_dmic;
 		label = hda_get_autocfg_input_label(codec, &spec->autocfg, i);
 		if (prev_label && !strcmp(label, prev_label))
 			type_idx++;
 		else
 			type_idx = 0;
 		prev_label = label;
+		inv_dmic = is_inv_dmic_pin(codec, spec->imux_pins[i]);
 
 		for (type = 0; type < 2; type++) {
 			err = add_single_cap_ctl(codec, label, type_idx, type,
-						 get_first_cap_ctl(codec, i, type));
+						 get_first_cap_ctl(codec, i, type),
+						 inv_dmic);
 			if (err < 0)
 				return err;
 		}
@@ -2703,6 +2722,7 @@ static int create_capture_mixers(struct hda_codec *codec)
 
 	for (n = 0; n < nums; n++) {
 		bool multi = false;
+		bool inv_dmic = false;
 		int vol, sw;
 
 		vol = sw = 0;
@@ -2721,10 +2741,13 @@ static int create_capture_mixers(struct hda_codec *codec)
 				sw = path->ctls[NID_PATH_MUTE_CTL];
 			else if (sw != path->ctls[NID_PATH_MUTE_CTL])
 				multi = true;
+			if (is_inv_dmic_pin(codec, spec->imux_pins[i]))
+				inv_dmic = true;
 		}
 
 		if (!multi)
-			err = create_single_cap_vol_ctl(codec, n, vol, sw);
+			err = create_single_cap_vol_ctl(codec, n, vol, sw,
+							inv_dmic);
 		else if (!spec->multi_cap_vol)
 			err = create_bind_cap_vol_ctl(codec, n, vol, sw);
 		else
@@ -2751,7 +2774,6 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec)
 	if (num_adcs < 0)
 		return 0;
 
-	spec->inv_dmic_split_idx = -1;
 	for (i = 0; i < cfg->num_inputs; i++) {
 		hda_nid_t pin;
 		const char *label;
@@ -2805,14 +2827,6 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec)
 				imux_added = true;
 			}
 		}
-
-		if (spec->inv_dmic_split) {
-			if (cfg->inputs[i].type == AUTO_PIN_MIC) {
-				unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin);
-				if (snd_hda_get_input_pin_attr(def_conf) == INPUT_PIN_ATTR_INT)
-					spec->inv_dmic_split_idx = i;
-			}
-		}
 	}
 
 	return 0;
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 043/112] ALSA: hda - Rearrange INPUT_PIN_ATTR_*
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (41 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 042/112] ALSA: hda/realtek - Fix split stereo dmic code Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 044/112] ALSA: hda - More generic auto-mic switching for Realtek codecs Takashi Iwai
                   ` (71 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

Put INPUT_PIN_ATTR_FRONT after INPUT_PIN_ATTR_REAR, and define
INPUT_PIN_ATTR_LAST to point to the last element.

This is a preliminary work for cleaning up Realtek auto-mic parser.
In the auto-mic implementation, the front panel is preferred over the
rear panel.  By arranging the attr definitions like in this commit, we
can simply use sort() for figuring out the priority order.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_auto_parser.c | 2 +-
 sound/pci/hda/hda_auto_parser.h | 3 ++-
 sound/pci/hda/patch_via.c       | 2 +-
 3 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c
index d460688..44c81d3 100644
--- a/sound/pci/hda/hda_auto_parser.c
+++ b/sound/pci/hda/hda_auto_parser.c
@@ -363,7 +363,7 @@ static const char *hda_get_input_pin_label(struct hda_codec *codec,
 {
 	unsigned int def_conf;
 	static const char * const mic_names[] = {
-		"Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic",
+		"Internal Mic", "Dock Mic", "Mic", "Rear Mic", "Front Mic"
 	};
 	int attr;
 
diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/pci/hda/hda_auto_parser.h
index ff11074..f748071 100644
--- a/sound/pci/hda/hda_auto_parser.h
+++ b/sound/pci/hda/hda_auto_parser.h
@@ -51,8 +51,9 @@ enum {
 	INPUT_PIN_ATTR_INT,	/* internal mic/line-in */
 	INPUT_PIN_ATTR_DOCK,	/* docking mic/line-in */
 	INPUT_PIN_ATTR_NORMAL,	/* mic/line-in jack */
-	INPUT_PIN_ATTR_FRONT,	/* mic/line-in jack in front */
 	INPUT_PIN_ATTR_REAR,	/* mic/line-in jack in rear */
+	INPUT_PIN_ATTR_FRONT,	/* mic/line-in jack in front */
+	INPUT_PIN_ATTR_LAST = INPUT_PIN_ATTR_FRONT,
 };
 
 int snd_hda_get_input_pin_attr(unsigned int def_conf);
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index b224b3d..d3c852a 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -1913,7 +1913,7 @@ static void mangle_smart51(struct hda_codec *codec)
 	int i, j, nums, attr;
 	int pins[AUTO_CFG_MAX_INS];
 
-	for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
+	for (attr = INPUT_PIN_ATTR_LAST; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
 		nums = 0;
 		for (i = 0; i < cfg->num_inputs; i++) {
 			unsigned int def;
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 044/112] ALSA: hda - More generic auto-mic switching for Realtek codecs
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (42 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 043/112] ALSA: hda - Rearrange INPUT_PIN_ATTR_* Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-10  0:41   ` Raymond Yau
  2013-01-08 11:38 ` [PATCH 045/112] ALSA: hda/realtek - Remove redundant argument from alc_mux_select() Takashi Iwai
                   ` (70 subsequent siblings)
  114 siblings, 1 reply; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

This patch extends the capability of the auto-mic feature.
Instead of limiting the automatic input-source selection only to the
mics (internal, external and dock mics), allow it for generic inputs,
e.g. switching between the rear line-in and the front mic.

The logic is to check the attribute and location of input pins, and
enable the automatic selection feature only if all such pins are in
different locations (e.g. internal, front, rear, etc) and line-in or
mic pins.  That is, if multiple input pins are assigned to a single
location, the feature isn't enabled because we don't know the
priority.

(You may wonder why this restriction doesn't exist for the headphone
 automute.  The reason is that the output case is different from the
 input: the input source is an exclusive selection while the output
 can be multiplexed.)

Note that, for avoiding regressions, the line-in auto switching
feature isn't activated as default.  It has to be set explicitly via
spec->line_in_auto_switch flag in a fixup code.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/patch_realtek.c | 143 +++++++++++++++++++++++-------------------
 1 file changed, 78 insertions(+), 65 deletions(-)

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index fecd89a..2aaecdc 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -28,6 +28,7 @@
 #include <linux/slab.h>
 #include <linux/pci.h>
 #include <linux/module.h>
+#include <linux/sort.h>
 #include <sound/core.h>
 #include <sound/jack.h>
 #include "hda_codec.h"
@@ -90,6 +91,13 @@ struct alc_multi_io {
 #define ALC_FIXUP_ACT_INIT	HDA_FIXUP_ACT_INIT
 #define ALC_FIXUP_ACT_BUILD	HDA_FIXUP_ACT_BUILD
 
+#define MAX_AUTO_MIC_PINS	3
+
+struct alc_automic_entry {
+	hda_nid_t pin;		/* pin */
+	int idx;		/* imux index, -1 = invalid */
+	unsigned int attr;	/* pin attribute (INPUT_PIN_ATTR_*) */
+};
 
 #define MAX_NID_PATH_DEPTH	5
 
@@ -159,9 +167,6 @@ struct alc_spec {
 	/* capture source */
 	struct hda_input_mux input_mux;
 	unsigned int cur_mux[3];
-	hda_nid_t ext_mic_pin;
-	hda_nid_t dock_mic_pin;
-	hda_nid_t int_mic_pin;
 
 	/* channel model */
 	const struct hda_channel_mode *channel_mode;
@@ -179,7 +184,6 @@ struct alc_spec {
 	hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
 	hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS];
 	unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS];
-	int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */
 	hda_nid_t inv_dmic_pin;
 	hda_nid_t shared_mic_vref_pin;
 
@@ -190,6 +194,10 @@ struct alc_spec {
 	/* path list */
 	struct snd_array paths;
 
+	/* auto-mic stuff */
+	int am_num_entries;
+	struct alc_automic_entry am_entry[MAX_AUTO_MIC_PINS];
+
 	/* hooks */
 	void (*init_hook)(struct hda_codec *codec);
 #ifdef CONFIG_PM
@@ -210,6 +218,7 @@ struct alc_spec {
 	unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */
 	unsigned int automute_lo_possible:1;	  /* there are line outs and HP */
 	unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */
+	unsigned int line_in_auto_switch:1; /* allow line-in auto switch */
 
 	/* other flags */
 	unsigned int need_dac_fix:1; /* need to limit DACs for multi channels */
@@ -608,20 +617,18 @@ static void alc_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack
 static void alc_mic_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
 {
 	struct alc_spec *spec = codec->spec;
-	hda_nid_t *pins = spec->imux_pins;
+	int i;
 
 	if (!spec->auto_mic)
 		return;
-	if (snd_BUG_ON(spec->int_mic_idx < 0 || spec->ext_mic_idx < 0))
-		return;
 
-	if (snd_hda_jack_detect(codec, pins[spec->ext_mic_idx]))
-		alc_mux_select(codec, 0, spec->ext_mic_idx, false);
-	else if (spec->dock_mic_idx >= 0 &&
-		   snd_hda_jack_detect(codec, pins[spec->dock_mic_idx]))
-		alc_mux_select(codec, 0, spec->dock_mic_idx, false);
-	else
-		alc_mux_select(codec, 0, spec->int_mic_idx, false);
+	for (i = spec->am_num_entries - 1; i > 0; i--) {
+		if (snd_hda_jack_detect(codec, spec->am_entry[i].pin)) {
+			alc_mux_select(codec, 0, spec->am_entry[i].idx, false);
+			return;
+		}
+	}
+	alc_mux_select(codec, 0, spec->am_entry[0].idx, false);
 }
 
 /* update the master volume per volume-knob's unsol event */
@@ -970,26 +977,33 @@ static bool alc_auto_mic_check_imux(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
 	const struct hda_input_mux *imux;
+	int i;
 
 	imux = &spec->input_mux;
-	spec->ext_mic_idx = find_idx_in_nid_list(spec->ext_mic_pin,
-					spec->imux_pins, imux->num_items);
-	spec->int_mic_idx = find_idx_in_nid_list(spec->int_mic_pin,
-					spec->imux_pins, imux->num_items);
-	spec->dock_mic_idx = find_idx_in_nid_list(spec->dock_mic_pin,
-					spec->imux_pins, imux->num_items);
-	if (spec->ext_mic_idx < 0 || spec->int_mic_idx < 0)
-		return false; /* no corresponding imux */
-
-	snd_hda_jack_detect_enable_callback(codec, spec->ext_mic_pin,
-					    ALC_MIC_EVENT, alc_mic_automute);
-	if (spec->dock_mic_pin)
-		snd_hda_jack_detect_enable_callback(codec, spec->dock_mic_pin,
+	for (i = 0; i < spec->am_num_entries; i++) {
+		spec->am_entry[i].idx =
+			find_idx_in_nid_list(spec->am_entry[i].pin,
+					     spec->imux_pins, imux->num_items);
+		if (spec->am_entry[i].idx < 0)
+			return false; /* no corresponding imux */
+	}
+
+	/* we don't need the jack detection for the first pin */
+	for (i = 1; i < spec->am_num_entries; i++)
+		snd_hda_jack_detect_enable_callback(codec,
+						    spec->am_entry[i].pin,
 						    ALC_MIC_EVENT,
 						    alc_mic_automute);
 	return true;
 }
 
+static int compare_attr(const void *ap, const void *bp)
+{
+	const struct alc_automic_entry *a = ap;
+	const struct alc_automic_entry *b = bp;
+	return (int)(a->attr - b->attr);
+}
+
 /*
  * Check the availability of auto-mic switch;
  * Set up if really supported
@@ -998,66 +1012,63 @@ static int alc_init_auto_mic(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
 	struct auto_pin_cfg *cfg = &spec->autocfg;
-	hda_nid_t fixed, ext, dock;
-	int i;
+	unsigned int types;
+	int i, num_pins;
 
-	spec->ext_mic_idx = spec->int_mic_idx = spec->dock_mic_idx = -1;
-
-	fixed = ext = dock = 0;
+	types = 0;
+	num_pins = 0;
 	for (i = 0; i < cfg->num_inputs; i++) {
 		hda_nid_t nid = cfg->inputs[i].pin;
-		unsigned int defcfg;
-		defcfg = snd_hda_codec_get_pincfg(codec, nid);
-		switch (snd_hda_get_input_pin_attr(defcfg)) {
+		unsigned int attr;
+		attr = snd_hda_codec_get_pincfg(codec, nid);
+		attr = snd_hda_get_input_pin_attr(attr);
+		if (types & (1 << attr))
+			return 0; /* already occupied */
+		switch (attr) {
 		case INPUT_PIN_ATTR_INT:
-			if (fixed)
-				return 0; /* already occupied */
 			if (cfg->inputs[i].type != AUTO_PIN_MIC)
 				return 0; /* invalid type */
-			fixed = nid;
 			break;
 		case INPUT_PIN_ATTR_UNUSED:
 			return 0; /* invalid entry */
-		case INPUT_PIN_ATTR_DOCK:
-			if (dock)
-				return 0; /* already occupied */
-			if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
-				return 0; /* invalid type */
-			dock = nid;
-			break;
 		default:
-			if (ext)
-				return 0; /* already occupied */
-			if (cfg->inputs[i].type != AUTO_PIN_MIC)
+			if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
 				return 0; /* invalid type */
-			ext = nid;
+			if (!spec->line_in_auto_switch &&
+			    cfg->inputs[i].type != AUTO_PIN_MIC)
+				return 0; /* only mic is allowed */
+			if (!is_jack_detectable(codec, nid))
+				return 0; /* no unsol support */
 			break;
 		}
+		if (num_pins >= MAX_AUTO_MIC_PINS)
+			return 0;
+		types |= (1 << attr);
+		spec->am_entry[num_pins].pin = nid;
+		spec->am_entry[num_pins].attr = attr;
+		num_pins++;
 	}
-	if (!ext && dock) {
-		ext = dock;
-		dock = 0;
-	}
-	if (!ext || !fixed)
+
+	if (num_pins < 2)
 		return 0;
-	if (!is_jack_detectable(codec, ext))
-		return 0; /* no unsol support */
-	if (dock && !is_jack_detectable(codec, dock))
-		return 0; /* no unsol support */
 
-	/* check imux indices */
-	spec->ext_mic_pin = ext;
-	spec->int_mic_pin = fixed;
-	spec->dock_mic_pin = dock;
+	spec->am_num_entries = num_pins;
+	/* sort the am_entry in the order of attr so that the pin with a
+	 * higher attr will be selected when the jack is plugged.
+	 */
+	sort(spec->am_entry, num_pins, sizeof(spec->am_entry[0]),
+	     compare_attr, NULL);
 
 	if (!alc_auto_mic_check_imux(codec))
 		return 0;
 
 	spec->auto_mic = 1;
 	spec->num_adc_nids = 1;
-	spec->cur_mux[0] = spec->int_mic_idx;
+	spec->cur_mux[0] = spec->am_entry[0].idx;
 	snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n",
-		    ext, fixed, dock);
+		    spec->am_entry[0].pin,
+		    spec->am_entry[1].pin,
+		    spec->am_entry[2].pin);
 
 	return 0;
 }
@@ -6193,8 +6204,10 @@ static void alc271_hp_gate_mic_jack(struct hda_codec *codec,
 {
 	struct alc_spec *spec = codec->spec;
 
+	if (snd_BUG_ON(!spec->am_entry[1].pin || !spec->autocfg.hp_pins[0]))
+		return;
 	if (action == ALC_FIXUP_ACT_PROBE)
-		snd_hda_jack_set_gating_jack(codec, spec->ext_mic_pin,
+		snd_hda_jack_set_gating_jack(codec, spec->am_entry[1].pin,
 					     spec->autocfg.hp_pins[0]);
 }
 
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 045/112] ALSA: hda/realtek - Remove redundant argument from alc_mux_select()
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (43 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 044/112] ALSA: hda - More generic auto-mic switching for Realtek codecs Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 046/112] ALSA: hda - Merge Realtek parser code to generic parser Takashi Iwai
                   ` (69 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

The argument "force" is always false in the recent code.

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

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 2aaecdc..2240ab6 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -360,7 +360,7 @@ static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic)
 
 /* select the given imux item; either unmute exclusively or select the route */
 static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx,
-			  unsigned int idx, bool force)
+			  unsigned int idx)
 {
 	struct alc_spec *spec = codec->spec;
 	const struct hda_input_mux *imux;
@@ -372,7 +372,7 @@ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx,
 
 	if (idx >= imux->num_items)
 		idx = imux->num_items - 1;
-	if (spec->cur_mux[adc_idx] == idx && !force)
+	if (spec->cur_mux[adc_idx] == idx)
 		return 0;
 
 	path = get_nid_path(codec, spec->imux_pins[spec->cur_mux[adc_idx]],
@@ -407,7 +407,7 @@ static int alc_mux_enum_put(struct snd_kcontrol *kcontrol,
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	return alc_mux_select(codec, adc_idx,
-			      ucontrol->value.enumerated.item[0], false);
+			      ucontrol->value.enumerated.item[0]);
 }
 
 /*
@@ -624,11 +624,11 @@ static void alc_mic_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
 
 	for (i = spec->am_num_entries - 1; i > 0; i--) {
 		if (snd_hda_jack_detect(codec, spec->am_entry[i].pin)) {
-			alc_mux_select(codec, 0, spec->am_entry[i].idx, false);
+			alc_mux_select(codec, 0, spec->am_entry[i].idx);
 			return;
 		}
 	}
-	alc_mux_select(codec, 0, spec->am_entry[0].idx, false);
+	alc_mux_select(codec, 0, spec->am_entry[0].idx);
 }
 
 /* update the master volume per volume-knob's unsol event */
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 046/112] ALSA: hda - Merge Realtek parser code to generic parser
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (44 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 045/112] ALSA: hda/realtek - Remove redundant argument from alc_mux_select() Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 047/112] ALSA: hda - Add EAPD control " Takashi Iwai
                   ` (68 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

Finally the whole generic parser code in Realtek driver is moved into
hda_generic.c so that it can be used for generic codec driver.
The old dumb generic driver is replaced.  Yay.

The future plan is to adapt this generic parser for other codecs,
i.e. the codec driver calls the exported functions in generic driver
but adds some codec-specific fixes and setups.

As of this commit, the complete driver code is still duplicated in
Realtek codec driver.  The big code reduction will come from now on.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 4130 ++++++++++++++++++++++++++++++++++---------
 sound/pci/hda/hda_generic.h |  199 +++
 2 files changed, 3518 insertions(+), 811 deletions(-)
 create mode 100644 sound/pci/hda/hda_generic.h

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index b81d3d0..2d19b91 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -23,1063 +23,3571 @@
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/export.h>
+#include <linux/sort.h>
 #include <sound/core.h>
+#include <sound/jack.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "hda_generic.h"
 
-/* widget node for parsing */
-struct hda_gnode {
-	hda_nid_t nid;		/* NID of this widget */
-	unsigned short nconns;	/* number of input connections */
-	hda_nid_t *conn_list;
-	hda_nid_t slist[2];	/* temporay list */
-	unsigned int wid_caps;	/* widget capabilities */
-	unsigned char type;	/* widget type */
-	unsigned char pin_ctl;	/* pin controls */
-	unsigned char checked;	/* the flag indicates that the node is already parsed */
-	unsigned int pin_caps;	/* pin widget capabilities */
-	unsigned int def_cfg;	/* default configuration */
-	unsigned int amp_out_caps;	/* AMP out capabilities */
-	unsigned int amp_in_caps;	/* AMP in capabilities */
-	struct list_head list;
-};
-
-/* patch-specific record */
-
-#define MAX_PCM_VOLS	2
-struct pcm_vol {
-	struct hda_gnode *node;	/* Node for PCM volume */
-	unsigned int index;	/* connection of PCM volume */
-};
 
-struct hda_gspec {
-	struct hda_gnode *dac_node[2];	/* DAC node */
-	struct hda_gnode *out_pin_node[2];	/* Output pin (Line-Out) node */
-	struct pcm_vol pcm_vol[MAX_PCM_VOLS];	/* PCM volumes */
-	unsigned int pcm_vol_nodes;	/* number of PCM volumes */
+/* initialize hda_gen_spec struct */
+int snd_hda_gen_spec_init(struct hda_gen_spec *spec)
+{
+	snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32);
+	snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8);
+	snd_array_init(&spec->paths, sizeof(struct nid_path), 8);
+	return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_gen_spec_init);
 
-	struct hda_gnode *adc_node;	/* ADC node */
-	struct hda_gnode *cap_vol_node;	/* Node for capture volume */
-	unsigned int cur_cap_src;	/* current capture source */
-	struct hda_input_mux input_mux;
+static struct snd_kcontrol_new *
+add_kctl(struct hda_gen_spec *spec, const char *name,
+	 const struct snd_kcontrol_new *temp)
+{
+	struct snd_kcontrol_new *knew = snd_array_new(&spec->kctls);
+	if (!knew)
+		return NULL;
+	*knew = *temp;
+	if (name)
+		knew->name = kstrdup(name, GFP_KERNEL);
+	else if (knew->name)
+		knew->name = kstrdup(knew->name, GFP_KERNEL);
+	if (!knew->name)
+		return NULL;
+	return knew;
+}
 
-	unsigned int def_amp_in_caps;
-	unsigned int def_amp_out_caps;
+static void free_kctls(struct hda_gen_spec *spec)
+{
+	if (spec->kctls.list) {
+		struct snd_kcontrol_new *kctl = spec->kctls.list;
+		int i;
+		for (i = 0; i < spec->kctls.used; i++)
+			kfree(kctl[i].name);
+	}
+	snd_array_free(&spec->kctls);
+}
 
-	struct hda_pcm pcm_rec;		/* PCM information */
+static struct hda_bind_ctls *new_bind_ctl(struct hda_codec *codec,
+					  unsigned int nums,
+					  struct hda_ctl_ops *ops)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	struct hda_bind_ctls **ctlp, *ctl;
+	ctlp = snd_array_new(&spec->bind_ctls);
+	if (!ctlp)
+		return NULL;
+	ctl = kzalloc(sizeof(*ctl) + sizeof(long) * (nums + 1), GFP_KERNEL);
+	*ctlp = ctl;
+	if (ctl)
+		ctl->ops = ops;
+	return ctl;
+}
 
-	struct list_head nid_list;	/* list of widgets */
+static void free_bind_ctls(struct hda_gen_spec *spec)
+{
+	if (spec->bind_ctls.list) {
+		struct hda_bind_ctls **ctl = spec->bind_ctls.list;
+		int i;
+		for (i = 0; i < spec->bind_ctls.used; i++)
+			kfree(ctl[i]);
+	}
+	snd_array_free(&spec->bind_ctls);
+}
 
-#ifdef CONFIG_PM
-#define MAX_LOOPBACK_AMPS	7
-	struct hda_loopback_check loopback;
-	int num_loopbacks;
-	struct hda_amp_list loopback_list[MAX_LOOPBACK_AMPS + 1];
-#endif
-};
+void snd_hda_gen_spec_free(struct hda_gen_spec *spec)
+{
+	if (!spec)
+		return;
+	free_kctls(spec);
+	free_bind_ctls(spec);
+	snd_array_free(&spec->paths);
+}
+EXPORT_SYMBOL_HDA(snd_hda_gen_spec_free);
 
 /*
- * retrieve the default device type from the default config value
+ * parsing paths
  */
-#define defcfg_type(node) (((node)->def_cfg & AC_DEFCFG_DEVICE) >> \
-			   AC_DEFCFG_DEVICE_SHIFT)
-#define defcfg_location(node) (((node)->def_cfg & AC_DEFCFG_LOCATION) >> \
-			       AC_DEFCFG_LOCATION_SHIFT)
-#define defcfg_port_conn(node) (((node)->def_cfg & AC_DEFCFG_PORT_CONN) >> \
-				AC_DEFCFG_PORT_CONN_SHIFT)
 
-/*
- * destructor
+/* get the path between the given NIDs;
+ * passing 0 to either @pin or @dac behaves as a wildcard
  */
-static void snd_hda_generic_free(struct hda_codec *codec)
+struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec,
+				      hda_nid_t from_nid, hda_nid_t to_nid)
 {
-	struct hda_gspec *spec = codec->spec;
-	struct hda_gnode *node, *n;
+	struct hda_gen_spec *spec = codec->spec;
+	int i;
 
-	if (! spec)
-		return;
-	/* free all widgets */
-	list_for_each_entry_safe(node, n, &spec->nid_list, list) {
-		if (node->conn_list != node->slist)
-			kfree(node->conn_list);
-		kfree(node);
+	for (i = 0; i < spec->paths.used; i++) {
+		struct nid_path *path = snd_array_elem(&spec->paths, i);
+		if (path->depth <= 0)
+			continue;
+		if ((!from_nid || path->path[0] == from_nid) &&
+		    (!to_nid || path->path[path->depth - 1] == to_nid))
+			return path;
 	}
-	kfree(spec);
+	return NULL;
 }
+EXPORT_SYMBOL_HDA(snd_hda_get_nid_path);
 
-
-/*
- * add a new widget node and read its attributes
- */
-static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid_t nid)
+/* check whether the given DAC is already found in any existing paths */
+static bool is_dac_already_used(struct hda_codec *codec, hda_nid_t nid)
 {
-	struct hda_gnode *node;
-	int nconns;
-	hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
+	struct hda_gen_spec *spec = codec->spec;
+	int i;
 
-	node = kzalloc(sizeof(*node), GFP_KERNEL);
-	if (node == NULL)
-		return -ENOMEM;
-	node->nid = nid;
-	node->wid_caps = get_wcaps(codec, nid);
-	node->type = get_wcaps_type(node->wid_caps);
-	if (node->wid_caps & AC_WCAP_CONN_LIST) {
-		nconns = snd_hda_get_connections(codec, nid, conn_list,
-						 HDA_MAX_CONNECTIONS);
-		if (nconns < 0) {
-			kfree(node);
-			return nconns;
-		}
-	} else {
-		nconns = 0;
-	}
-	if (nconns <= ARRAY_SIZE(node->slist))
-		node->conn_list = node->slist;
-	else {
-		node->conn_list = kmalloc(sizeof(hda_nid_t) * nconns,
-					  GFP_KERNEL);
-		if (! node->conn_list) {
-			snd_printk(KERN_ERR "hda-generic: cannot malloc\n");
-			kfree(node);
-			return -ENOMEM;
-		}
+	for (i = 0; i < spec->paths.used; i++) {
+		struct nid_path *path = snd_array_elem(&spec->paths, i);
+		if (path->path[0] == nid)
+			return true;
 	}
-	memcpy(node->conn_list, conn_list, nconns * sizeof(hda_nid_t));
-	node->nconns = nconns;
+	return false;
+}
 
-	if (node->type == AC_WID_PIN) {
-		node->pin_caps = snd_hda_query_pin_caps(codec, node->nid);
-		node->pin_ctl = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-		node->def_cfg = snd_hda_codec_get_pincfg(codec, node->nid);
-	}
+/* check whether the given two widgets can be connected */
+static bool is_reachable_path(struct hda_codec *codec,
+			      hda_nid_t from_nid, hda_nid_t to_nid)
+{
+	if (!from_nid || !to_nid)
+		return false;
+	return snd_hda_get_conn_index(codec, to_nid, from_nid, true) >= 0;
+}
 
-	if (node->wid_caps & AC_WCAP_OUT_AMP) {
-		if (node->wid_caps & AC_WCAP_AMP_OVRD)
-			node->amp_out_caps = snd_hda_param_read(codec, node->nid, AC_PAR_AMP_OUT_CAP);
-		if (! node->amp_out_caps)
-			node->amp_out_caps = spec->def_amp_out_caps;
-	}
-	if (node->wid_caps & AC_WCAP_IN_AMP) {
-		if (node->wid_caps & AC_WCAP_AMP_OVRD)
-			node->amp_in_caps = snd_hda_param_read(codec, node->nid, AC_PAR_AMP_IN_CAP);
-		if (! node->amp_in_caps)
-			node->amp_in_caps = spec->def_amp_in_caps;
+/* nid, dir and idx */
+#define AMP_VAL_COMPARE_MASK	(0xffff | (1U << 18) | (0x0f << 19))
+
+/* check whether the given ctl is already assigned in any path elements */
+static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	int i;
+
+	val &= AMP_VAL_COMPARE_MASK;
+	for (i = 0; i < spec->paths.used; i++) {
+		struct nid_path *path = snd_array_elem(&spec->paths, i);
+		if ((path->ctls[type] & AMP_VAL_COMPARE_MASK) == val)
+			return true;
 	}
-	list_add_tail(&node->list, &spec->nid_list);
-	return 0;
+	return false;
 }
 
-/*
- * build the AFG subtree
- */
-static int build_afg_tree(struct hda_codec *codec)
+/* check whether a control with the given (nid, dir, idx) was assigned */
+static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid,
+			      int dir, int idx)
 {
-	struct hda_gspec *spec = codec->spec;
-	int i, nodes, err;
-	hda_nid_t nid;
-
-	if (snd_BUG_ON(!spec))
-		return -EINVAL;
-
-	spec->def_amp_out_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_OUT_CAP);
-	spec->def_amp_in_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_IN_CAP);
+	unsigned int val = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir);
+	return is_ctl_used(codec, val, NID_PATH_VOL_CTL) ||
+		is_ctl_used(codec, val, NID_PATH_MUTE_CTL);
+}
 
-	nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
-	if (! nid || nodes < 0) {
-		printk(KERN_ERR "Invalid AFG subtree\n");
-		return -EINVAL;
+/* called recursively */
+static bool __parse_nid_path(struct hda_codec *codec,
+			     hda_nid_t from_nid, hda_nid_t to_nid,
+			     int with_aa_mix, struct nid_path *path, int depth)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	hda_nid_t conn[16];
+	int i, nums;
+
+	if (to_nid == spec->mixer_nid) {
+		if (!with_aa_mix)
+			return false;
+		with_aa_mix = 2; /* mark aa-mix is included */
 	}
 
-	/* parse all nodes belonging to the AFG */
-	for (i = 0; i < nodes; i++, nid++) {
-		if ((err = add_new_node(codec, spec, nid)) < 0)
-			return err;
+	nums = snd_hda_get_connections(codec, to_nid, conn, ARRAY_SIZE(conn));
+	for (i = 0; i < nums; i++) {
+		if (conn[i] != from_nid) {
+			/* special case: when from_nid is 0,
+			 * try to find an empty DAC
+			 */
+			if (from_nid ||
+			    get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT ||
+			    is_dac_already_used(codec, conn[i]))
+				continue;
+		}
+		/* aa-mix is requested but not included? */
+		if (!(spec->mixer_nid && with_aa_mix == 1))
+			goto found;
 	}
-
-	return 0;
+	if (depth >= MAX_NID_PATH_DEPTH)
+		return false;
+	for (i = 0; i < nums; i++) {
+		unsigned int type;
+		type = get_wcaps_type(get_wcaps(codec, conn[i]));
+		if (type == AC_WID_AUD_OUT || type == AC_WID_AUD_IN ||
+		    type == AC_WID_PIN)
+			continue;
+		if (__parse_nid_path(codec, from_nid, conn[i],
+				     with_aa_mix, path, depth + 1))
+			goto found;
+	}
+	return false;
+
+ found:
+	path->path[path->depth] = conn[i];
+	path->idx[path->depth + 1] = i;
+	if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX)
+		path->multi[path->depth + 1] = 1;
+	path->depth++;
+	return true;
 }
 
-
-/*
- * look for the node record for the given NID
+/* parse the widget path from the given nid to the target nid;
+ * when @from_nid is 0, try to find an empty DAC;
+ * when @with_aa_mix is 0, paths with spec->mixer_nid are excluded.
+ * when @with_aa_mix is 1, paths without spec->mixer_nid are excluded.
+ * when @with_aa_mix is 2, no special handling about spec->mixer_nid.
  */
-/* FIXME: should avoid the braindead linear search */
-static struct hda_gnode *hda_get_node(struct hda_gspec *spec, hda_nid_t nid)
+bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
+			    hda_nid_t to_nid, int with_aa_mix,
+			    struct nid_path *path)
 {
-	struct hda_gnode *node;
-
-	list_for_each_entry(node, &spec->nid_list, list) {
-		if (node->nid == nid)
-			return node;
+	if (__parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path, 1)) {
+		path->path[path->depth] = to_nid;
+		path->depth++;
+#if 0
+		snd_printdd("path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
+			    path->depth, path->path[0], path->path[1],
+			    path->path[2], path->path[3], path->path[4]);
+#endif
+		return true;
 	}
-	return NULL;
+	return false;
 }
+EXPORT_SYMBOL_HDA(snd_hda_parse_nid_path);
 
 /*
- * unmute (and set max vol) the output amplifier
+ * parse the path between the given NIDs and add to the path list.
+ * if no valid path is found, return NULL
  */
-static int unmute_output(struct hda_codec *codec, struct hda_gnode *node)
-{
-	unsigned int val, ofs;
-	snd_printdd("UNMUTE OUT: NID=0x%x\n", node->nid);
-	val = (node->amp_out_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
-	ofs = (node->amp_out_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
-	if (val >= ofs)
-		val -= ofs;
-	snd_hda_codec_amp_stereo(codec, node->nid, HDA_OUTPUT, 0, 0xff, val);
-	return 0;
+struct nid_path *
+snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid,
+		     hda_nid_t to_nid, int with_aa_mix)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	struct nid_path *path;
+
+	if (from_nid && to_nid && !is_reachable_path(codec, from_nid, to_nid))
+		return NULL;
+
+	path = snd_array_new(&spec->paths);
+	if (!path)
+		return NULL;
+	memset(path, 0, sizeof(*path));
+	if (snd_hda_parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path))
+		return path;
+	/* push back */
+	spec->paths.used--;
+	return NULL;
 }
+EXPORT_SYMBOL_HDA(snd_hda_add_new_path);
 
-/*
- * unmute (and set max vol) the input amplifier
- */
-static int unmute_input(struct hda_codec *codec, struct hda_gnode *node, unsigned int index)
-{
-	unsigned int val, ofs;
-	snd_printdd("UNMUTE IN: NID=0x%x IDX=0x%x\n", node->nid, index);
-	val = (node->amp_in_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
-	ofs = (node->amp_in_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
-	if (val >= ofs)
-		val -= ofs;
-	snd_hda_codec_amp_stereo(codec, node->nid, HDA_INPUT, index, 0xff, val);
+/* look for an empty DAC slot */
+static hda_nid_t look_for_dac(struct hda_codec *codec, hda_nid_t pin,
+			      bool is_digital)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	bool cap_digital;
+	int i;
+
+	for (i = 0; i < spec->num_all_dacs; i++) {
+		hda_nid_t nid = spec->all_dacs[i];
+		if (!nid || is_dac_already_used(codec, nid))
+			continue;
+		cap_digital = !!(get_wcaps(codec, nid) & AC_WCAP_DIGITAL);
+		if (is_digital != cap_digital)
+			continue;
+		if (is_reachable_path(codec, nid, pin))
+			return nid;
+	}
 	return 0;
 }
 
-/*
- * select the input connection of the given node.
- */
-static int select_input_connection(struct hda_codec *codec, struct hda_gnode *node,
-				   unsigned int index)
+/* replace the channels in the composed amp value with the given number */
+static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs)
 {
-	snd_printdd("CONNECT: NID=0x%x IDX=0x%x\n", node->nid, index);
-	return snd_hda_codec_write_cache(codec, node->nid, 0,
-					 AC_VERB_SET_CONNECT_SEL, index);
+	val &= ~(0x3U << 16);
+	val |= chs << 16;
+	return val;
 }
 
-/*
- * clear checked flag of each node in the node list
- */
-static void clear_check_flags(struct hda_gspec *spec)
+/* check whether the widget has the given amp capability for the direction */
+static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid,
+			   int dir, unsigned int bits)
 {
-	struct hda_gnode *node;
+	if (!nid)
+		return false;
+	if (get_wcaps(codec, nid) & (1 << (dir + 1)))
+		if (query_amp_caps(codec, nid, dir) & bits)
+			return true;
+	return false;
+}
+
+#define nid_has_mute(codec, nid, dir) \
+	check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
+#define nid_has_volume(codec, nid, dir) \
+	check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS)
+
+/* look for a widget suitable for assigning a mute switch in the path */
+static hda_nid_t look_for_out_mute_nid(struct hda_codec *codec,
+				       struct nid_path *path)
+{
+	int i;
+
+	for (i = path->depth - 1; i >= 0; i--) {
+		if (nid_has_mute(codec, path->path[i], HDA_OUTPUT))
+			return path->path[i];
+		if (i != path->depth - 1 && i != 0 &&
+		    nid_has_mute(codec, path->path[i], HDA_INPUT))
+			return path->path[i];
+	}
+	return 0;
+}
+
+/* look for a widget suitable for assigning a volume ctl in the path */
+static hda_nid_t look_for_out_vol_nid(struct hda_codec *codec,
+				      struct nid_path *path)
+{
+	int i;
 
-	list_for_each_entry(node, &spec->nid_list, list) {
-		node->checked = 0;
+	for (i = path->depth - 1; i >= 0; i--) {
+		if (nid_has_volume(codec, path->path[i], HDA_OUTPUT))
+			return path->path[i];
 	}
+	return 0;
 }
 
 /*
- * parse the output path recursively until reach to an audio output widget
- *
- * returns 0 if not found, 1 if found, or a negative error code.
+ * path activation / deactivation
  */
-static int parse_output_path(struct hda_codec *codec, struct hda_gspec *spec,
-			     struct hda_gnode *node, int dac_idx)
+
+/* can have the amp-in capability? */
+static bool has_amp_in(struct hda_codec *codec, struct nid_path *path, int idx)
 {
-	int i, err;
-	struct hda_gnode *child;
+	hda_nid_t nid = path->path[idx];
+	unsigned int caps = get_wcaps(codec, nid);
+	unsigned int type = get_wcaps_type(caps);
+
+	if (!(caps & AC_WCAP_IN_AMP))
+		return false;
+	if (type == AC_WID_PIN && idx > 0) /* only for input pins */
+		return false;
+	return true;
+}
 
-	if (node->checked)
-		return 0;
+/* can have the amp-out capability? */
+static bool has_amp_out(struct hda_codec *codec, struct nid_path *path, int idx)
+{
+	hda_nid_t nid = path->path[idx];
+	unsigned int caps = get_wcaps(codec, nid);
+	unsigned int type = get_wcaps_type(caps);
+
+	if (!(caps & AC_WCAP_OUT_AMP))
+		return false;
+	if (type == AC_WID_PIN && !idx) /* only for output pins */
+		return false;
+	return true;
+}
 
-	node->checked = 1;
-	if (node->type == AC_WID_AUD_OUT) {
-		if (node->wid_caps & AC_WCAP_DIGITAL) {
-			snd_printdd("Skip Digital OUT node %x\n", node->nid);
-			return 0;
-		}
-		snd_printdd("AUD_OUT found %x\n", node->nid);
-		if (spec->dac_node[dac_idx]) {
-			/* already DAC node is assigned, just unmute & connect */
-			return node == spec->dac_node[dac_idx];
-		}
-		spec->dac_node[dac_idx] = node;
-		if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
-		    spec->pcm_vol_nodes < MAX_PCM_VOLS) {
-			spec->pcm_vol[spec->pcm_vol_nodes].node = node;
-			spec->pcm_vol[spec->pcm_vol_nodes].index = 0;
-			spec->pcm_vol_nodes++;
-		}
-		return 1; /* found */
-	}
+/* check whether the given (nid,dir,idx) is active */
+static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid,
+			  unsigned int idx, unsigned int dir)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	int i, n;
 
-	for (i = 0; i < node->nconns; i++) {
-		child = hda_get_node(spec, node->conn_list[i]);
-		if (! child)
+	for (n = 0; n < spec->paths.used; n++) {
+		struct nid_path *path = snd_array_elem(&spec->paths, n);
+		if (!path->active)
 			continue;
-		err = parse_output_path(codec, spec, child, dac_idx);
-		if (err < 0)
-			return err;
-		else if (err > 0) {
-			/* found one,
-			 * select the path, unmute both input and output
-			 */
-			if (node->nconns > 1)
-				select_input_connection(codec, node, i);
-			unmute_input(codec, node, i);
-			unmute_output(codec, node);
-			if (spec->dac_node[dac_idx] &&
-			    spec->pcm_vol_nodes < MAX_PCM_VOLS &&
-			    !(spec->dac_node[dac_idx]->wid_caps &
-			      AC_WCAP_OUT_AMP)) {
-				if ((node->wid_caps & AC_WCAP_IN_AMP) ||
-				    (node->wid_caps & AC_WCAP_OUT_AMP)) {
-					int n = spec->pcm_vol_nodes;
-					spec->pcm_vol[n].node = node;
-					spec->pcm_vol[n].index = i;
-					spec->pcm_vol_nodes++;
-				}
+		for (i = 0; i < path->depth; i++) {
+			if (path->path[i] == nid) {
+				if (dir == HDA_OUTPUT || path->idx[i] == idx)
+					return true;
+				break;
 			}
-			return 1;
 		}
 	}
-	return 0;
+	return false;
 }
 
-/*
- * Look for the output PIN widget with the given jack type
- * and parse the output path to that PIN.
- *
- * Returns the PIN node when the path to DAC is established.
- */
-static struct hda_gnode *parse_output_jack(struct hda_codec *codec,
-					   struct hda_gspec *spec,
-					   int jack_type)
+/* get the default amp value for the target state */
+static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid,
+				   int dir, bool enable)
 {
-	struct hda_gnode *node;
-	int err;
-
-	list_for_each_entry(node, &spec->nid_list, list) {
-		if (node->type != AC_WID_PIN)
-			continue;
-		/* output capable? */
-		if (! (node->pin_caps & AC_PINCAP_OUT))
-			continue;
-		if (defcfg_port_conn(node) == AC_JACK_PORT_NONE)
-			continue; /* unconnected */
-		if (jack_type >= 0) {
-			if (jack_type != defcfg_type(node))
-				continue;
-			if (node->wid_caps & AC_WCAP_DIGITAL)
-				continue; /* skip SPDIF */
-		} else {
-			/* output as default? */
-			if (! (node->pin_ctl & AC_PINCTL_OUT_EN))
-				continue;
-		}
-		clear_check_flags(spec);
-		err = parse_output_path(codec, spec, node, 0);
-		if (err < 0)
-			return NULL;
-		if (! err && spec->out_pin_node[0]) {
-			err = parse_output_path(codec, spec, node, 1);
-			if (err < 0)
-				return NULL;
-		}
-		if (err > 0) {
-			/* unmute the PIN output */
-			unmute_output(codec, node);
-			/* set PIN-Out enable */
-			snd_hda_codec_write_cache(codec, node->nid, 0,
-					    AC_VERB_SET_PIN_WIDGET_CONTROL,
-					    AC_PINCTL_OUT_EN |
-					    ((node->pin_caps & AC_PINCAP_HP_DRV) ?
-					     AC_PINCTL_HP_EN : 0));
-			return node;
-		}
+	unsigned int caps;
+	unsigned int val = 0;
+
+	caps = query_amp_caps(codec, nid, dir);
+	if (caps & AC_AMPCAP_NUM_STEPS) {
+		/* set to 0dB */
+		if (enable)
+			val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
 	}
-	return NULL;
+	if (caps & AC_AMPCAP_MUTE) {
+		if (!enable)
+			val |= HDA_AMP_MUTE;
+	}
+	return val;
 }
 
+/* initialize the amp value (only at the first time) */
+static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx)
+{
+	int val = get_amp_val_to_activate(codec, nid, dir, false);
+	snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val);
+}
 
-/*
- * parse outputs
- */
-static int parse_output(struct hda_codec *codec)
+static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir,
+			 int idx, bool enable)
+{
+	int val;
+	if (is_ctl_associated(codec, nid, dir, idx) ||
+	    is_active_nid(codec, nid, dir, idx))
+		return;
+	val = get_amp_val_to_activate(codec, nid, dir, enable);
+	snd_hda_codec_amp_stereo(codec, nid, dir, idx, 0xff, val);
+}
+
+static void activate_amp_out(struct hda_codec *codec, struct nid_path *path,
+			     int i, bool enable)
+{
+	hda_nid_t nid = path->path[i];
+	init_amp(codec, nid, HDA_OUTPUT, 0);
+	activate_amp(codec, nid, HDA_OUTPUT, 0, enable);
+}
+
+static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
+			    int i, bool enable, bool add_aamix)
 {
-	struct hda_gspec *spec = codec->spec;
-	struct hda_gnode *node;
+	struct hda_gen_spec *spec = codec->spec;
+	hda_nid_t conn[16];
+	int n, nums, idx;
+	int type;
+	hda_nid_t nid = path->path[i];
+
+	nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
+	type = get_wcaps_type(get_wcaps(codec, nid));
+	if (type == AC_WID_PIN ||
+	    (type == AC_WID_AUD_IN && codec->single_adc_amp)) {
+		nums = 1;
+		idx = 0;
+	} else
+		idx = path->idx[i];
+
+	for (n = 0; n < nums; n++)
+		init_amp(codec, nid, HDA_INPUT, n);
+
+	if (is_ctl_associated(codec, nid, HDA_INPUT, idx))
+		return;
 
-	/*
-	 * Look for the output PIN widget
+	/* here is a little bit tricky in comparison with activate_amp_out();
+	 * when aa-mixer is available, we need to enable the path as well
 	 */
-	/* first, look for the line-out pin */
-	node = parse_output_jack(codec, spec, AC_JACK_LINE_OUT);
-	if (node) /* found, remember the PIN node */
-		spec->out_pin_node[0] = node;
-	else {
-		/* if no line-out is found, try speaker out */
-		node = parse_output_jack(codec, spec, AC_JACK_SPEAKER);
-		if (node)
-			spec->out_pin_node[0] = node;
-	}
-	/* look for the HP-out pin */
-	node = parse_output_jack(codec, spec, AC_JACK_HP_OUT);
-	if (node) {
-		if (! spec->out_pin_node[0])
-			spec->out_pin_node[0] = node;
-		else
-			spec->out_pin_node[1] = node;
+	for (n = 0; n < nums; n++) {
+		if (n != idx && (!add_aamix || conn[n] != spec->mixer_nid))
+			continue;
+		activate_amp(codec, nid, HDA_INPUT, n, enable);
 	}
+}
 
-	if (! spec->out_pin_node[0]) {
-		/* no line-out or HP pins found,
-		 * then choose for the first output pin
-		 */
-		spec->out_pin_node[0] = parse_output_jack(codec, spec, -1);
-		if (! spec->out_pin_node[0])
-			snd_printd("hda_generic: no proper output path found\n");
+/* activate or deactivate the given path
+ * if @add_aamix is set, enable the input from aa-mix NID as well (if any)
+ */
+void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path,
+			   bool enable, bool add_aamix)
+{
+	int i;
+
+	if (!enable)
+		path->active = false;
+
+	for (i = path->depth - 1; i >= 0; i--) {
+		if (enable && path->multi[i])
+			snd_hda_codec_write_cache(codec, path->path[i], 0,
+					    AC_VERB_SET_CONNECT_SEL,
+					    path->idx[i]);
+		if (has_amp_in(codec, path, i))
+			activate_amp_in(codec, path, i, enable, add_aamix);
+		if (has_amp_out(codec, path, i))
+			activate_amp_out(codec, path, i, enable);
 	}
 
-	return 0;
+	if (enable)
+		path->active = true;
 }
+EXPORT_SYMBOL_HDA(snd_hda_activate_path);
+
 
 /*
- * input MUX
+ * Helper functions for creating mixer ctl elements
  */
 
-/* control callbacks */
-static int capture_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct hda_gspec *spec = codec->spec;
-	return snd_hda_input_mux_info(&spec->input_mux, uinfo);
-}
+enum {
+	HDA_CTL_WIDGET_VOL,
+	HDA_CTL_WIDGET_MUTE,
+	HDA_CTL_BIND_MUTE,
+	HDA_CTL_BIND_VOL,
+	HDA_CTL_BIND_SW,
+};
+static const struct snd_kcontrol_new control_templates[] = {
+	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
+	HDA_CODEC_MUTE(NULL, 0, 0, 0),
+	HDA_BIND_MUTE(NULL, 0, 0, 0),
+	HDA_BIND_VOL(NULL, 0),
+	HDA_BIND_SW(NULL, 0),
+};
 
-static int capture_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+/* add dynamic controls from template */
+static int add_control(struct hda_gen_spec *spec, int type, const char *name,
+		       int cidx, unsigned long val)
 {
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct hda_gspec *spec = codec->spec;
+	struct snd_kcontrol_new *knew;
 
-	ucontrol->value.enumerated.item[0] = spec->cur_cap_src;
+	knew = add_kctl(spec, name, &control_templates[type]);
+	if (!knew)
+		return -ENOMEM;
+	knew->index = cidx;
+	if (get_amp_nid_(val))
+		knew->subdevice = HDA_SUBDEV_AMP_FLAG;
+	knew->private_value = val;
 	return 0;
 }
 
-static int capture_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int add_control_with_pfx(struct hda_gen_spec *spec, int type,
+				const char *pfx, const char *dir,
+				const char *sfx, int cidx, unsigned long val)
 {
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct hda_gspec *spec = codec->spec;
-	return snd_hda_input_mux_put(codec, &spec->input_mux, ucontrol,
-				     spec->adc_node->nid, &spec->cur_cap_src);
+	char name[32];
+	snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx);
+	return add_control(spec, type, name, cidx, val);
 }
 
-/*
- * return the string name of the given input PIN widget
- */
-static const char *get_input_type(struct hda_gnode *node, unsigned int *pinctl)
-{
-	unsigned int location = defcfg_location(node);
-	switch (defcfg_type(node)) {
-	case AC_JACK_LINE_IN:
-		if ((location & 0x0f) == AC_JACK_LOC_FRONT)
-			return "Front Line";
-		return "Line";
-	case AC_JACK_CD:
-#if 0
-		if (pinctl)
-			*pinctl |= AC_PINCTL_VREF_GRD;
-#endif
-		return "CD";
-	case AC_JACK_AUX:
-		if ((location & 0x0f) == AC_JACK_LOC_FRONT)
-			return "Front Aux";
-		return "Aux";
-	case AC_JACK_MIC_IN:
-		if (pinctl &&
-		    (node->pin_caps &
-		     (AC_PINCAP_VREF_80 << AC_PINCAP_VREF_SHIFT)))
-			*pinctl |= AC_PINCTL_VREF_80;
-		if ((location & 0x0f) == AC_JACK_LOC_FRONT)
-			return "Front Mic";
-		return "Mic";
-	case AC_JACK_SPDIF_IN:
-		return "SPDIF";
-	case AC_JACK_DIG_OTHER_IN:
-		return "Digital";
+#define add_pb_vol_ctrl(spec, type, pfx, val)			\
+	add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val)
+#define add_pb_sw_ctrl(spec, type, pfx, val)			\
+	add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val)
+#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val)			\
+	add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val)
+#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val)			\
+	add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val)
+
+static int add_vol_ctl(struct hda_codec *codec, const char *pfx, int cidx,
+		       unsigned int chs, struct nid_path *path)
+{
+	unsigned int val;
+	if (!path)
+		return 0;
+	val = path->ctls[NID_PATH_VOL_CTL];
+	if (!val)
+		return 0;
+	val = amp_val_replace_channels(val, chs);
+	return __add_pb_vol_ctrl(codec->spec, HDA_CTL_WIDGET_VOL, pfx, cidx, val);
+}
+
+/* return the channel bits suitable for the given path->ctls[] */
+static int get_default_ch_nums(struct hda_codec *codec, struct nid_path *path,
+			       int type)
+{
+	int chs = 1; /* mono (left only) */
+	if (path) {
+		hda_nid_t nid = get_amp_nid_(path->ctls[type]);
+		if (nid && (get_wcaps(codec, nid) & AC_WCAP_STEREO))
+			chs = 3; /* stereo */
 	}
-	return NULL;
+	return chs;
 }
 
-/*
- * parse the nodes recursively until reach to the input PIN
- *
- * returns 0 if not found, 1 if found, or a negative error code.
+static int add_stereo_vol(struct hda_codec *codec, const char *pfx, int cidx,
+			  struct nid_path *path)
+{
+	int chs = get_default_ch_nums(codec, path, NID_PATH_VOL_CTL);
+	return add_vol_ctl(codec, pfx, cidx, chs, path);
+}
+
+/* create a mute-switch for the given mixer widget;
+ * if it has multiple sources (e.g. DAC and loopback), create a bind-mute
  */
-static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
-			       struct hda_gnode *node, int idx)
+static int add_sw_ctl(struct hda_codec *codec, const char *pfx, int cidx,
+		      unsigned int chs, struct nid_path *path)
 {
-	int i, err;
-	unsigned int pinctl;
-	const char *type;
+	unsigned int val;
+	int type = HDA_CTL_WIDGET_MUTE;
 
-	if (node->checked)
+	if (!path)
 		return 0;
-
-	node->checked = 1;
-	if (node->type != AC_WID_PIN) {
-		for (i = 0; i < node->nconns; i++) {
-			struct hda_gnode *child;
-			child = hda_get_node(spec, node->conn_list[i]);
-			if (! child)
-				continue;
-			err = parse_adc_sub_nodes(codec, spec, child, idx);
-			if (err < 0)
-				return err;
-			if (err > 0) {
-				/* found one,
-				 * select the path, unmute both input and output
-				 */
-				if (node->nconns > 1)
-					select_input_connection(codec, node, i);
-				unmute_input(codec, node, i);
-				unmute_output(codec, node);
-				return err;
-			}
-		}
+	val = path->ctls[NID_PATH_MUTE_CTL];
+	if (!val)
 		return 0;
+	val = amp_val_replace_channels(val, chs);
+	if (get_amp_direction_(val) == HDA_INPUT) {
+		hda_nid_t nid = get_amp_nid_(val);
+		int nums = snd_hda_get_num_conns(codec, nid);
+		if (nums > 1) {
+			type = HDA_CTL_BIND_MUTE;
+			val |= nums << 19;
+		}
 	}
+	return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val);
+}
 
-	/* input capable? */
-	if (! (node->pin_caps & AC_PINCAP_IN))
-		return 0;
+static int add_stereo_sw(struct hda_codec *codec, const char *pfx,
+				  int cidx, struct nid_path *path)
+{
+	int chs = get_default_ch_nums(codec, path, NID_PATH_MUTE_CTL);
+	return add_sw_ctl(codec, pfx, cidx, chs, path);
+}
 
-	if (defcfg_port_conn(node) == AC_JACK_PORT_NONE)
-		return 0; /* unconnected */
+static const char * const channel_name[4] = {
+	"Front", "Surround", "CLFE", "Side"
+};
 
-	if (node->wid_caps & AC_WCAP_DIGITAL)
-		return 0; /* skip SPDIF */
+/* give some appropriate ctl name prefix for the given line out channel */
+static const char *get_line_out_pfx(struct hda_gen_spec *spec, int ch,
+				    bool can_be_master, int *index)
+{
+	struct auto_pin_cfg *cfg = &spec->autocfg;
 
-	if (spec->input_mux.num_items >= HDA_MAX_NUM_INPUTS) {
-		snd_printk(KERN_ERR "hda_generic: Too many items for capture\n");
-		return -EINVAL;
-	}
+	*index = 0;
+	if (cfg->line_outs == 1 && !spec->multi_ios &&
+	    !cfg->hp_outs && !cfg->speaker_outs && can_be_master)
+		return spec->vmaster_mute.hook ? "PCM" : "Master";
 
-	pinctl = AC_PINCTL_IN_EN;
-	/* create a proper capture source label */
-	type = get_input_type(node, &pinctl);
-	if (! type) {
-		/* input as default? */
-		if (! (node->pin_ctl & AC_PINCTL_IN_EN))
-			return 0;
-		type = "Input";
+	/* if there is really a single DAC used in the whole output paths,
+	 * use it master (or "PCM" if a vmaster hook is present)
+	 */
+	if (spec->multiout.num_dacs == 1 && !spec->mixer_nid &&
+	    !spec->multiout.hp_out_nid[0] && !spec->multiout.extra_out_nid[0])
+		return spec->vmaster_mute.hook ? "PCM" : "Master";
+
+	switch (cfg->line_out_type) {
+	case AUTO_PIN_SPEAKER_OUT:
+		if (cfg->line_outs == 1)
+			return "Speaker";
+		if (cfg->line_outs == 2)
+			return ch ? "Bass Speaker" : "Speaker";
+		break;
+	case AUTO_PIN_HP_OUT:
+		/* for multi-io case, only the primary out */
+		if (ch && spec->multi_ios)
+			break;
+		*index = ch;
+		return "Headphone";
+	default:
+		if (cfg->line_outs == 1 && !spec->multi_ios)
+			return "PCM";
+		break;
+	}
+	if (ch >= ARRAY_SIZE(channel_name)) {
+		snd_BUG();
+		return "PCM";
 	}
-	snd_hda_add_imux_item(&spec->input_mux, type, idx, NULL);
-
-	/* unmute the PIN external input */
-	unmute_input(codec, node, 0); /* index = 0? */
-	/* set PIN-In enable */
-	snd_hda_codec_write_cache(codec, node->nid, 0,
-				  AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl);
 
-	return 1; /* found */
+	return channel_name[ch];
 }
 
 /*
- * parse input
+ * Parse output paths
  */
-static int parse_input_path(struct hda_codec *codec, struct hda_gnode *adc_node)
+
+/* badness definition */
+enum {
+	/* No primary DAC is found for the main output */
+	BAD_NO_PRIMARY_DAC = 0x10000,
+	/* No DAC is found for the extra output */
+	BAD_NO_DAC = 0x4000,
+	/* No possible multi-ios */
+	BAD_MULTI_IO = 0x103,
+	/* No individual DAC for extra output */
+	BAD_NO_EXTRA_DAC = 0x102,
+	/* No individual DAC for extra surrounds */
+	BAD_NO_EXTRA_SURR_DAC = 0x101,
+	/* Primary DAC shared with main surrounds */
+	BAD_SHARED_SURROUND = 0x100,
+	/* Primary DAC shared with main CLFE */
+	BAD_SHARED_CLFE = 0x10,
+	/* Primary DAC shared with extra surrounds */
+	BAD_SHARED_EXTRA_SURROUND = 0x10,
+	/* Volume widget is shared */
+	BAD_SHARED_VOL = 0x10,
+};
+
+/* look for widgets in the path between the given NIDs appropriate for
+ * volume and mute controls, and assign the values to ctls[].
+ *
+ * When no appropriate widget is found in the path, the badness value
+ * is incremented depending on the situation.  The function returns the
+ * total badness for both volume and mute controls.
+ */
+static int assign_out_path_ctls(struct hda_codec *codec, hda_nid_t pin,
+				hda_nid_t dac)
 {
-	struct hda_gspec *spec = codec->spec;
-	struct hda_gnode *node;
-	int i, err;
+	struct nid_path *path = snd_hda_get_nid_path(codec, dac, pin);
+	hda_nid_t nid;
+	unsigned int val;
+	int badness = 0;
+
+	if (!path)
+		return BAD_SHARED_VOL * 2;
+	nid = look_for_out_vol_nid(codec, path);
+	if (nid) {
+		val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+		if (is_ctl_used(codec, val, NID_PATH_VOL_CTL))
+			badness += BAD_SHARED_VOL;
+		else
+			path->ctls[NID_PATH_VOL_CTL] = val;
+	} else
+		badness += BAD_SHARED_VOL;
+	nid = look_for_out_mute_nid(codec, path);
+	if (nid) {
+		unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid));
+		if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT ||
+		    nid_has_mute(codec, nid, HDA_OUTPUT))
+			val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+		else
+			val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT);
+		if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL))
+			badness += BAD_SHARED_VOL;
+		else
+			path->ctls[NID_PATH_MUTE_CTL] = val;
+	} else
+		badness += BAD_SHARED_VOL;
+	return badness;
+}
 
-	snd_printdd("AUD_IN = %x\n", adc_node->nid);
-	clear_check_flags(spec);
+struct badness_table {
+	int no_primary_dac;	/* no primary DAC */
+	int no_dac;		/* no secondary DACs */
+	int shared_primary;	/* primary DAC is shared with main output */
+	int shared_surr;	/* secondary DAC shared with main or primary */
+	int shared_clfe;	/* third DAC shared with main or primary */
+	int shared_surr_main;	/* secondary DAC sahred with main/DAC0 */
+};
 
-	// awk added - fixed no recording due to muted widget
-	unmute_input(codec, adc_node, 0);
-	
-	/*
-	 * check each connection of the ADC
-	 * if it reaches to a proper input PIN, add the path as the
-	 * input path.
-	 */
-	/* first, check the direct connections to PIN widgets */
-	for (i = 0; i < adc_node->nconns; i++) {
-		node = hda_get_node(spec, adc_node->conn_list[i]);
-		if (node && node->type == AC_WID_PIN) {
-			err = parse_adc_sub_nodes(codec, spec, node, i);
-			if (err < 0)
-				return err;
+static struct badness_table main_out_badness = {
+	.no_primary_dac = BAD_NO_PRIMARY_DAC,
+	.no_dac = BAD_NO_DAC,
+	.shared_primary = BAD_NO_PRIMARY_DAC,
+	.shared_surr = BAD_SHARED_SURROUND,
+	.shared_clfe = BAD_SHARED_CLFE,
+	.shared_surr_main = BAD_SHARED_SURROUND,
+};
+
+static struct badness_table extra_out_badness = {
+	.no_primary_dac = BAD_NO_DAC,
+	.no_dac = BAD_NO_DAC,
+	.shared_primary = BAD_NO_EXTRA_DAC,
+	.shared_surr = BAD_SHARED_EXTRA_SURROUND,
+	.shared_clfe = BAD_SHARED_EXTRA_SURROUND,
+	.shared_surr_main = BAD_NO_EXTRA_SURR_DAC,
+};
+
+/* try to assign DACs to pins and return the resultant badness */
+static int try_assign_dacs(struct hda_codec *codec, int num_outs,
+			   const hda_nid_t *pins, hda_nid_t *dacs,
+			   const struct badness_table *bad)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	int i, j;
+	int badness = 0;
+	hda_nid_t dac;
+
+	if (!num_outs)
+		return 0;
+
+	for (i = 0; i < num_outs; i++) {
+		hda_nid_t pin = pins[i];
+		if (!dacs[i])
+			dacs[i] = look_for_dac(codec, pin, false);
+		if (!dacs[i] && !i) {
+			for (j = 1; j < num_outs; j++) {
+				if (is_reachable_path(codec, dacs[j], pin)) {
+					dacs[0] = dacs[j];
+					dacs[j] = 0;
+					break;
+				}
+			}
 		}
-	}
-	/* ... then check the rests, more complicated connections */
-	for (i = 0; i < adc_node->nconns; i++) {
-		node = hda_get_node(spec, adc_node->conn_list[i]);
-		if (node && node->type != AC_WID_PIN) {
-			err = parse_adc_sub_nodes(codec, spec, node, i);
-			if (err < 0)
-				return err;
+		dac = dacs[i];
+		if (!dac) {
+			if (is_reachable_path(codec, dacs[0], pin))
+				dac = dacs[0];
+			else if (cfg->line_outs > i &&
+				 is_reachable_path(codec, spec->private_dac_nids[i], pin))
+				dac = spec->private_dac_nids[i];
+			if (dac) {
+				if (!i)
+					badness += bad->shared_primary;
+				else if (i == 1)
+					badness += bad->shared_surr;
+				else
+					badness += bad->shared_clfe;
+			} else if (is_reachable_path(codec, spec->private_dac_nids[0], pin)) {
+				dac = spec->private_dac_nids[0];
+				badness += bad->shared_surr_main;
+			} else if (!i)
+				badness += bad->no_primary_dac;
+			else
+				badness += bad->no_dac;
 		}
+		if (!snd_hda_add_new_path(codec, dac, pin, 0))
+			dac = dacs[i] = 0;
+		if (dac)
+			badness += assign_out_path_ctls(codec, pin, dac);
 	}
 
-	if (! spec->input_mux.num_items)
-		return 0; /* no input path found... */
-
-	snd_printdd("[Capture Source] NID=0x%x, #SRC=%d\n", adc_node->nid, spec->input_mux.num_items);
-	for (i = 0; i < spec->input_mux.num_items; i++)
-		snd_printdd("  [%s] IDX=0x%x\n", spec->input_mux.items[i].label,
-			    spec->input_mux.items[i].index);
-
-	spec->adc_node = adc_node;
-	return 1;
+	return badness;
 }
 
-/*
- * parse input
- */
-static int parse_input(struct hda_codec *codec)
+/* return NID if the given pin has only a single connection to a certain DAC */
+static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin)
 {
-	struct hda_gspec *spec = codec->spec;
-	struct hda_gnode *node;
-	int err;
+	struct hda_gen_spec *spec = codec->spec;
+	int i;
+	hda_nid_t nid_found = 0;
 
-	/*
-	 * At first we look for an audio input widget.
-	 * If it reaches to certain input PINs, we take it as the
-	 * input path.
-	 */
-	list_for_each_entry(node, &spec->nid_list, list) {
-		if (node->wid_caps & AC_WCAP_DIGITAL)
-			continue; /* skip SPDIF */
-		if (node->type == AC_WID_AUD_IN) {
-			err = parse_input_path(codec, node);
-			if (err < 0)
-				return err;
-			else if (err > 0)
+	for (i = 0; i < spec->num_all_dacs; i++) {
+		hda_nid_t nid = spec->all_dacs[i];
+		if (!nid || is_dac_already_used(codec, nid))
+			continue;
+		if (is_reachable_path(codec, nid, pin)) {
+			if (nid_found)
 				return 0;
+			nid_found = nid;
 		}
 	}
-	snd_printd("hda_generic: no proper input path found\n");
-	return 0;
+	return nid_found;
 }
 
-#ifdef CONFIG_PM
-static void add_input_loopback(struct hda_codec *codec, hda_nid_t nid,
-			       int dir, int idx)
+/* check whether the given pin can be a multi-io pin */
+static bool can_be_multiio_pin(struct hda_codec *codec,
+			       unsigned int location, hda_nid_t nid)
 {
-	struct hda_gspec *spec = codec->spec;
-	struct hda_amp_list *p;
-
-	if (spec->num_loopbacks >= MAX_LOOPBACK_AMPS) {
-		snd_printk(KERN_ERR "hda_generic: Too many loopback ctls\n");
-		return;
-	}
-	p = &spec->loopback_list[spec->num_loopbacks++];
-	p->nid = nid;
-	p->dir = dir;
-	p->idx = idx;
-	spec->loopback.amplist = spec->loopback_list;
+	unsigned int defcfg, caps;
+
+	defcfg = snd_hda_codec_get_pincfg(codec, nid);
+	if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX)
+		return false;
+	if (location && get_defcfg_location(defcfg) != location)
+		return false;
+	caps = snd_hda_query_pin_caps(codec, nid);
+	if (!(caps & AC_PINCAP_OUT))
+		return false;
+	return true;
 }
-#else
-#define add_input_loopback(codec,nid,dir,idx)
-#endif
 
 /*
- * create mixer controls if possible
+ * multi-io helper
+ *
+ * When hardwired is set, try to fill ony hardwired pins, and returns
+ * zero if any pins are filled, non-zero if nothing found.
+ * When hardwired is off, try to fill possible input pins, and returns
+ * the badness value.
  */
-static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
-			unsigned int index, const char *type,
-			const char *dir_sfx, int is_loopback)
+static int fill_multi_ios(struct hda_codec *codec,
+			  hda_nid_t reference_pin,
+			  bool hardwired, int offset)
 {
-	char name[32];
-	int err;
-	int created = 0;
-	struct snd_kcontrol_new knew;
-
-	if (type)
-		sprintf(name, "%s %s Switch", type, dir_sfx);
-	else
-		sprintf(name, "%s Switch", dir_sfx);
-	if ((node->wid_caps & AC_WCAP_IN_AMP) &&
-	    (node->amp_in_caps & AC_AMPCAP_MUTE)) {
-		knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, index, HDA_INPUT);
-		if (is_loopback)
-			add_input_loopback(codec, node->nid, HDA_INPUT, index);
-		snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
-		err = snd_hda_ctl_add(codec, node->nid,
-					snd_ctl_new1(&knew, codec));
-		if (err < 0)
-			return err;
-		created = 1;
-	} else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
-		   (node->amp_out_caps & AC_AMPCAP_MUTE)) {
-		knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, 0, HDA_OUTPUT);
-		if (is_loopback)
-			add_input_loopback(codec, node->nid, HDA_OUTPUT, 0);
-		snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
-		err = snd_hda_ctl_add(codec, node->nid,
-					snd_ctl_new1(&knew, codec));
-		if (err < 0)
-			return err;
-		created = 1;
+	struct hda_gen_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	int type, i, j, dacs, num_pins, old_pins;
+	unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin);
+	unsigned int location = get_defcfg_location(defcfg);
+	int badness = 0;
+
+	old_pins = spec->multi_ios;
+	if (old_pins >= 2)
+		goto end_fill;
+
+	num_pins = 0;
+	for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
+		for (i = 0; i < cfg->num_inputs; i++) {
+			if (cfg->inputs[i].type != type)
+				continue;
+			if (can_be_multiio_pin(codec, location,
+					       cfg->inputs[i].pin))
+				num_pins++;
+		}
 	}
+	if (num_pins < 2)
+		goto end_fill;
 
-	if (type)
-		sprintf(name, "%s %s Volume", type, dir_sfx);
-	else
-		sprintf(name, "%s Volume", dir_sfx);
-	if ((node->wid_caps & AC_WCAP_IN_AMP) &&
-	    (node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) {
-		knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT);
-		snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
-		err = snd_hda_ctl_add(codec, node->nid,
-					snd_ctl_new1(&knew, codec));
-		if (err < 0)
-			return err;
-		created = 1;
-	} else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
-		   (node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) {
-		knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT);
-		snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
-		err = snd_hda_ctl_add(codec, node->nid,
-					snd_ctl_new1(&knew, codec));
-		if (err < 0)
-			return err;
-		created = 1;
-	}
+	dacs = spec->multiout.num_dacs;
+	for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
+		for (i = 0; i < cfg->num_inputs; i++) {
+			hda_nid_t nid = cfg->inputs[i].pin;
+			hda_nid_t dac = 0;
 
-	return created;
-}
+			if (cfg->inputs[i].type != type)
+				continue;
+			if (!can_be_multiio_pin(codec, location, nid))
+				continue;
+			for (j = 0; j < spec->multi_ios; j++) {
+				if (nid == spec->multi_io[j].pin)
+					break;
+			}
+			if (j < spec->multi_ios)
+				continue;
+
+			if (offset && offset + spec->multi_ios < dacs) {
+				dac = spec->private_dac_nids[offset + spec->multi_ios];
+				if (!is_reachable_path(codec, dac, nid))
+					dac = 0;
+			}
+			if (hardwired)
+				dac = get_dac_if_single(codec, nid);
+			else if (!dac)
+				dac = look_for_dac(codec, nid, false);
+			if (!dac) {
+				badness++;
+				continue;
+			}
+			if (!snd_hda_add_new_path(codec, dac, nid, 0)) {
+				badness++;
+				continue;
+			}
+			spec->multi_io[spec->multi_ios].pin = nid;
+			spec->multi_io[spec->multi_ios].dac = dac;
+			spec->multi_ios++;
+			if (spec->multi_ios >= 2)
+				break;
+		}
+	}
+ end_fill:
+	if (badness)
+		badness = BAD_MULTI_IO;
+	if (old_pins == spec->multi_ios) {
+		if (hardwired)
+			return 1; /* nothing found */
+		else
+			return badness; /* no badness if nothing found */
+	}
+	if (!hardwired && spec->multi_ios < 2) {
+		/* cancel newly assigned paths */
+		spec->paths.used -= spec->multi_ios - old_pins;
+		spec->multi_ios = old_pins;
+		return badness;
+	}
+
+	/* assign volume and mute controls */
+	for (i = old_pins; i < spec->multi_ios; i++)
+		badness += assign_out_path_ctls(codec, spec->multi_io[i].pin,
+						spec->multi_io[i].dac);
+
+	return badness;
+}
+
+/* map DACs for all pins in the list if they are single connections */
+static bool map_singles(struct hda_codec *codec, int outs,
+			const hda_nid_t *pins, hda_nid_t *dacs)
+{
+	int i;
+	bool found = false;
+	for (i = 0; i < outs; i++) {
+		hda_nid_t dac;
+		if (dacs[i])
+			continue;
+		dac = get_dac_if_single(codec, pins[i]);
+		if (!dac)
+			continue;
+		if (snd_hda_add_new_path(codec, dac, pins[i], 0)) {
+			dacs[i] = dac;
+			found = true;
+		}
+	}
+	return found;
+}
+
+/* fill in the dac_nids table from the parsed pin configuration */
+static int fill_and_eval_dacs(struct hda_codec *codec,
+			      bool fill_hardwired,
+			      bool fill_mio_first)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	int i, err, badness;
+
+	/* set num_dacs once to full for look_for_dac() */
+	spec->multiout.num_dacs = cfg->line_outs;
+	spec->multiout.dac_nids = spec->private_dac_nids;
+	memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids));
+	memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid));
+	memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid));
+	spec->multi_ios = 0;
+	snd_array_free(&spec->paths);
+	badness = 0;
+
+	/* fill hard-wired DACs first */
+	if (fill_hardwired) {
+		bool mapped;
+		do {
+			mapped = map_singles(codec, cfg->line_outs,
+					     cfg->line_out_pins,
+					     spec->private_dac_nids);
+			mapped |= map_singles(codec, cfg->hp_outs,
+					      cfg->hp_pins,
+					      spec->multiout.hp_out_nid);
+			mapped |= map_singles(codec, cfg->speaker_outs,
+					      cfg->speaker_pins,
+					      spec->multiout.extra_out_nid);
+			if (fill_mio_first && cfg->line_outs == 1 &&
+			    cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+				err = fill_multi_ios(codec, cfg->line_out_pins[0], true, 0);
+				if (!err)
+					mapped = true;
+			}
+		} while (mapped);
+	}
+
+	badness += try_assign_dacs(codec, cfg->line_outs, cfg->line_out_pins,
+				   spec->private_dac_nids,
+				   &main_out_badness);
+
+	/* re-count num_dacs and squash invalid entries */
+	spec->multiout.num_dacs = 0;
+	for (i = 0; i < cfg->line_outs; i++) {
+		if (spec->private_dac_nids[i])
+			spec->multiout.num_dacs++;
+		else {
+			memmove(spec->private_dac_nids + i,
+				spec->private_dac_nids + i + 1,
+				sizeof(hda_nid_t) * (cfg->line_outs - i - 1));
+			spec->private_dac_nids[cfg->line_outs - 1] = 0;
+		}
+	}
+
+	if (fill_mio_first &&
+	    cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+		/* try to fill multi-io first */
+		err = fill_multi_ios(codec, cfg->line_out_pins[0], false, 0);
+		if (err < 0)
+			return err;
+		/* we don't count badness at this stage yet */
+	}
+
+	if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
+		err = try_assign_dacs(codec, cfg->hp_outs, cfg->hp_pins,
+				      spec->multiout.hp_out_nid,
+				      &extra_out_badness);
+		if (err < 0)
+			return err;
+		badness += err;
+	}
+	if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+		err = try_assign_dacs(codec, cfg->speaker_outs,
+				      cfg->speaker_pins,
+				      spec->multiout.extra_out_nid,
+					 &extra_out_badness);
+		if (err < 0)
+			return err;
+		badness += err;
+	}
+	if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+		err = fill_multi_ios(codec, cfg->line_out_pins[0], false, 0);
+		if (err < 0)
+			return err;
+		badness += err;
+	}
+	if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
+		/* try multi-ios with HP + inputs */
+		int offset = 0;
+		if (cfg->line_outs >= 3)
+			offset = 1;
+		err = fill_multi_ios(codec, cfg->hp_pins[0], false, offset);
+		if (err < 0)
+			return err;
+		badness += err;
+	}
+
+	if (spec->multi_ios == 2) {
+		for (i = 0; i < 2; i++)
+			spec->private_dac_nids[spec->multiout.num_dacs++] =
+				spec->multi_io[i].dac;
+		spec->ext_channel_count = 2;
+	} else if (spec->multi_ios) {
+		spec->multi_ios = 0;
+		badness += BAD_MULTI_IO;
+	}
+
+	return badness;
+}
+
+#define DEBUG_BADNESS
+
+#ifdef DEBUG_BADNESS
+#define debug_badness	snd_printdd
+#else
+#define debug_badness(...)
+#endif
+
+static void debug_show_configs(struct hda_gen_spec *spec, struct auto_pin_cfg *cfg)
+{
+	debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
+		      cfg->line_out_pins[0], cfg->line_out_pins[1],
+		      cfg->line_out_pins[2], cfg->line_out_pins[2],
+		      spec->multiout.dac_nids[0],
+		      spec->multiout.dac_nids[1],
+		      spec->multiout.dac_nids[2],
+		      spec->multiout.dac_nids[3]);
+	if (spec->multi_ios > 0)
+		debug_badness("multi_ios(%d) = %x/%x : %x/%x\n",
+			      spec->multi_ios,
+			      spec->multi_io[0].pin, spec->multi_io[1].pin,
+			      spec->multi_io[0].dac, spec->multi_io[1].dac);
+	debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
+		      cfg->hp_pins[0], cfg->hp_pins[1],
+		      cfg->hp_pins[2], cfg->hp_pins[2],
+		      spec->multiout.hp_out_nid[0],
+		      spec->multiout.hp_out_nid[1],
+		      spec->multiout.hp_out_nid[2],
+		      spec->multiout.hp_out_nid[3]);
+	debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
+		      cfg->speaker_pins[0], cfg->speaker_pins[1],
+		      cfg->speaker_pins[2], cfg->speaker_pins[3],
+		      spec->multiout.extra_out_nid[0],
+		      spec->multiout.extra_out_nid[1],
+		      spec->multiout.extra_out_nid[2],
+		      spec->multiout.extra_out_nid[3]);
+}
+
+/* find all available DACs of the codec */
+static void fill_all_dac_nids(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	int i;
+	hda_nid_t nid = codec->start_nid;
+
+	spec->num_all_dacs = 0;
+	memset(spec->all_dacs, 0, sizeof(spec->all_dacs));
+	for (i = 0; i < codec->num_nodes; i++, nid++) {
+		if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_OUT)
+			continue;
+		if (spec->num_all_dacs >= ARRAY_SIZE(spec->all_dacs)) {
+			snd_printk(KERN_ERR "hda: Too many DACs!\n");
+			break;
+		}
+		spec->all_dacs[spec->num_all_dacs++] = nid;
+	}
+}
+
+static int parse_output_paths(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	struct auto_pin_cfg *best_cfg;
+	int best_badness = INT_MAX;
+	int badness;
+	bool fill_hardwired = true, fill_mio_first = true;
+	bool best_wired = true, best_mio = true;
+	bool hp_spk_swapped = false;
+
+	fill_all_dac_nids(codec);
+
+	best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL);
+	if (!best_cfg)
+		return -ENOMEM;
+	*best_cfg = *cfg;
+
+	for (;;) {
+		badness = fill_and_eval_dacs(codec, fill_hardwired,
+					     fill_mio_first);
+		if (badness < 0) {
+			kfree(best_cfg);
+			return badness;
+		}
+		debug_badness("==> lo_type=%d, wired=%d, mio=%d, badness=0x%x\n",
+			      cfg->line_out_type, fill_hardwired, fill_mio_first,
+			      badness);
+		debug_show_configs(spec, cfg);
+		if (badness < best_badness) {
+			best_badness = badness;
+			*best_cfg = *cfg;
+			best_wired = fill_hardwired;
+			best_mio = fill_mio_first;
+		}
+		if (!badness)
+			break;
+		fill_mio_first = !fill_mio_first;
+		if (!fill_mio_first)
+			continue;
+		fill_hardwired = !fill_hardwired;
+		if (!fill_hardwired)
+			continue;
+		if (hp_spk_swapped)
+			break;
+		hp_spk_swapped = true;
+		if (cfg->speaker_outs > 0 &&
+		    cfg->line_out_type == AUTO_PIN_HP_OUT) {
+			cfg->hp_outs = cfg->line_outs;
+			memcpy(cfg->hp_pins, cfg->line_out_pins,
+			       sizeof(cfg->hp_pins));
+			cfg->line_outs = cfg->speaker_outs;
+			memcpy(cfg->line_out_pins, cfg->speaker_pins,
+			       sizeof(cfg->speaker_pins));
+			cfg->speaker_outs = 0;
+			memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
+			cfg->line_out_type = AUTO_PIN_SPEAKER_OUT;
+			fill_hardwired = true;
+			continue;
+		}
+		if (cfg->hp_outs > 0 &&
+		    cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
+			cfg->speaker_outs = cfg->line_outs;
+			memcpy(cfg->speaker_pins, cfg->line_out_pins,
+			       sizeof(cfg->speaker_pins));
+			cfg->line_outs = cfg->hp_outs;
+			memcpy(cfg->line_out_pins, cfg->hp_pins,
+			       sizeof(cfg->hp_pins));
+			cfg->hp_outs = 0;
+			memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
+			cfg->line_out_type = AUTO_PIN_HP_OUT;
+			fill_hardwired = true;
+			continue;
+		}
+		break;
+	}
+
+	if (badness) {
+		*cfg = *best_cfg;
+		fill_and_eval_dacs(codec, best_wired, best_mio);
+	}
+	debug_badness("==> Best config: lo_type=%d, wired=%d, mio=%d\n",
+		      cfg->line_out_type, best_wired, best_mio);
+	debug_show_configs(spec, cfg);
+
+	if (cfg->line_out_pins[0]) {
+		struct nid_path *path;
+		path = snd_hda_get_nid_path(codec,
+					    spec->multiout.dac_nids[0],
+					    cfg->line_out_pins[0]);
+		if (path)
+			spec->vmaster_nid = look_for_out_vol_nid(codec, path);
+	}
+
+	kfree(best_cfg);
+	return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int create_multi_out_ctls(struct hda_codec *codec,
+				 const struct auto_pin_cfg *cfg)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	int i, err, noutputs;
+
+	noutputs = cfg->line_outs;
+	if (spec->multi_ios > 0 && cfg->line_outs < 3)
+		noutputs += spec->multi_ios;
+
+	for (i = 0; i < noutputs; i++) {
+		const char *name;
+		int index;
+		hda_nid_t dac, pin;
+		struct nid_path *path;
+
+		dac = spec->multiout.dac_nids[i];
+		if (!dac)
+			continue;
+		if (i >= cfg->line_outs) {
+			pin = spec->multi_io[i - 1].pin;
+			index = 0;
+			name = channel_name[i];
+		} else {
+			pin = cfg->line_out_pins[i];
+			name = get_line_out_pfx(spec, i, true, &index);
+		}
+
+		path = snd_hda_get_nid_path(codec, dac, pin);
+		if (!path)
+			continue;
+		if (!name || !strcmp(name, "CLFE")) {
+			/* Center/LFE */
+			err = add_vol_ctl(codec, "Center", 0, 1, path);
+			if (err < 0)
+				return err;
+			err = add_vol_ctl(codec, "LFE", 0, 2, path);
+			if (err < 0)
+				return err;
+			err = add_sw_ctl(codec, "Center", 0, 1, path);
+			if (err < 0)
+				return err;
+			err = add_sw_ctl(codec, "LFE", 0, 2, path);
+			if (err < 0)
+				return err;
+		} else {
+			err = add_stereo_vol(codec, name, index, path);
+			if (err < 0)
+				return err;
+			err = add_stereo_sw(codec, name, index, path);
+			if (err < 0)
+				return err;
+		}
+	}
+	return 0;
+}
+
+static int create_extra_out(struct hda_codec *codec, hda_nid_t pin,
+			    hda_nid_t dac, const char *pfx, int cidx)
+{
+	struct nid_path *path;
+	int err;
+
+	path = snd_hda_get_nid_path(codec, dac, pin);
+	if (!path)
+		return 0;
+	/* bind volume control will be created in the case of dac = 0 */
+	if (dac) {
+		err = add_stereo_vol(codec, pfx, cidx, path);
+		if (err < 0)
+			return err;
+	}
+	err = add_stereo_sw(codec, pfx, cidx, path);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+/* add playback controls for speaker and HP outputs */
+static int create_extra_outs(struct hda_codec *codec, int num_pins,
+			     const hda_nid_t *pins, const hda_nid_t *dacs,
+			     const char *pfx)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	struct hda_bind_ctls *ctl;
+	char name[32];
+	int i, n, err;
+
+	if (!num_pins || !pins[0])
+		return 0;
+
+	if (num_pins == 1) {
+		hda_nid_t dac = *dacs;
+		if (!dac)
+			dac = spec->multiout.dac_nids[0];
+		return create_extra_out(codec, *pins, dac, pfx, 0);
+	}
+
+	for (i = 0; i < num_pins; i++) {
+		hda_nid_t dac;
+		if (dacs[num_pins - 1])
+			dac = dacs[i]; /* with individual volumes */
+		else
+			dac = 0;
+		if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) {
+			err = create_extra_out(codec, pins[i], dac,
+					       "Bass Speaker", 0);
+		} else if (num_pins >= 3) {
+			snprintf(name, sizeof(name), "%s %s",
+				 pfx, channel_name[i]);
+			err = create_extra_out(codec, pins[i], dac, name, 0);
+		} else {
+			err = create_extra_out(codec, pins[i], dac, pfx, i);
+		}
+		if (err < 0)
+			return err;
+	}
+	if (dacs[num_pins - 1])
+		return 0;
+
+	/* Let's create a bind-controls for volumes */
+	ctl = new_bind_ctl(codec, num_pins, &snd_hda_bind_vol);
+	if (!ctl)
+		return -ENOMEM;
+	n = 0;
+	for (i = 0; i < num_pins; i++) {
+		hda_nid_t vol;
+		struct nid_path *path;
+		if (!pins[i] || !dacs[i])
+			continue;
+		path = snd_hda_get_nid_path(codec, dacs[i], pins[i]);
+		if (!path)
+			continue;
+		vol = look_for_out_vol_nid(codec, path);
+		if (vol)
+			ctl->values[n++] =
+				HDA_COMPOSE_AMP_VAL(vol, 3, 0, HDA_OUTPUT);
+	}
+	if (n) {
+		snprintf(name, sizeof(name), "%s Playback Volume", pfx);
+		err = add_control(spec, HDA_CTL_BIND_VOL, name, 0, (long)ctl);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+static int create_hp_out_ctls(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	return create_extra_outs(codec, spec->autocfg.hp_outs,
+				 spec->autocfg.hp_pins,
+				 spec->multiout.hp_out_nid,
+				 "Headphone");
+}
+
+static int create_speaker_out_ctls(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	return create_extra_outs(codec, spec->autocfg.speaker_outs,
+				 spec->autocfg.speaker_pins,
+				 spec->multiout.extra_out_nid,
+				 "Speaker");
+}
+
+/*
+ * channel mode enum control
+ */
+
+static int ch_mode_info(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_info *uinfo)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct hda_gen_spec *spec = codec->spec;
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = spec->multi_ios + 1;
+	if (uinfo->value.enumerated.item > spec->multi_ios)
+		uinfo->value.enumerated.item = spec->multi_ios;
+	sprintf(uinfo->value.enumerated.name, "%dch",
+		(uinfo->value.enumerated.item + 1) * 2);
+	return 0;
+}
+
+static int ch_mode_get(struct snd_kcontrol *kcontrol,
+		       struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct hda_gen_spec *spec = codec->spec;
+	ucontrol->value.enumerated.item[0] = (spec->ext_channel_count - 1) / 2;
+	return 0;
+}
+
+static int set_multi_io(struct hda_codec *codec, int idx, bool output)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	hda_nid_t nid = spec->multi_io[idx].pin;
+	struct nid_path *path;
+
+	path = snd_hda_get_nid_path(codec, spec->multi_io[idx].dac, nid);
+	if (!path)
+		return -EINVAL;
+
+	if (path->active == output)
+		return 0;
+
+	if (output) {
+		snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT);
+		snd_hda_activate_path(codec, path, true, true);
+	} else {
+		snd_hda_activate_path(codec, path, false, true);
+		snd_hda_set_pin_ctl_cache(codec, nid,
+					  spec->multi_io[idx].ctl_in);
+	}
+	return 0;
+}
+
+static int ch_mode_put(struct snd_kcontrol *kcontrol,
+		       struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct hda_gen_spec *spec = codec->spec;
+	int i, ch;
+
+	ch = ucontrol->value.enumerated.item[0];
+	if (ch < 0 || ch > spec->multi_ios)
+		return -EINVAL;
+	if (ch == (spec->ext_channel_count - 1) / 2)
+		return 0;
+	spec->ext_channel_count = (ch + 1) * 2;
+	for (i = 0; i < spec->multi_ios; i++)
+		set_multi_io(codec, i, i < ch);
+	spec->multiout.max_channels = max(spec->ext_channel_count,
+					  spec->const_channel_count);
+	if (spec->need_dac_fix)
+		spec->multiout.num_dacs = spec->multiout.max_channels / 2;
+	return 1;
+}
+
+static const struct snd_kcontrol_new channel_mode_enum = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Channel Mode",
+	.info = ch_mode_info,
+	.get = ch_mode_get,
+	.put = ch_mode_put,
+};
+
+static int create_multi_channel_mode(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+
+	if (spec->multi_ios > 0) {
+		if (!add_kctl(spec, NULL, &channel_mode_enum))
+			return -ENOMEM;
+	}
+	return 0;
+}
+
+/*
+ * shared headphone/mic handling
+ */
+
+static void call_update_outputs(struct hda_codec *codec);
+
+/* for shared I/O, change the pin-control accordingly */
+static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	unsigned int val;
+	hda_nid_t pin = spec->autocfg.inputs[1].pin;
+	/* NOTE: this assumes that there are only two inputs, the
+	 * first is the real internal mic and the second is HP/mic jack.
+	 */
+
+	val = snd_hda_get_default_vref(codec, pin);
+
+	/* This pin does not have vref caps - let's enable vref on pin 0x18
+	   instead, as suggested by Realtek */
+	if (val == AC_PINCTL_VREF_HIZ && spec->shared_mic_vref_pin) {
+		const hda_nid_t vref_pin = spec->shared_mic_vref_pin;
+		unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin);
+		if (vref_val != AC_PINCTL_VREF_HIZ)
+			snd_hda_set_pin_ctl(codec, vref_pin, PIN_IN | (set_as_mic ? vref_val : 0));
+	}
+
+	val = set_as_mic ? val | PIN_IN : PIN_HP;
+	snd_hda_set_pin_ctl(codec, pin, val);
+
+	spec->automute_speaker = !set_as_mic;
+	call_update_outputs(codec);
+}
+
+/* create a shared input with the headphone out */
+static int create_shared_input(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	unsigned int defcfg;
+	hda_nid_t nid;
+
+	/* only one internal input pin? */
+	if (cfg->num_inputs != 1)
+		return 0;
+	defcfg = snd_hda_codec_get_pincfg(codec, cfg->inputs[0].pin);
+	if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT)
+		return 0;
+
+	if (cfg->hp_outs == 1 && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+		nid = cfg->hp_pins[0]; /* OK, we have a single HP-out */
+	else if (cfg->line_outs == 1 && cfg->line_out_type == AUTO_PIN_HP_OUT)
+		nid = cfg->line_out_pins[0]; /* OK, we have a single line-out */
+	else
+		return 0; /* both not available */
+
+	if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_IN))
+		return 0; /* no input */
+
+	cfg->inputs[1].pin = nid;
+	cfg->inputs[1].type = AUTO_PIN_MIC;
+	cfg->num_inputs = 2;
+	spec->shared_mic_hp = 1;
+	snd_printdd("hda-codec: Enable shared I/O jack on NID 0x%x\n", nid);
+	return 0;
+}
+
+
+/*
+ * Parse input paths
+ */
+
+#ifdef CONFIG_PM
+/* add the powersave loopback-list entry */
+static void add_loopback_list(struct hda_gen_spec *spec, hda_nid_t mix, int idx)
+{
+	struct hda_amp_list *list;
+
+	if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
+		return;
+	list = spec->loopback_list + spec->num_loopbacks;
+	list->nid = mix;
+	list->dir = HDA_INPUT;
+	list->idx = idx;
+	spec->num_loopbacks++;
+	spec->loopback.amplist = spec->loopback_list;
+}
+#else
+#define add_loopback_list(spec, mix, idx) /* NOP */
+#endif
+
+/* create input playback/capture controls for the given pin */
+static int new_analog_input(struct hda_codec *codec, hda_nid_t pin,
+			    const char *ctlname, int ctlidx,
+			    hda_nid_t mix_nid)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	struct nid_path *path;
+	unsigned int val;
+	int err, idx;
+
+	if (!nid_has_volume(codec, mix_nid, HDA_INPUT) &&
+	    !nid_has_mute(codec, mix_nid, HDA_INPUT))
+		return 0; /* no need for analog loopback */
+
+	path = snd_hda_add_new_path(codec, pin, mix_nid, 2);
+	if (!path)
+		return -EINVAL;
+
+	idx = path->idx[path->depth - 1];
+	if (nid_has_volume(codec, mix_nid, HDA_INPUT)) {
+		val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT);
+		err = __add_pb_vol_ctrl(spec, HDA_CTL_WIDGET_VOL, ctlname, ctlidx, val);
+		if (err < 0)
+			return err;
+		path->ctls[NID_PATH_VOL_CTL] = val;
+	}
+
+	if (nid_has_mute(codec, mix_nid, HDA_INPUT)) {
+		val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT);
+		err = __add_pb_sw_ctrl(spec, HDA_CTL_WIDGET_MUTE, ctlname, ctlidx, val);
+		if (err < 0)
+			return err;
+		path->ctls[NID_PATH_MUTE_CTL] = val;
+	}
+
+	path->active = true;
+	add_loopback_list(spec, mix_nid, idx);
+	return 0;
+}
+
+static int is_input_pin(struct hda_codec *codec, hda_nid_t nid)
+{
+	unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
+	return (pincap & AC_PINCAP_IN) != 0;
+}
+
+/* Parse the codec tree and retrieve ADCs */
+static int fill_adc_nids(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	hda_nid_t nid;
+	hda_nid_t *adc_nids = spec->adc_nids;
+	int max_nums = ARRAY_SIZE(spec->adc_nids);
+	int i, nums = 0;
+
+	nid = codec->start_nid;
+	for (i = 0; i < codec->num_nodes; i++, nid++) {
+		unsigned int caps = get_wcaps(codec, nid);
+		int type = get_wcaps_type(caps);
+
+		if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL))
+			continue;
+		adc_nids[nums] = nid;
+		if (++nums >= max_nums)
+			break;
+	}
+	spec->num_adc_nids = nums;
+	return nums;
+}
+
+/* filter out invalid adc_nids that don't give all active input pins;
+ * if needed, check whether dynamic ADC-switching is available
+ */
+static int check_dyn_adc_switch(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	struct hda_input_mux *imux = &spec->input_mux;
+	hda_nid_t adc_nids[ARRAY_SIZE(spec->adc_nids)];
+	int i, n, nums;
+	hda_nid_t pin, adc;
+
+ again:
+	nums = 0;
+	for (n = 0; n < spec->num_adc_nids; n++) {
+		adc = spec->adc_nids[n];
+		for (i = 0; i < imux->num_items; i++) {
+			pin = spec->imux_pins[i];
+			if (!is_reachable_path(codec, pin, adc))
+				break;
+		}
+		if (i >= imux->num_items)
+			adc_nids[nums++] = adc;
+	}
+
+	if (!nums) {
+		if (spec->shared_mic_hp) {
+			spec->shared_mic_hp = 0;
+			imux->num_items = 1;
+			goto again;
+		}
+
+		/* check whether ADC-switch is possible */
+		for (i = 0; i < imux->num_items; i++) {
+			pin = spec->imux_pins[i];
+			for (n = 0; n < spec->num_adc_nids; n++) {
+				adc = spec->adc_nids[n];
+				if (is_reachable_path(codec, pin, adc)) {
+					spec->dyn_adc_idx[i] = n;
+					break;
+				}
+			}
+		}
+
+		snd_printdd("hda-codec: enabling ADC switching\n");
+		spec->dyn_adc_switch = 1;
+	} else if (nums != spec->num_adc_nids) {
+		memcpy(spec->adc_nids, adc_nids, nums * sizeof(hda_nid_t));
+		spec->num_adc_nids = nums;
+	}
+
+	if (imux->num_items == 1 || spec->shared_mic_hp) {
+		snd_printdd("hda-codec: reducing to a single ADC\n");
+		spec->num_adc_nids = 1; /* reduce to a single ADC */
+	}
+
+	/* single index for individual volumes ctls */
+	if (!spec->dyn_adc_switch && spec->multi_cap_vol)
+		spec->num_adc_nids = 1;
+
+	return 0;
+}
+
+/*
+ * create playback/capture controls for input pins
+ */
+static int create_input_ctls(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	const struct auto_pin_cfg *cfg = &spec->autocfg;
+	hda_nid_t mixer = spec->mixer_nid;
+	struct hda_input_mux *imux = &spec->input_mux;
+	int num_adcs;
+	int i, c, err, type_idx = 0;
+	const char *prev_label = NULL;
+
+	num_adcs = fill_adc_nids(codec);
+	if (num_adcs < 0)
+		return 0;
+
+	for (i = 0; i < cfg->num_inputs; i++) {
+		hda_nid_t pin;
+		const char *label;
+		bool imux_added;
+
+		pin = cfg->inputs[i].pin;
+		if (!is_input_pin(codec, pin))
+			continue;
+
+		label = hda_get_autocfg_input_label(codec, cfg, i);
+		if (spec->shared_mic_hp && !strcmp(label, "Misc"))
+			label = "Headphone Mic";
+		if (prev_label && !strcmp(label, prev_label))
+			type_idx++;
+		else
+			type_idx = 0;
+		prev_label = label;
+
+		if (mixer) {
+			if (is_reachable_path(codec, pin, mixer)) {
+				err = new_analog_input(codec, pin,
+						       label, type_idx, mixer);
+				if (err < 0)
+					return err;
+			}
+		}
+
+		imux_added = false;
+		for (c = 0; c < num_adcs; c++) {
+			struct nid_path *path;
+			hda_nid_t adc = spec->adc_nids[c];
+
+			if (!is_reachable_path(codec, pin, adc))
+				continue;
+			path = snd_array_new(&spec->paths);
+			if (!path)
+				return -ENOMEM;
+			memset(path, 0, sizeof(*path));
+			if (!snd_hda_parse_nid_path(codec, pin, adc, 2, path)) {
+				snd_printd(KERN_ERR
+					   "invalid input path 0x%x -> 0x%x\n",
+					   pin, adc);
+				spec->paths.used--;
+				continue;
+			}
+
+			if (!imux_added) {
+				spec->imux_pins[imux->num_items] = pin;
+				snd_hda_add_imux_item(imux, label,
+						      imux->num_items, NULL);
+				imux_added = true;
+			}
+		}
+	}
+
+	return 0;
+}
+
+
+/*
+ * input source mux
+ */
+
+/* get the ADC NID corresponding to the given index */
+static hda_nid_t get_adc_nid(struct hda_codec *codec, int adc_idx, int imux_idx)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	if (spec->dyn_adc_switch)
+		adc_idx = spec->dyn_adc_idx[imux_idx];
+	return spec->adc_nids[adc_idx];
+}
+
+static int mux_select(struct hda_codec *codec, unsigned int adc_idx,
+		      unsigned int idx);
+
+static int mux_enum_info(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_info *uinfo)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct hda_gen_spec *spec = codec->spec;
+	return snd_hda_input_mux_info(&spec->input_mux, uinfo);
+}
+
+static int mux_enum_get(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct hda_gen_spec *spec = codec->spec;
+	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+	ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
+	return 0;
+}
+
+static int mux_enum_put(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+	return mux_select(codec, adc_idx,
+			  ucontrol->value.enumerated.item[0]);
+}
+
+/*
+ * capture volume and capture switch ctls
+ */
+
+static const struct snd_kcontrol_new cap_src_temp = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Input Source",
+	.info = mux_enum_info,
+	.get = mux_enum_get,
+	.put = mux_enum_put,
+};
+
+typedef int (*put_call_t)(struct snd_kcontrol *kcontrol,
+			  struct snd_ctl_elem_value *ucontrol);
+
+static int cap_put_caller(struct snd_kcontrol *kcontrol,
+			  struct snd_ctl_elem_value *ucontrol,
+			  put_call_t func, int type)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct hda_gen_spec *spec = codec->spec;
+	const struct hda_input_mux *imux;
+	struct nid_path *path;
+	int i, adc_idx, err = 0;
+
+	imux = &spec->input_mux;
+	adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+	mutex_lock(&codec->control_mutex);
+	codec->cached_write = 1;
+	for (i = 0; i < imux->num_items; i++) {
+		path = snd_hda_get_nid_path(codec, spec->imux_pins[i],
+					    get_adc_nid(codec, adc_idx, i));
+		if (!path->ctls[type])
+			continue;
+		kcontrol->private_value = path->ctls[type];
+		err = func(kcontrol, ucontrol);
+		if (err < 0)
+			goto error;
+	}
+ error:
+	codec->cached_write = 0;
+	mutex_unlock(&codec->control_mutex);
+	if (err >= 0 && spec->cap_sync_hook)
+		spec->cap_sync_hook(codec);
+	return err;
+}
+
+/* capture volume ctl callbacks */
+#define cap_vol_info		snd_hda_mixer_amp_volume_info
+#define cap_vol_get		snd_hda_mixer_amp_volume_get
+#define cap_vol_tlv		snd_hda_mixer_amp_tlv
+
+static int cap_vol_put(struct snd_kcontrol *kcontrol,
+		       struct snd_ctl_elem_value *ucontrol)
+{
+	return cap_put_caller(kcontrol, ucontrol,
+			      snd_hda_mixer_amp_volume_put,
+			      NID_PATH_VOL_CTL);
+}
+
+static const struct snd_kcontrol_new cap_vol_temp = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Capture Volume",
+	.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+		   SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+		   SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK),
+	.info = cap_vol_info,
+	.get = cap_vol_get,
+	.put = cap_vol_put,
+	.tlv = { .c = cap_vol_tlv },
+};
+
+/* capture switch ctl callbacks */
+#define cap_sw_info		snd_ctl_boolean_stereo_info
+#define cap_sw_get		snd_hda_mixer_amp_switch_get
+
+static int cap_sw_put(struct snd_kcontrol *kcontrol,
+		      struct snd_ctl_elem_value *ucontrol)
+{
+	return cap_put_caller(kcontrol, ucontrol,
+			      snd_hda_mixer_amp_switch_put,
+			      NID_PATH_MUTE_CTL);
+}
+
+static const struct snd_kcontrol_new cap_sw_temp = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Capture Switch",
+	.info = cap_sw_info,
+	.get = cap_sw_get,
+	.put = cap_sw_put,
+};
+
+static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path)
+{
+	hda_nid_t nid;
+	int i, depth;
+
+	path->ctls[NID_PATH_VOL_CTL] = path->ctls[NID_PATH_MUTE_CTL] = 0;
+	for (depth = 0; depth < 3; depth++) {
+		if (depth >= path->depth)
+			return -EINVAL;
+		i = path->depth - depth - 1;
+		nid = path->path[i];
+		if (!path->ctls[NID_PATH_VOL_CTL]) {
+			if (nid_has_volume(codec, nid, HDA_OUTPUT))
+				path->ctls[NID_PATH_VOL_CTL] =
+					HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+			else if (nid_has_volume(codec, nid, HDA_INPUT)) {
+				int idx = path->idx[i];
+				if (!depth && codec->single_adc_amp)
+					idx = 0;
+				path->ctls[NID_PATH_VOL_CTL] =
+					HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT);
+			}
+		}
+		if (!path->ctls[NID_PATH_MUTE_CTL]) {
+			if (nid_has_mute(codec, nid, HDA_OUTPUT))
+				path->ctls[NID_PATH_MUTE_CTL] =
+					HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+			else if (nid_has_mute(codec, nid, HDA_INPUT)) {
+				int idx = path->idx[i];
+				if (!depth && codec->single_adc_amp)
+					idx = 0;
+				path->ctls[NID_PATH_MUTE_CTL] =
+					HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT);
+			}
+		}
+	}
+	return 0;
+}
+
+static bool is_inv_dmic_pin(struct hda_codec *codec, hda_nid_t nid)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	unsigned int val;
+	int i;
+
+	if (!spec->inv_dmic_split)
+		return false;
+	for (i = 0; i < cfg->num_inputs; i++) {
+		if (cfg->inputs[i].pin != nid)
+			continue;
+		if (cfg->inputs[i].type != AUTO_PIN_MIC)
+			return false;
+		val = snd_hda_codec_get_pincfg(codec, nid);
+		return snd_hda_get_input_pin_attr(val) == INPUT_PIN_ATTR_INT;
+	}
+	return false;
+}
+
+static int add_single_cap_ctl(struct hda_codec *codec, const char *label,
+			      int idx, bool is_switch, unsigned int ctl,
+			      bool inv_dmic)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	char tmpname[44];
+	int type = is_switch ? HDA_CTL_WIDGET_MUTE : HDA_CTL_WIDGET_VOL;
+	const char *sfx = is_switch ? "Switch" : "Volume";
+	unsigned int chs = inv_dmic ? 1 : 3;
+	int err;
+
+	if (!ctl)
+		return 0;
+
+	if (label)
+		snprintf(tmpname, sizeof(tmpname),
+			 "%s Capture %s", label, sfx);
+	else
+		snprintf(tmpname, sizeof(tmpname),
+			 "Capture %s", sfx);
+	err = add_control(spec, type, tmpname, idx,
+			  amp_val_replace_channels(ctl, chs));
+	if (err < 0 || !inv_dmic)
+		return err;
+
+	/* Make independent right kcontrol */
+	if (label)
+		snprintf(tmpname, sizeof(tmpname),
+			 "Inverted %s Capture %s", label, sfx);
+	else
+		snprintf(tmpname, sizeof(tmpname),
+			 "Inverted Capture %s", sfx);
+	return add_control(spec, type, tmpname, idx,
+			   amp_val_replace_channels(ctl, 2));
+}
+
+/* create single (and simple) capture volume and switch controls */
+static int create_single_cap_vol_ctl(struct hda_codec *codec, int idx,
+				     unsigned int vol_ctl, unsigned int sw_ctl,
+				     bool inv_dmic)
+{
+	int err;
+	err = add_single_cap_ctl(codec, NULL, idx, false, vol_ctl, inv_dmic);
+	if (err < 0)
+		return err;
+	err = add_single_cap_ctl(codec, NULL, idx, true, sw_ctl, inv_dmic);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+/* create bound capture volume and switch controls */
+static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx,
+				   unsigned int vol_ctl, unsigned int sw_ctl)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	struct snd_kcontrol_new *knew;
+
+	if (vol_ctl) {
+		knew = add_kctl(spec, NULL, &cap_vol_temp);
+		if (!knew)
+			return -ENOMEM;
+		knew->index = idx;
+		knew->private_value = vol_ctl;
+		knew->subdevice = HDA_SUBDEV_AMP_FLAG;
+	}
+	if (sw_ctl) {
+		knew = add_kctl(spec, NULL, &cap_sw_temp);
+		if (!knew)
+			return -ENOMEM;
+		knew->index = idx;
+		knew->private_value = sw_ctl;
+		knew->subdevice = HDA_SUBDEV_AMP_FLAG;
+	}
+	return 0;
+}
+
+/* return the vol ctl when used first in the imux list */
+static unsigned int get_first_cap_ctl(struct hda_codec *codec, int idx, int type)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	struct nid_path *path;
+	unsigned int ctl;
+	int i;
+
+	path = snd_hda_get_nid_path(codec, spec->imux_pins[idx],
+				    get_adc_nid(codec, 0, idx));
+	if (!path)
+		return 0;
+	ctl = path->ctls[type];
+	if (!ctl)
+		return 0;
+	for (i = 0; i < idx - 1; i++) {
+		path = snd_hda_get_nid_path(codec, spec->imux_pins[i],
+					    get_adc_nid(codec, 0, i));
+		if (path && path->ctls[type] == ctl)
+			return 0;
+	}
+	return ctl;
+}
+
+/* create individual capture volume and switch controls per input */
+static int create_multi_cap_vol_ctl(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	struct hda_input_mux *imux = &spec->input_mux;
+	int i, err, type, type_idx = 0;
+	const char *prev_label = NULL;
+
+	for (i = 0; i < imux->num_items; i++) {
+		const char *label;
+		bool inv_dmic;
+		label = hda_get_autocfg_input_label(codec, &spec->autocfg, i);
+		if (prev_label && !strcmp(label, prev_label))
+			type_idx++;
+		else
+			type_idx = 0;
+		prev_label = label;
+		inv_dmic = is_inv_dmic_pin(codec, spec->imux_pins[i]);
+
+		for (type = 0; type < 2; type++) {
+			err = add_single_cap_ctl(codec, label, type_idx, type,
+						 get_first_cap_ctl(codec, i, type),
+						 inv_dmic);
+			if (err < 0)
+				return err;
+		}
+	}
+	return 0;
+}
+
+static int create_capture_mixers(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	struct hda_input_mux *imux = &spec->input_mux;
+	int i, n, nums, err;
+
+	if (spec->dyn_adc_switch)
+		nums = 1;
+	else
+		nums = spec->num_adc_nids;
+
+	if (!spec->auto_mic && imux->num_items > 1) {
+		struct snd_kcontrol_new *knew;
+		knew = add_kctl(spec, NULL, &cap_src_temp);
+		if (!knew)
+			return -ENOMEM;
+		knew->count = nums;
+	}
+
+	for (n = 0; n < nums; n++) {
+		bool multi = false;
+		bool inv_dmic = false;
+		int vol, sw;
+
+		vol = sw = 0;
+		for (i = 0; i < imux->num_items; i++) {
+			struct nid_path *path;
+			path = snd_hda_get_nid_path(codec, spec->imux_pins[i],
+						    get_adc_nid(codec, n, i));
+			if (!path)
+				continue;
+			parse_capvol_in_path(codec, path);
+			if (!vol)
+				vol = path->ctls[NID_PATH_VOL_CTL];
+			else if (vol != path->ctls[NID_PATH_VOL_CTL])
+				multi = true;
+			if (!sw)
+				sw = path->ctls[NID_PATH_MUTE_CTL];
+			else if (sw != path->ctls[NID_PATH_MUTE_CTL])
+				multi = true;
+			if (is_inv_dmic_pin(codec, spec->imux_pins[i]))
+				inv_dmic = true;
+		}
+
+		if (!multi)
+			err = create_single_cap_vol_ctl(codec, n, vol, sw,
+							inv_dmic);
+		else if (!spec->multi_cap_vol)
+			err = create_bind_cap_vol_ctl(codec, n, vol, sw);
+		else
+			err = create_multi_cap_vol_ctl(codec);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+/*
+ * add mic boosts if needed
+ */
+static int parse_mic_boost(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	int i, err;
+	int type_idx = 0;
+	hda_nid_t nid;
+	const char *prev_label = NULL;
+
+	for (i = 0; i < cfg->num_inputs; i++) {
+		if (cfg->inputs[i].type > AUTO_PIN_MIC)
+			break;
+		nid = cfg->inputs[i].pin;
+		if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) {
+			const char *label;
+			char boost_label[32];
+			struct nid_path *path;
+			unsigned int val;
+
+			label = hda_get_autocfg_input_label(codec, cfg, i);
+			if (spec->shared_mic_hp && !strcmp(label, "Misc"))
+				label = "Headphone Mic";
+			if (prev_label && !strcmp(label, prev_label))
+				type_idx++;
+			else
+				type_idx = 0;
+			prev_label = label;
+
+			snprintf(boost_label, sizeof(boost_label),
+				 "%s Boost Volume", label);
+			val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT);
+			err = add_control(spec, HDA_CTL_WIDGET_VOL,
+					  boost_label, type_idx, val);
+			if (err < 0)
+				return err;
+
+			path = snd_hda_get_nid_path(codec, nid, 0);
+			if (path)
+				path->ctls[NID_PATH_BOOST_CTL] = val;
+		}
+	}
+	return 0;
+}
+
+/*
+ * parse digital I/Os and set up NIDs in BIOS auto-parse mode
+ */
+static void parse_digital(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	int i, nums;
+	hda_nid_t dig_nid;
+
+	/* support multiple SPDIFs; the secondary is set up as a slave */
+	nums = 0;
+	for (i = 0; i < spec->autocfg.dig_outs; i++) {
+		hda_nid_t pin = spec->autocfg.dig_out_pins[i];
+		dig_nid = look_for_dac(codec, pin, true);
+		if (!dig_nid)
+			continue;
+		if (!snd_hda_add_new_path(codec, dig_nid, pin, 2))
+			continue;
+		if (!nums) {
+			spec->multiout.dig_out_nid = dig_nid;
+			spec->dig_out_type = spec->autocfg.dig_out_type[0];
+		} else {
+			spec->multiout.slave_dig_outs = spec->slave_dig_outs;
+			if (nums >= ARRAY_SIZE(spec->slave_dig_outs) - 1)
+			break;
+			spec->slave_dig_outs[nums - 1] = dig_nid;
+		}
+		nums++;
+	}
+
+	if (spec->autocfg.dig_in_pin) {
+		dig_nid = codec->start_nid;
+		for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
+			struct nid_path *path;
+			unsigned int wcaps = get_wcaps(codec, dig_nid);
+			if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
+				continue;
+			if (!(wcaps & AC_WCAP_DIGITAL))
+				continue;
+			path = snd_hda_add_new_path(codec,
+						    spec->autocfg.dig_in_pin,
+						    dig_nid, 2);
+			if (path) {
+				path->active = true;
+				spec->dig_in_nid = dig_nid;
+				break;
+			}
+		}
+	}
+}
+
+
+/*
+ * input MUX handling
+ */
+
+static bool dyn_adc_pcm_resetup(struct hda_codec *codec, int cur);
+
+/* select the given imux item; either unmute exclusively or select the route */
+static int mux_select(struct hda_codec *codec, unsigned int adc_idx,
+		      unsigned int idx)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	const struct hda_input_mux *imux;
+	struct nid_path *path;
+
+	imux = &spec->input_mux;
+	if (!imux->num_items)
+		return 0;
+
+	if (idx >= imux->num_items)
+		idx = imux->num_items - 1;
+	if (spec->cur_mux[adc_idx] == idx)
+		return 0;
+
+	path = snd_hda_get_nid_path(codec,
+				    spec->imux_pins[spec->cur_mux[adc_idx]],
+				    spec->adc_nids[adc_idx]);
+	if (!path)
+		return 0;
+	if (path->active)
+		snd_hda_activate_path(codec, path, false, false);
+
+	spec->cur_mux[adc_idx] = idx;
+
+	if (spec->shared_mic_hp)
+		update_shared_mic_hp(codec, spec->cur_mux[adc_idx]);
+
+	if (spec->dyn_adc_switch)
+		dyn_adc_pcm_resetup(codec, idx);
+
+	path = snd_hda_get_nid_path(codec, spec->imux_pins[idx],
+				    get_adc_nid(codec, adc_idx, idx));
+	if (!path)
+		return 0;
+	if (path->active)
+		return 0;
+	snd_hda_activate_path(codec, path, true, false);
+	if (spec->cap_sync_hook)
+		spec->cap_sync_hook(codec);
+	return 1;
+}
+
+
+/*
+ * Jack detections for HP auto-mute and mic-switch
+ */
+
+/* check each pin in the given array; returns true if any of them is plugged */
+static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins)
+{
+	int i, present = 0;
+
+	for (i = 0; i < num_pins; i++) {
+		hda_nid_t nid = pins[i];
+		if (!nid)
+			break;
+		present |= snd_hda_jack_detect(codec, nid);
+	}
+	return present;
+}
+
+/* standard HP/line-out auto-mute helper */
+static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
+			bool mute, bool hp_out)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	unsigned int pin_bits = mute ? 0 : (hp_out ? PIN_HP : PIN_OUT);
+	int i;
+
+	for (i = 0; i < num_pins; i++) {
+		hda_nid_t nid = pins[i];
+		unsigned int val;
+		if (!nid)
+			break;
+		/* don't reset VREF value in case it's controlling
+		 * the amp (see alc861_fixup_asus_amp_vref_0f())
+		 */
+		if (spec->keep_vref_in_automute) {
+			val = snd_hda_codec_read(codec, nid, 0,
+					AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+			val &= ~PIN_HP;
+		} else
+			val = 0;
+		val |= pin_bits;
+		snd_hda_set_pin_ctl(codec, nid, val);
+	}
+}
+
+/* Toggle outputs muting */
+static void update_outputs(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	int on;
+
+	/* Control HP pins/amps depending on master_mute state;
+	 * in general, HP pins/amps control should be enabled in all cases,
+	 * but currently set only for master_mute, just to be safe
+	 */
+	if (!spec->shared_mic_hp) /* don't change HP-pin when shared with mic */
+		do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins),
+		    spec->autocfg.hp_pins, spec->master_mute, true);
+
+	if (!spec->automute_speaker)
+		on = 0;
+	else
+		on = spec->hp_jack_present | spec->line_jack_present;
+	on |= spec->master_mute;
+	do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins),
+		    spec->autocfg.speaker_pins, on, false);
+
+	/* toggle line-out mutes if needed, too */
+	/* if LO is a copy of either HP or Speaker, don't need to handle it */
+	if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0] ||
+	    spec->autocfg.line_out_pins[0] == spec->autocfg.speaker_pins[0])
+		return;
+	if (!spec->automute_lo)
+		on = 0;
+	else
+		on = spec->hp_jack_present;
+	on |= spec->master_mute;
+	do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins),
+		    spec->autocfg.line_out_pins, on, false);
+}
+
+static void call_update_outputs(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	if (spec->automute_hook)
+		spec->automute_hook(codec);
+	else
+		update_outputs(codec);
+}
+
+/* standard HP-automute helper */
+static void hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
+{
+	struct hda_gen_spec *spec = codec->spec;
+
+	spec->hp_jack_present =
+		detect_jacks(codec, ARRAY_SIZE(spec->autocfg.hp_pins),
+			     spec->autocfg.hp_pins);
+	if (!spec->detect_hp || (!spec->automute_speaker && !spec->automute_lo))
+		return;
+	call_update_outputs(codec);
+}
+
+/* standard line-out-automute helper */
+static void line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
+{
+	struct hda_gen_spec *spec = codec->spec;
+
+	if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
+		return;
+	/* check LO jack only when it's different from HP */
+	if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0])
+		return;
+
+	spec->line_jack_present =
+		detect_jacks(codec, ARRAY_SIZE(spec->autocfg.line_out_pins),
+			     spec->autocfg.line_out_pins);
+	if (!spec->automute_speaker || !spec->detect_lo)
+		return;
+	call_update_outputs(codec);
+}
+
+/* standard mic auto-switch helper */
+static void mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *jack)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	int i;
+
+	if (!spec->auto_mic)
+		return;
+
+	for (i = spec->am_num_entries - 1; i > 0; i--) {
+		if (snd_hda_jack_detect(codec, spec->am_entry[i].pin)) {
+			mux_select(codec, 0, spec->am_entry[i].idx);
+			return;
+		}
+	}
+	mux_select(codec, 0, spec->am_entry[0].idx);
+}
+
+/*
+ * Auto-Mute mode mixer enum support
+ */
+static int automute_mode_info(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_info *uinfo)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct hda_gen_spec *spec = codec->spec;
+	static const char * const texts3[] = {
+		"Disabled", "Speaker Only", "Line Out+Speaker"
+	};
+
+	if (spec->automute_speaker_possible && spec->automute_lo_possible)
+		return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3);
+	return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
+}
+
+static int automute_mode_get(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct hda_gen_spec *spec = codec->spec;
+	unsigned int val = 0;
+	if (spec->automute_speaker)
+		val++;
+	if (spec->automute_lo)
+		val++;
+
+	ucontrol->value.enumerated.item[0] = val;
+	return 0;
+}
+
+static int automute_mode_put(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct hda_gen_spec *spec = codec->spec;
+
+	switch (ucontrol->value.enumerated.item[0]) {
+	case 0:
+		if (!spec->automute_speaker && !spec->automute_lo)
+			return 0;
+		spec->automute_speaker = 0;
+		spec->automute_lo = 0;
+		break;
+	case 1:
+		if (spec->automute_speaker_possible) {
+			if (!spec->automute_lo && spec->automute_speaker)
+				return 0;
+			spec->automute_speaker = 1;
+			spec->automute_lo = 0;
+		} else if (spec->automute_lo_possible) {
+			if (spec->automute_lo)
+				return 0;
+			spec->automute_lo = 1;
+		} else
+			return -EINVAL;
+		break;
+	case 2:
+		if (!spec->automute_lo_possible || !spec->automute_speaker_possible)
+			return -EINVAL;
+		if (spec->automute_speaker && spec->automute_lo)
+			return 0;
+		spec->automute_speaker = 1;
+		spec->automute_lo = 1;
+		break;
+	default:
+		return -EINVAL;
+	}
+	call_update_outputs(codec);
+	return 1;
+}
+
+static const struct snd_kcontrol_new automute_mode_enum = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Auto-Mute Mode",
+	.info = automute_mode_info,
+	.get = automute_mode_get,
+	.put = automute_mode_put,
+};
+
+static int add_automute_mode_enum(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+
+	if (!add_kctl(spec, NULL, &automute_mode_enum))
+		return -ENOMEM;
+	return 0;
+}
+
+/*
+ * Check the availability of HP/line-out auto-mute;
+ * Set up appropriately if really supported
+ */
+static int check_auto_mute_availability(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	int present = 0;
+	int i, err;
+
+	if (cfg->hp_pins[0])
+		present++;
+	if (cfg->line_out_pins[0])
+		present++;
+	if (cfg->speaker_pins[0])
+		present++;
+	if (present < 2) /* need two different output types */
+		return 0;
+
+	if (!cfg->speaker_pins[0] &&
+	    cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
+		memcpy(cfg->speaker_pins, cfg->line_out_pins,
+		       sizeof(cfg->speaker_pins));
+		cfg->speaker_outs = cfg->line_outs;
+	}
+
+	if (!cfg->hp_pins[0] &&
+	    cfg->line_out_type == AUTO_PIN_HP_OUT) {
+		memcpy(cfg->hp_pins, cfg->line_out_pins,
+		       sizeof(cfg->hp_pins));
+		cfg->hp_outs = cfg->line_outs;
+	}
+
+	for (i = 0; i < cfg->hp_outs; i++) {
+		hda_nid_t nid = cfg->hp_pins[i];
+		if (!is_jack_detectable(codec, nid))
+			continue;
+		snd_printdd("hda-codec: Enable HP auto-muting on NID 0x%x\n",
+			    nid);
+		snd_hda_jack_detect_enable_callback(codec, nid, HDA_GEN_HP_EVENT,
+						    hp_automute);
+		spec->detect_hp = 1;
+	}
+
+	if (cfg->line_out_type == AUTO_PIN_LINE_OUT && cfg->line_outs) {
+		if (cfg->speaker_outs)
+			for (i = 0; i < cfg->line_outs; i++) {
+				hda_nid_t nid = cfg->line_out_pins[i];
+				if (!is_jack_detectable(codec, nid))
+					continue;
+				snd_printdd("hda-codec: Enable Line-Out auto-muting on NID 0x%x\n", nid);
+				snd_hda_jack_detect_enable_callback(codec, nid,
+								    HDA_GEN_FRONT_EVENT,
+								    line_automute);
+				spec->detect_lo = 1;
+			}
+		spec->automute_lo_possible = spec->detect_hp;
+	}
+
+	spec->automute_speaker_possible = cfg->speaker_outs &&
+		(spec->detect_hp || spec->detect_lo);
+
+	spec->automute_lo = spec->automute_lo_possible;
+	spec->automute_speaker = spec->automute_speaker_possible;
+
+	if (spec->automute_speaker_possible || spec->automute_lo_possible) {
+		/* create a control for automute mode */
+		err = add_automute_mode_enum(codec);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+/* return the position of NID in the list, or -1 if not found */
+static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
+{
+	int i;
+	for (i = 0; i < nums; i++)
+		if (list[i] == nid)
+			return i;
+	return -1;
+}
+
+/* check whether all auto-mic pins are valid; setup indices if OK */
+static bool auto_mic_check_imux(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	const struct hda_input_mux *imux;
+	int i;
+
+	imux = &spec->input_mux;
+	for (i = 0; i < spec->am_num_entries; i++) {
+		spec->am_entry[i].idx =
+			find_idx_in_nid_list(spec->am_entry[i].pin,
+					     spec->imux_pins, imux->num_items);
+		if (spec->am_entry[i].idx < 0)
+			return false; /* no corresponding imux */
+	}
+
+	/* we don't need the jack detection for the first pin */
+	for (i = 1; i < spec->am_num_entries; i++)
+		snd_hda_jack_detect_enable_callback(codec,
+						    spec->am_entry[i].pin,
+						    HDA_GEN_MIC_EVENT,
+						    mic_autoswitch);
+	return true;
+}
+
+static int compare_attr(const void *ap, const void *bp)
+{
+	const struct automic_entry *a = ap;
+	const struct automic_entry *b = bp;
+	return (int)(a->attr - b->attr);
+}
 
 /*
- * check whether the controls with the given name and direction suffix already exist
+ * Check the availability of auto-mic switch;
+ * Set up if really supported
  */
-static int check_existing_control(struct hda_codec *codec, const char *type, const char *dir)
-{
-	struct snd_ctl_elem_id id;
-	memset(&id, 0, sizeof(id));
-	sprintf(id.name, "%s %s Volume", type, dir);
-	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-	if (snd_ctl_find_id(codec->bus->card, &id))
-		return 1;
-	sprintf(id.name, "%s %s Switch", type, dir);
-	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-	if (snd_ctl_find_id(codec->bus->card, &id))
-		return 1;
+static int check_auto_mic_availability(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	unsigned int types;
+	int i, num_pins;
+
+	types = 0;
+	num_pins = 0;
+	for (i = 0; i < cfg->num_inputs; i++) {
+		hda_nid_t nid = cfg->inputs[i].pin;
+		unsigned int attr;
+		attr = snd_hda_codec_get_pincfg(codec, nid);
+		attr = snd_hda_get_input_pin_attr(attr);
+		if (types & (1 << attr))
+			return 0; /* already occupied */
+		switch (attr) {
+		case INPUT_PIN_ATTR_INT:
+			if (cfg->inputs[i].type != AUTO_PIN_MIC)
+				return 0; /* invalid type */
+			break;
+		case INPUT_PIN_ATTR_UNUSED:
+			return 0; /* invalid entry */
+		default:
+			if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
+				return 0; /* invalid type */
+			if (!spec->line_in_auto_switch &&
+			    cfg->inputs[i].type != AUTO_PIN_MIC)
+				return 0; /* only mic is allowed */
+			if (!is_jack_detectable(codec, nid))
+				return 0; /* no unsol support */
+			break;
+		}
+		if (num_pins >= MAX_AUTO_MIC_PINS)
+			return 0;
+		types |= (1 << attr);
+		spec->am_entry[num_pins].pin = nid;
+		spec->am_entry[num_pins].attr = attr;
+		num_pins++;
+	}
+
+	if (num_pins < 2)
+		return 0;
+
+	spec->am_num_entries = num_pins;
+	/* sort the am_entry in the order of attr so that the pin with a
+	 * higher attr will be selected when the jack is plugged.
+	 */
+	sort(spec->am_entry, num_pins, sizeof(spec->am_entry[0]),
+	     compare_attr, NULL);
+
+	if (!auto_mic_check_imux(codec))
+		return 0;
+
+	spec->auto_mic = 1;
+	spec->num_adc_nids = 1;
+	spec->cur_mux[0] = spec->am_entry[0].idx;
+	snd_printdd("hda-codec: Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n",
+		    spec->am_entry[0].pin,
+		    spec->am_entry[1].pin,
+		    spec->am_entry[2].pin);
+
 	return 0;
 }
 
-/*
- * build output mixer controls
- */
-static int create_output_mixers(struct hda_codec *codec,
-				const char * const *names)
-{
-	struct hda_gspec *spec = codec->spec;
-	int i, err;
 
-	for (i = 0; i < spec->pcm_vol_nodes; i++) {
-		err = create_mixer(codec, spec->pcm_vol[i].node,
-				   spec->pcm_vol[i].index,
-				   names[i], "Playback", 0);
+/* parse the BIOS configuration and set up the hda_gen_spec */
+/* return 1 if successful, 0 if the proper config is not found,
+ * or a negative error code
+ */
+int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
+				  const hda_nid_t *ignore_nids)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	int err;
+
+	err = snd_hda_parse_pin_defcfg(codec, cfg, ignore_nids,
+				       spec->parse_flags);
+	if (err < 0)
+		return err;
+	if (!cfg->line_outs) {
+		if (cfg->dig_outs || cfg->dig_in_pin) {
+			spec->multiout.max_channels = 2;
+			spec->no_analog = 1;
+			goto dig_only;
+		}
+		return 0; /* can't find valid BIOS pin config */
+	}
+
+	if (!spec->no_primary_hp &&
+	    cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
+	    cfg->line_outs <= cfg->hp_outs) {
+		/* use HP as primary out */
+		cfg->speaker_outs = cfg->line_outs;
+		memcpy(cfg->speaker_pins, cfg->line_out_pins,
+		       sizeof(cfg->speaker_pins));
+		cfg->line_outs = cfg->hp_outs;
+		memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins));
+		cfg->hp_outs = 0;
+		memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
+		cfg->line_out_type = AUTO_PIN_HP_OUT;
+	}
+
+	err = parse_output_paths(codec);
+	if (err < 0)
+		return err;
+	err = create_multi_channel_mode(codec);
+	if (err < 0)
+		return err;
+	err = create_multi_out_ctls(codec, cfg);
+	if (err < 0)
+		return err;
+	err = create_hp_out_ctls(codec);
+	if (err < 0)
+		return err;
+	err = create_speaker_out_ctls(codec);
+	if (err < 0)
+		return err;
+	err = create_shared_input(codec);
+	if (err < 0)
+		return err;
+	err = create_input_ctls(codec);
+	if (err < 0)
+		return err;
+
+	/* check the multiple speaker pins */
+	if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+		spec->const_channel_count = cfg->line_outs * 2;
+	else
+		spec->const_channel_count = cfg->speaker_outs * 2;
+
+	if (spec->multi_ios > 0)
+		spec->multiout.max_channels = max(spec->ext_channel_count,
+						  spec->const_channel_count);
+	else
+		spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+
+	err = check_auto_mute_availability(codec);
+	if (err < 0)
+		return err;
+
+	err = check_dyn_adc_switch(codec);
+	if (err < 0)
+		return err;
+
+	if (!spec->shared_mic_hp) {
+		err = check_auto_mic_availability(codec);
 		if (err < 0)
 			return err;
 	}
-	return 0;
-}
 
-static int build_output_controls(struct hda_codec *codec)
-{
-	struct hda_gspec *spec = codec->spec;
-	static const char * const types_speaker[] = { "Speaker", "Headphone" };
-	static const char * const types_line[] = { "Front", "Headphone" };
+	err = create_capture_mixers(codec);
+	if (err < 0)
+		return err;
 
-	switch (spec->pcm_vol_nodes) {
-	case 1:
-		return create_mixer(codec, spec->pcm_vol[0].node,
-				    spec->pcm_vol[0].index,
-				    "Master", "Playback", 0);
-	case 2:
-		if (defcfg_type(spec->out_pin_node[0]) == AC_JACK_SPEAKER)
-			return create_output_mixers(codec, types_speaker);
-		else
-			return create_output_mixers(codec, types_line);
-	}
-	return 0;
+	err = parse_mic_boost(codec);
+	if (err < 0)
+		return err;
+
+ dig_only:
+	parse_digital(codec);
+
+	return 1;
 }
+EXPORT_SYMBOL_HDA(snd_hda_gen_parse_auto_config);
 
-/* create capture volume/switch */
-static int build_input_controls(struct hda_codec *codec)
-{
-	struct hda_gspec *spec = codec->spec;
-	struct hda_gnode *adc_node = spec->adc_node;
-	int i, err;
-	static struct snd_kcontrol_new cap_sel = {
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Capture Source",
-		.info = capture_source_info,
-		.get = capture_source_get,
-		.put = capture_source_put,
-	};
 
-	if (! adc_node || ! spec->input_mux.num_items)
-		return 0; /* not found */
+/*
+ * Build control elements
+ */
+
+/* slave controls for virtual master */
+static const char * const slave_pfxs[] = {
+	"Front", "Surround", "Center", "LFE", "Side",
+	"Headphone", "Speaker", "Mono", "Line Out",
+	"CLFE", "Bass Speaker", "PCM",
+	NULL,
+};
+
+int snd_hda_gen_build_controls(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	int err;
 
-	spec->cur_cap_src = 0;
-	select_input_connection(codec, adc_node,
-				spec->input_mux.items[0].index);
+	err = snd_hda_add_new_ctls(codec, spec->kctls.list);
+	if (err < 0)
+		return err;
 
-	/* create capture volume and switch controls if the ADC has an amp */
-	/* do we have only a single item? */
-	if (spec->input_mux.num_items == 1) {
-		err = create_mixer(codec, adc_node,
-				   spec->input_mux.items[0].index,
-				   NULL, "Capture", 0);
+	if (spec->multiout.dig_out_nid) {
+		err = snd_hda_create_dig_out_ctls(codec,
+						  spec->multiout.dig_out_nid,
+						  spec->multiout.dig_out_nid,
+						  spec->pcm_rec[1].pcm_type);
+		if (err < 0)
+			return err;
+		if (!spec->no_analog) {
+			err = snd_hda_create_spdif_share_sw(codec,
+							    &spec->multiout);
+			if (err < 0)
+				return err;
+			spec->multiout.share_spdif = 1;
+		}
+	}
+	if (spec->dig_in_nid) {
+		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
 		if (err < 0)
 			return err;
-		return 0;
 	}
 
-	/* create input MUX if multiple sources are available */
-	err = snd_hda_ctl_add(codec, spec->adc_node->nid,
-			      snd_ctl_new1(&cap_sel, codec));
-	if (err < 0)
-		return err;
+	/* if we have no master control, let's create it */
+	if (!spec->no_analog &&
+	    !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
+		unsigned int vmaster_tlv[4];
+		snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
+					HDA_OUTPUT, vmaster_tlv);
+		err = snd_hda_add_vmaster(codec, "Master Playback Volume",
+					  vmaster_tlv, slave_pfxs,
+					  "Playback Volume");
+		if (err < 0)
+			return err;
+	}
+	if (!spec->no_analog &&
+	    !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
+		err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
+					    NULL, slave_pfxs,
+					    "Playback Switch",
+					    true, &spec->vmaster_mute.sw_kctl);
+		if (err < 0)
+			return err;
+		if (spec->vmaster_mute.hook)
+			snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true);
+	}
 
-	/* no volume control? */
-	if (! (adc_node->wid_caps & AC_WCAP_IN_AMP) ||
-	    ! (adc_node->amp_in_caps & AC_AMPCAP_NUM_STEPS))
-		return 0;
+	free_kctls(spec); /* no longer needed */
 
-	for (i = 0; i < spec->input_mux.num_items; i++) {
-		struct snd_kcontrol_new knew;
-		char name[32];
-		sprintf(name, "%s Capture Volume",
-			spec->input_mux.items[i].label);
-		knew = (struct snd_kcontrol_new)
-			HDA_CODEC_VOLUME(name, adc_node->nid,
-					 spec->input_mux.items[i].index,
-					 HDA_INPUT);
-		err = snd_hda_ctl_add(codec, adc_node->nid,
-					snd_ctl_new1(&knew, codec));
+	if (spec->shared_mic_hp) {
+		int err;
+		int nid = spec->autocfg.inputs[1].pin;
+		err = snd_hda_jack_add_kctl(codec, nid, "Headphone Mic", 0);
+		if (err < 0)
+			return err;
+		err = snd_hda_jack_detect_enable(codec, nid, 0);
 		if (err < 0)
 			return err;
 	}
 
+	err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
+	if (err < 0)
+		return err;
+
 	return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_gen_build_controls);
 
 
 /*
- * parse the nodes recursively until reach to the output PIN.
- *
- * returns 0 - if not found,
- *         1 - if found, but no mixer is created
- *         2 - if found and mixer was already created, (just skip)
- *         a negative error code
+ * PCM definitions
  */
-static int parse_loopback_path(struct hda_codec *codec, struct hda_gspec *spec,
-			       struct hda_gnode *node, struct hda_gnode *dest_node,
-			       const char *type)
-{
-	int i, err;
 
-	if (node->checked)
-		return 0;
+/*
+ * Analog playback callbacks
+ */
+static int playback_pcm_open(struct hda_pcm_stream *hinfo,
+			     struct hda_codec *codec,
+			     struct snd_pcm_substream *substream)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+					     hinfo);
+}
 
-	node->checked = 1;
-	if (node == dest_node) {
-		/* loopback connection found */
-		return 1;
-	}
+static int playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+				struct hda_codec *codec,
+				unsigned int stream_tag,
+				unsigned int format,
+				struct snd_pcm_substream *substream)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
+						stream_tag, format, substream);
+}
 
-	for (i = 0; i < node->nconns; i++) {
-		struct hda_gnode *child = hda_get_node(spec, node->conn_list[i]);
-		if (! child)
-			continue;
-		err = parse_loopback_path(codec, spec, child, dest_node, type);
-		if (err < 0)
-			return err;
-		else if (err >= 1) {
-			if (err == 1) {
-				err = create_mixer(codec, node, i, type,
-						   "Playback", 1);
-				if (err < 0)
-					return err;
-				if (err > 0)
-					return 2; /* ok, created */
-				/* not created, maybe in the lower path */
-				err = 1;
-			}
-			/* connect and unmute */
-			if (node->nconns > 1)
-				select_input_connection(codec, node, i);
-			unmute_input(codec, node, i);
-			unmute_output(codec, node);
-			return err;
-		}
-	}
-	return 0;
+static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+				struct hda_codec *codec,
+				struct snd_pcm_substream *substream)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
 }
 
 /*
- * parse the tree and build the loopback controls
+ * Digital out
  */
-static int build_loopback_controls(struct hda_codec *codec)
+static int dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+				 struct hda_codec *codec,
+				 struct snd_pcm_substream *substream)
 {
-	struct hda_gspec *spec = codec->spec;
-	struct hda_gnode *node;
-	int err;
-	const char *type;
+	struct hda_gen_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
 
-	if (! spec->out_pin_node[0])
-		return 0;
+static int dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+				    struct hda_codec *codec,
+				    unsigned int stream_tag,
+				    unsigned int format,
+				    struct snd_pcm_substream *substream)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+					     stream_tag, format, substream);
+}
 
-	list_for_each_entry(node, &spec->nid_list, list) {
-		if (node->type != AC_WID_PIN)
-			continue;
-		/* input capable? */
-		if (! (node->pin_caps & AC_PINCAP_IN))
-			return 0;
-		type = get_input_type(node, NULL);
-		if (type) {
-			if (check_existing_control(codec, type, "Playback"))
-				continue;
-			clear_check_flags(spec);
-			err = parse_loopback_path(codec, spec,
-						  spec->out_pin_node[0],
-						  node, type);
-			if (err < 0)
-				return err;
-			if (! err)
-				continue;
-		}
-	}
-	return 0;
+static int dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+				    struct hda_codec *codec,
+				    struct snd_pcm_substream *substream)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
+}
+
+static int dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+				  struct hda_codec *codec,
+				  struct snd_pcm_substream *substream)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
 }
 
 /*
- * build mixer controls
+ * Analog capture
  */
-static int build_generic_controls(struct hda_codec *codec)
+static int alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+				   struct hda_codec *codec,
+				   unsigned int stream_tag,
+				   unsigned int format,
+				   struct snd_pcm_substream *substream)
 {
-	int err;
+	struct hda_gen_spec *spec = codec->spec;
 
-	if ((err = build_input_controls(codec)) < 0 ||
-	    (err = build_output_controls(codec)) < 0 ||
-	    (err = build_loopback_controls(codec)) < 0)
-		return err;
+	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1],
+				   stream_tag, 0, format);
+	return 0;
+}
+
+static int alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+				   struct hda_codec *codec,
+				   struct snd_pcm_substream *substream)
+{
+	struct hda_gen_spec *spec = codec->spec;
 
+	snd_hda_codec_cleanup_stream(codec,
+				     spec->adc_nids[substream->number + 1]);
 	return 0;
 }
 
 /*
- * PCM
  */
-static struct hda_pcm_stream generic_pcm_playback = {
+static const struct hda_pcm_stream pcm_analog_playback = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 8,
+	/* NID is set in build_pcms */
+	.ops = {
+		.open = playback_pcm_open,
+		.prepare = playback_pcm_prepare,
+		.cleanup = playback_pcm_cleanup
+	},
+};
+
+static const struct hda_pcm_stream pcm_analog_capture = {
 	.substreams = 1,
 	.channels_min = 2,
 	.channels_max = 2,
+	/* NID is set in build_pcms */
 };
 
-static int generic_pcm2_prepare(struct hda_pcm_stream *hinfo,
-				struct hda_codec *codec,
-				unsigned int stream_tag,
-				unsigned int format,
-				struct snd_pcm_substream *substream)
+static const struct hda_pcm_stream pcm_analog_alt_playback = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	/* NID is set in build_pcms */
+};
+
+static const struct hda_pcm_stream pcm_analog_alt_capture = {
+	.substreams = 2, /* can be overridden */
+	.channels_min = 2,
+	.channels_max = 2,
+	/* NID is set in build_pcms */
+	.ops = {
+		.prepare = alt_capture_pcm_prepare,
+		.cleanup = alt_capture_pcm_cleanup
+	},
+};
+
+static const struct hda_pcm_stream pcm_digital_playback = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	/* NID is set in build_pcms */
+	.ops = {
+		.open = dig_playback_pcm_open,
+		.close = dig_playback_pcm_close,
+		.prepare = dig_playback_pcm_prepare,
+		.cleanup = dig_playback_pcm_cleanup
+	},
+};
+
+static const struct hda_pcm_stream pcm_digital_capture = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	/* NID is set in build_pcms */
+};
+
+/* Used by build_pcms to flag that a PCM has no playback stream */
+static const struct hda_pcm_stream pcm_null_stream = {
+	.substreams = 0,
+	.channels_min = 0,
+	.channels_max = 0,
+};
+
+/*
+ * dynamic changing ADC PCM streams
+ */
+static bool dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
 {
-	struct hda_gspec *spec = codec->spec;
+	struct hda_gen_spec *spec = codec->spec;
+	hda_nid_t new_adc = spec->adc_nids[spec->dyn_adc_idx[cur]];
+
+	if (spec->cur_adc && spec->cur_adc != new_adc) {
+		/* stream is running, let's swap the current ADC */
+		__snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
+		spec->cur_adc = new_adc;
+		snd_hda_codec_setup_stream(codec, new_adc,
+					   spec->cur_adc_stream_tag, 0,
+					   spec->cur_adc_format);
+		return true;
+	}
+	return false;
+}
 
-	snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
-	snd_hda_codec_setup_stream(codec, spec->dac_node[1]->nid,
-				   stream_tag, 0, format);
+/* analog capture with dynamic dual-adc changes */
+static int dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+				       struct hda_codec *codec,
+				       unsigned int stream_tag,
+				       unsigned int format,
+				       struct snd_pcm_substream *substream)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	spec->cur_adc = spec->adc_nids[spec->dyn_adc_idx[spec->cur_mux[0]]];
+	spec->cur_adc_stream_tag = stream_tag;
+	spec->cur_adc_format = format;
+	snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
 	return 0;
 }
 
-static int generic_pcm2_cleanup(struct hda_pcm_stream *hinfo,
-				struct hda_codec *codec,
-				struct snd_pcm_substream *substream)
+static int dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+				       struct hda_codec *codec,
+				       struct snd_pcm_substream *substream)
 {
-	struct hda_gspec *spec = codec->spec;
-
-	snd_hda_codec_cleanup_stream(codec, hinfo->nid);
-	snd_hda_codec_cleanup_stream(codec, spec->dac_node[1]->nid);
+	struct hda_gen_spec *spec = codec->spec;
+	snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
+	spec->cur_adc = 0;
 	return 0;
 }
 
-static int build_generic_pcms(struct hda_codec *codec)
+static const struct hda_pcm_stream dyn_adc_pcm_analog_capture = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	.nid = 0, /* fill later */
+	.ops = {
+		.prepare = dyn_adc_capture_pcm_prepare,
+		.cleanup = dyn_adc_capture_pcm_cleanup
+	},
+};
+
+/* build PCM streams based on the parsed results */
+int snd_hda_gen_build_pcms(struct hda_codec *codec)
 {
-	struct hda_gspec *spec = codec->spec;
-	struct hda_pcm *info = &spec->pcm_rec;
+	struct hda_gen_spec *spec = codec->spec;
+	struct hda_pcm *info = spec->pcm_rec;
+	const struct hda_pcm_stream *p;
+	bool have_multi_adcs;
+	int i;
+
+	codec->num_pcms = 1;
+	codec->pcm_info = info;
+
+	if (spec->no_analog)
+		goto skip_analog;
+
+	snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
+		 "%s Analog", codec->chip_name);
+	info->name = spec->stream_name_analog;
+
+	if (spec->multiout.num_dacs > 0) {
+		p = spec->stream_analog_playback;
+		if (!p)
+			p = &pcm_analog_playback;
+		info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p;
+		info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
+		info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
+			spec->multiout.max_channels;
+		if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT &&
+		    spec->autocfg.line_outs == 2)
+			info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap =
+				snd_pcm_2_1_chmaps;
+	}
+	if (spec->num_adc_nids) {
+		p = spec->stream_analog_capture;
+		if (!p) {
+			if (spec->dyn_adc_switch)
+				p = &dyn_adc_pcm_analog_capture;
+			else
+				p = &pcm_analog_capture;
+		}
+		info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p;
+		info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
+	}
+
+	if (spec->channel_mode) {
+		info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 0;
+		for (i = 0; i < spec->num_channel_mode; i++) {
+			if (spec->channel_mode[i].channels > info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max) {
+				info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->channel_mode[i].channels;
+			}
+		}
+	}
+
+ skip_analog:
+	/* SPDIF for stream index #1 */
+	if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
+		snprintf(spec->stream_name_digital,
+			 sizeof(spec->stream_name_digital),
+			 "%s Digital", codec->chip_name);
+		codec->num_pcms = 2;
+		codec->slave_dig_outs = spec->multiout.slave_dig_outs;
+		info = spec->pcm_rec + 1;
+		info->name = spec->stream_name_digital;
+		if (spec->dig_out_type)
+			info->pcm_type = spec->dig_out_type;
+		else
+			info->pcm_type = HDA_PCM_TYPE_SPDIF;
+		if (spec->multiout.dig_out_nid) {
+			p = spec->stream_digital_playback;
+			if (!p)
+				p = &pcm_digital_playback;
+			info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p;
+			info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
+		}
+		if (spec->dig_in_nid) {
+			p = spec->stream_digital_capture;
+			if (!p)
+				p = &pcm_digital_capture;
+			info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p;
+			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
+		}
+	}
 
-	if (! spec->dac_node[0] && ! spec->adc_node) {
-		snd_printd("hda_generic: no PCM found\n");
+	if (spec->no_analog)
 		return 0;
+
+	/* If the use of more than one ADC is requested for the current
+	 * model, configure a second analog capture-only PCM.
+	 */
+	have_multi_adcs = (spec->num_adc_nids > 1) &&
+		!spec->dyn_adc_switch && !spec->auto_mic;
+	/* Additional Analaog capture for index #2 */
+	if (spec->alt_dac_nid || have_multi_adcs) {
+		codec->num_pcms = 3;
+		info = spec->pcm_rec + 2;
+		info->name = spec->stream_name_analog;
+		if (spec->alt_dac_nid) {
+			p = spec->stream_analog_alt_playback;
+			if (!p)
+				p = &pcm_analog_alt_playback;
+			info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p;
+			info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
+				spec->alt_dac_nid;
+		} else {
+			info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+				pcm_null_stream;
+			info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0;
+		}
+		if (have_multi_adcs) {
+			p = spec->stream_analog_alt_capture;
+			if (!p)
+				p = &pcm_analog_alt_capture;
+			info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p;
+			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
+				spec->adc_nids[1];
+			info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
+				spec->num_adc_nids - 1;
+		} else {
+			info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+				pcm_null_stream;
+			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 0;
+		}
 	}
 
-	codec->num_pcms = 1;
-	codec->pcm_info = info;
+	return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_gen_build_pcms);
+
+
+/*
+ * Standard auto-parser initializations
+ */
+
+/* configure the path from the given dac to the pin as the proper output */
+static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin,
+				  int pin_type, hda_nid_t dac)
+{
+	struct nid_path *path;
+
+	snd_hda_set_pin_ctl_cache(codec, pin, pin_type);
+	path = snd_hda_get_nid_path(codec, dac, pin);
+	if (!path)
+		return;
+	if (path->active)
+		return;
+	snd_hda_activate_path(codec, path, true, true);
+}
+
+/* initialize primary output paths */
+static void init_multi_out(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	int pin_type;
+	int i;
+
+	if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
+		pin_type = PIN_HP;
+	else
+		pin_type = PIN_OUT;
+
+	for (i = 0; i <= HDA_SIDE; i++) {
+		hda_nid_t nid = spec->autocfg.line_out_pins[i];
+		if (nid)
+			set_output_and_unmute(codec, nid, pin_type,
+					      spec->multiout.dac_nids[i]);
+
+	}
+}
+
+/* initialize hp and speaker paths */
+static void init_extra_out(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	int i;
+	hda_nid_t pin, dac;
+
+	for (i = 0; i < spec->autocfg.hp_outs; i++) {
+		if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
+			break;
+		pin = spec->autocfg.hp_pins[i];
+		if (!pin)
+			break;
+		dac = spec->multiout.hp_out_nid[i];
+		if (!dac) {
+			if (i > 0 && spec->multiout.hp_out_nid[0])
+				dac = spec->multiout.hp_out_nid[0];
+			else
+				dac = spec->multiout.dac_nids[0];
+		}
+		set_output_and_unmute(codec, pin, PIN_HP, dac);
+	}
+	for (i = 0; i < spec->autocfg.speaker_outs; i++) {
+		if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
+			break;
+		pin = spec->autocfg.speaker_pins[i];
+		if (!pin)
+			break;
+		dac = spec->multiout.extra_out_nid[i];
+		if (!dac) {
+			if (i > 0 && spec->multiout.extra_out_nid[0])
+				dac = spec->multiout.extra_out_nid[0];
+			else
+				dac = spec->multiout.dac_nids[0];
+		}
+		set_output_and_unmute(codec, pin, PIN_OUT, dac);
+	}
+}
+
+/* initialize multi-io paths */
+static void init_multi_io(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	int i;
+
+	for (i = 0; i < spec->multi_ios; i++) {
+		hda_nid_t pin = spec->multi_io[i].pin;
+		struct nid_path *path;
+		path = snd_hda_get_nid_path(codec, spec->multi_io[i].dac, pin);
+		if (!path)
+			continue;
+		if (!spec->multi_io[i].ctl_in)
+			spec->multi_io[i].ctl_in =
+				snd_hda_codec_update_cache(codec, pin, 0,
+					   AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+		snd_hda_activate_path(codec, path, path->active, true);
+	}
+}
+
+/* set up the input pin config, depending on the given auto-pin type */
+static void set_input_pin(struct hda_codec *codec, hda_nid_t nid,
+			  int auto_pin_type)
+{
+	unsigned int val = PIN_IN;
+	if (auto_pin_type == AUTO_PIN_MIC)
+		val |= snd_hda_get_default_vref(codec, nid);
+	snd_hda_set_pin_ctl(codec, nid, val);
+}
+
+/* set up input pins and loopback paths */
+static void init_analog_input(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	int i;
+
+	for (i = 0; i < cfg->num_inputs; i++) {
+		hda_nid_t nid = cfg->inputs[i].pin;
+		if (is_input_pin(codec, nid))
+			set_input_pin(codec, nid, cfg->inputs[i].type);
+
+		/* init loopback inputs */
+		if (spec->mixer_nid) {
+			struct nid_path *path;
+			path = snd_hda_get_nid_path(codec, nid, spec->mixer_nid);
+			if (path)
+				snd_hda_activate_path(codec, path,
+						      path->active, false);
+		}
+	}
+}
+
+/* initialize ADC paths */
+static void init_input_src(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	struct hda_input_mux *imux = &spec->input_mux;
+	struct nid_path *path;
+	int i, c, nums;
 
-	info->name = "HDA Generic";
-	if (spec->dac_node[0]) {
-		info->stream[0] = generic_pcm_playback;
-		info->stream[0].nid = spec->dac_node[0]->nid;
-		if (spec->dac_node[1]) {
-			info->stream[0].ops.prepare = generic_pcm2_prepare;
-			info->stream[0].ops.cleanup = generic_pcm2_cleanup;
+	if (spec->dyn_adc_switch)
+		nums = 1;
+	else
+		nums = spec->num_adc_nids;
+
+	for (c = 0; c < nums; c++) {
+		for (i = 0; i < imux->num_items; i++) {
+			path = snd_hda_get_nid_path(codec, spec->imux_pins[i],
+						    get_adc_nid(codec, c, i));
+			if (path) {
+				bool active = path->active;
+				if (i == spec->cur_mux[c])
+					active = true;
+				snd_hda_activate_path(codec, path, active, false);
+			}
 		}
 	}
-	if (spec->adc_node) {
-		info->stream[1] = generic_pcm_playback;
-		info->stream[1].nid = spec->adc_node->nid;
+
+	if (spec->shared_mic_hp)
+		update_shared_mic_hp(codec, spec->cur_mux[0]);
+
+	if (spec->cap_sync_hook)
+		spec->cap_sync_hook(codec);
+}
+
+/* set right pin controls for digital I/O */
+static void init_digital(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	int i;
+	hda_nid_t pin;
+
+	for (i = 0; i < spec->autocfg.dig_outs; i++) {
+		pin = spec->autocfg.dig_out_pins[i];
+		if (!pin)
+			continue;
+		set_output_and_unmute(codec, pin, PIN_OUT, 0);
 	}
+	pin = spec->autocfg.dig_in_pin;
+	if (pin)
+		snd_hda_set_pin_ctl(codec, pin, PIN_IN);
+}
+
+int snd_hda_gen_init(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+
+	if (spec->init_hook)
+		spec->init_hook(codec);
+
+	snd_hda_apply_verbs(codec);
+
+	init_multi_out(codec);
+	init_extra_out(codec);
+	init_multi_io(codec);
+	init_analog_input(codec);
+	init_input_src(codec);
+	init_digital(codec);
 
+	/* call init functions of standard auto-mute helpers */
+	hp_automute(codec, NULL);
+	line_automute(codec, NULL);
+	mic_autoswitch(codec, NULL);
+
+	if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook)
+		snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
+
+	hda_call_check_power_status(codec, 0x01);
 	return 0;
 }
+EXPORT_SYMBOL(snd_hda_gen_init);
+
+
+/*
+ * the generic codec support
+ */
 
 #ifdef CONFIG_PM
 static int generic_check_power_status(struct hda_codec *codec, hda_nid_t nid)
 {
-	struct hda_gspec *spec = codec->spec;
+	struct hda_gen_spec *spec = codec->spec;
 	return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
 }
 #endif
 
+static void generic_free(struct hda_codec *codec)
+{
+	snd_hda_gen_spec_free(codec->spec);
+	kfree(codec->spec);
+	codec->spec = NULL;
+}
 
-/*
- */
-static struct hda_codec_ops generic_patch_ops = {
-	.build_controls = build_generic_controls,
-	.build_pcms = build_generic_pcms,
-	.free = snd_hda_generic_free,
+static const struct hda_codec_ops generic_patch_ops = {
+	.build_controls = snd_hda_gen_build_controls,
+	.build_pcms = snd_hda_gen_build_pcms,
+	.init = snd_hda_gen_init,
+	.free = generic_free,
+	.unsol_event = snd_hda_jack_unsol_event,
 #ifdef CONFIG_PM
 	.check_power_status = generic_check_power_status,
 #endif
 };
 
-/*
- * the generic parser
- */
 int snd_hda_parse_generic_codec(struct hda_codec *codec)
 {
-	struct hda_gspec *spec;
+	struct hda_gen_spec *spec;
 	int err;
 
-	if(!codec->afg)
-		return 0;
-
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-	if (spec == NULL) {
-		printk(KERN_ERR "hda_generic: can't allocate spec\n");
+	if (!spec)
 		return -ENOMEM;
-	}
+	snd_hda_gen_spec_init(spec);
 	codec->spec = spec;
-	INIT_LIST_HEAD(&spec->nid_list);
-
-	if ((err = build_afg_tree(codec)) < 0)
-		goto error;
 
-	if ((err = parse_input(codec)) < 0 ||
-	    (err = parse_output(codec)) < 0)
+	err = snd_hda_gen_parse_auto_config(codec, NULL);
+	if (err < 0)
 		goto error;
 
 	codec->patch_ops = generic_patch_ops;
-
 	return 0;
 
- error:
-	snd_hda_generic_free(codec);
+error:
+	generic_free(codec);
 	return err;
 }
 EXPORT_SYMBOL(snd_hda_parse_generic_codec);
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
new file mode 100644
index 0000000..a9f4f63
--- /dev/null
+++ b/sound/pci/hda/hda_generic.h
@@ -0,0 +1,199 @@
+/*
+ * Generic BIOS auto-parser helper functions for HD-audio
+ *
+ * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de>
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __SOUND_HDA_GENERIC_H
+#define __SOUND_HDA_GENERIC_H
+
+/* unsol event tags */
+enum {
+	HDA_GEN_HP_EVENT, HDA_GEN_FRONT_EVENT, HDA_GEN_MIC_EVENT,
+	HDA_GEN_LAST_EVENT = HDA_GEN_MIC_EVENT
+};
+
+/* table entry for multi-io paths */
+struct hda_multi_io {
+	hda_nid_t pin;		/* multi-io widget pin NID */
+	hda_nid_t dac;		/* DAC to be connected */
+	unsigned int ctl_in;	/* cached input-pin control value */
+};
+
+/* Widget connection path
+ *
+ * For output, stored in the order of DAC -> ... -> pin,
+ * for input, pin -> ... -> ADC.
+ *
+ * idx[i] contains the source index number to select on of the widget path[i];
+ * e.g. idx[1] is the index of the DAC (path[0]) selected by path[1] widget
+ * multi[] indicates whether it's a selector widget with multi-connectors
+ * (i.e. the connection selection is mandatory)
+ * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
+ */
+
+#define MAX_NID_PATH_DEPTH	5
+
+enum {
+	NID_PATH_VOL_CTL,
+	NID_PATH_MUTE_CTL,
+	NID_PATH_BOOST_CTL,
+	NID_PATH_NUM_CTLS
+};
+
+struct nid_path {
+	int depth;
+	hda_nid_t path[MAX_NID_PATH_DEPTH];
+	unsigned char idx[MAX_NID_PATH_DEPTH];
+	unsigned char multi[MAX_NID_PATH_DEPTH];
+	unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */
+	bool active;
+};
+
+/* mic/line-in auto switching entry */
+
+#define MAX_AUTO_MIC_PINS	3
+
+struct automic_entry {
+	hda_nid_t pin;		/* pin */
+	int idx;		/* imux index, -1 = invalid */
+	unsigned int attr;	/* pin attribute (INPUT_PIN_ATTR_*) */
+};
+
+struct hda_gen_spec {
+	char stream_name_analog[32];	/* analog PCM stream */
+	const struct hda_pcm_stream *stream_analog_playback;
+	const struct hda_pcm_stream *stream_analog_capture;
+	const struct hda_pcm_stream *stream_analog_alt_playback;
+	const struct hda_pcm_stream *stream_analog_alt_capture;
+
+	char stream_name_digital[32];	/* digital PCM stream */
+	const struct hda_pcm_stream *stream_digital_playback;
+	const struct hda_pcm_stream *stream_digital_capture;
+
+	/* playback */
+	struct hda_multi_out multiout;	/* playback set-up
+					 * max_channels, dacs must be set
+					 * dig_out_nid and hp_nid are optional
+					 */
+	hda_nid_t alt_dac_nid;
+	hda_nid_t slave_dig_outs[3];	/* optional - for auto-parsing */
+	int dig_out_type;
+
+	/* capture */
+	unsigned int num_adc_nids;
+	hda_nid_t adc_nids[AUTO_CFG_MAX_OUTS];
+	hda_nid_t dig_in_nid;		/* digital-in NID; optional */
+	hda_nid_t mixer_nid;		/* analog-mixer NID */
+
+	/* capture setup for dynamic dual-adc switch */
+	hda_nid_t cur_adc;
+	unsigned int cur_adc_stream_tag;
+	unsigned int cur_adc_format;
+
+	/* capture source */
+	struct hda_input_mux input_mux;
+	unsigned int cur_mux[3];
+
+	/* channel model */
+	const struct hda_channel_mode *channel_mode;
+	int num_channel_mode;
+	int const_channel_count;	/* min. channel count (for speakers) */
+	int ext_channel_count;		/* current channel count for multi-io */
+
+	/* PCM information */
+	struct hda_pcm pcm_rec[3];	/* used in build_pcms() */
+
+	/* dynamic controls, init_verbs and input_mux */
+	struct auto_pin_cfg autocfg;
+	struct snd_array kctls;
+	hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
+	hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS];
+	unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS];
+	hda_nid_t shared_mic_vref_pin;
+
+	/* DAC list */
+	int num_all_dacs;
+	hda_nid_t all_dacs[16];
+
+	/* path list */
+	struct snd_array paths;
+
+	/* auto-mic stuff */
+	int am_num_entries;
+	struct automic_entry am_entry[MAX_AUTO_MIC_PINS];
+
+	/* for pin sensing */
+	unsigned int hp_jack_present:1;
+	unsigned int line_jack_present:1;
+	unsigned int master_mute:1;
+	unsigned int auto_mic:1;
+	unsigned int automute_speaker:1; /* automute speaker outputs */
+	unsigned int automute_lo:1; /* automute LO outputs */
+	unsigned int detect_hp:1;	/* Headphone detection enabled */
+	unsigned int detect_lo:1;	/* Line-out detection enabled */
+	unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */
+	unsigned int automute_lo_possible:1;	  /* there are line outs and HP */
+	unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */
+	unsigned int line_in_auto_switch:1; /* allow line-in auto switch */
+
+	/* other flags */
+	unsigned int need_dac_fix:1; /* need to limit DACs for multi channels */
+	unsigned int no_analog:1; /* digital I/O only */
+	unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */
+	unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */
+	unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */
+	unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */
+	unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */
+
+	unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */
+
+	/* for virtual master */
+	hda_nid_t vmaster_nid;
+	struct hda_vmaster_mute_hook vmaster_mute;
+#ifdef CONFIG_PM
+	struct hda_loopback_check loopback;
+	int num_loopbacks;
+	struct hda_amp_list loopback_list[8];
+#endif
+
+	/* multi-io */
+	int multi_ios;
+	struct hda_multi_io multi_io[4];
+
+	/* bind volumes */
+	struct snd_array bind_ctls;
+
+	/* hooks */
+	void (*init_hook)(struct hda_codec *codec);
+	void (*automute_hook)(struct hda_codec *codec);
+	void (*cap_sync_hook)(struct hda_codec *codec);
+};
+
+int snd_hda_gen_spec_init(struct hda_gen_spec *spec);
+void snd_hda_gen_spec_free(struct hda_gen_spec *spec);
+
+int snd_hda_gen_init(struct hda_codec *codec);
+
+struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec,
+				      hda_nid_t from_nid, hda_nid_t to_nid);
+bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
+			    hda_nid_t to_nid, int with_aa_mix,
+			    struct nid_path *path);
+struct nid_path *
+snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid,
+		     hda_nid_t to_nid, int with_aa_mix);
+void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path,
+			   bool enable, bool add_aamix);
+
+int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
+				  const hda_nid_t *ignore_nids);
+int snd_hda_gen_build_controls(struct hda_codec *codec);
+int snd_hda_gen_build_pcms(struct hda_codec *codec);
+
+#endif /* __SOUND_HDA_GENERIC_H */
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 047/112] ALSA: hda - Add EAPD control to generic parser
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (45 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 046/112] ALSA: hda - Merge Realtek parser code to generic parser Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 048/112] ALSA: hda - Export snd_hda_gen_add_kctl() Takashi Iwai
                   ` (67 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

Enable EAPD in output path initializations automatically unless the
new flag spec->own_eapd_ctl is set.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 6 ++++++
 sound/pci/hda/hda_generic.h | 1 +
 2 files changed, 7 insertions(+)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 2d19b91..31c5677 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -3333,6 +3333,7 @@ EXPORT_SYMBOL_HDA(snd_hda_gen_build_pcms);
 static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin,
 				  int pin_type, hda_nid_t dac)
 {
+	struct hda_gen_spec *spec = codec->spec;
 	struct nid_path *path;
 
 	snd_hda_set_pin_ctl_cache(codec, pin, pin_type);
@@ -3342,6 +3343,11 @@ static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin,
 	if (path->active)
 		return;
 	snd_hda_activate_path(codec, path, true, true);
+
+	if (!spec->own_eapd_ctl &&
+	    (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD))
+		snd_hda_codec_update_cache(codec, pin, 0,
+					   AC_VERB_SET_EAPD_BTLENABLE, 0x02);
 }
 
 /* initialize primary output paths */
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index a9f4f63..9c00bd5 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -150,6 +150,7 @@ struct hda_gen_spec {
 	unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */
 	unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */
 	unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */
+	unsigned int own_eapd_ctl:1; /* set EAPD by own function */
 
 	unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */
 
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 048/112] ALSA: hda - Export snd_hda_gen_add_kctl()
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (46 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 047/112] ALSA: hda - Add EAPD control " Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 049/112] ALSA: hda - Move the call of snd_hda_parse_pin_defcfg() from snd_hda_gen_parse_auto_config() Takashi Iwai
                   ` (66 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

It may be used in other codec drivers, so let it free.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 19 ++++++++++---------
 sound/pci/hda/hda_generic.h |  4 ++++
 2 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 31c5677..49e968c 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -43,9 +43,9 @@ int snd_hda_gen_spec_init(struct hda_gen_spec *spec)
 }
 EXPORT_SYMBOL_HDA(snd_hda_gen_spec_init);
 
-static struct snd_kcontrol_new *
-add_kctl(struct hda_gen_spec *spec, const char *name,
-	 const struct snd_kcontrol_new *temp)
+struct snd_kcontrol_new *
+snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name,
+		     const struct snd_kcontrol_new *temp)
 {
 	struct snd_kcontrol_new *knew = snd_array_new(&spec->kctls);
 	if (!knew)
@@ -59,6 +59,7 @@ add_kctl(struct hda_gen_spec *spec, const char *name,
 		return NULL;
 	return knew;
 }
+EXPORT_SYMBOL_HDA(snd_hda_gen_add_kctl);
 
 static void free_kctls(struct hda_gen_spec *spec)
 {
@@ -548,7 +549,7 @@ static int add_control(struct hda_gen_spec *spec, int type, const char *name,
 {
 	struct snd_kcontrol_new *knew;
 
-	knew = add_kctl(spec, name, &control_templates[type]);
+	knew = snd_hda_gen_add_kctl(spec, name, &control_templates[type]);
 	if (!knew)
 		return -ENOMEM;
 	knew->index = cidx;
@@ -1527,7 +1528,7 @@ static int create_multi_channel_mode(struct hda_codec *codec)
 	struct hda_gen_spec *spec = codec->spec;
 
 	if (spec->multi_ios > 0) {
-		if (!add_kctl(spec, NULL, &channel_mode_enum))
+		if (!snd_hda_gen_add_kctl(spec, NULL, &channel_mode_enum))
 			return -ENOMEM;
 	}
 	return 0;
@@ -2086,7 +2087,7 @@ static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx,
 	struct snd_kcontrol_new *knew;
 
 	if (vol_ctl) {
-		knew = add_kctl(spec, NULL, &cap_vol_temp);
+		knew = snd_hda_gen_add_kctl(spec, NULL, &cap_vol_temp);
 		if (!knew)
 			return -ENOMEM;
 		knew->index = idx;
@@ -2094,7 +2095,7 @@ static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx,
 		knew->subdevice = HDA_SUBDEV_AMP_FLAG;
 	}
 	if (sw_ctl) {
-		knew = add_kctl(spec, NULL, &cap_sw_temp);
+		knew = snd_hda_gen_add_kctl(spec, NULL, &cap_sw_temp);
 		if (!knew)
 			return -ENOMEM;
 		knew->index = idx;
@@ -2171,7 +2172,7 @@ static int create_capture_mixers(struct hda_codec *codec)
 
 	if (!spec->auto_mic && imux->num_items > 1) {
 		struct snd_kcontrol_new *knew;
-		knew = add_kctl(spec, NULL, &cap_src_temp);
+		knew = snd_hda_gen_add_kctl(spec, NULL, &cap_src_temp);
 		if (!knew)
 			return -ENOMEM;
 		knew->count = nums;
@@ -2592,7 +2593,7 @@ static int add_automute_mode_enum(struct hda_codec *codec)
 {
 	struct hda_gen_spec *spec = codec->spec;
 
-	if (!add_kctl(spec, NULL, &automute_mode_enum))
+	if (!snd_hda_gen_add_kctl(spec, NULL, &automute_mode_enum))
 		return -ENOMEM;
 	return 0;
 }
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index 9c00bd5..d71e86d 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -192,6 +192,10 @@ snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid,
 void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path,
 			   bool enable, bool add_aamix);
 
+struct snd_kcontrol_new *
+snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name,
+		     const struct snd_kcontrol_new *temp);
+
 int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
 				  const hda_nid_t *ignore_nids);
 int snd_hda_gen_build_controls(struct hda_codec *codec);
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 049/112] ALSA: hda - Move the call of snd_hda_parse_pin_defcfg() from snd_hda_gen_parse_auto_config()
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (47 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 048/112] ALSA: hda - Export snd_hda_gen_add_kctl() Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 050/112] ALSA: hda - Fix NULL dereference in snd_hda_gen_build_controls() Takashi Iwai
                   ` (65 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

In some cases, we want to manipulate the auto_pin_cfg table before
passing to snd_hda_gen_parse_auto_config() (e.g. Realtek SSID check
code fiddles with the headphone pin).   Also passing ignore_pins just
for snd_hda_parse_pin_defcfg() isn't good.

In this patch, snd_hda_gen_parse_auto_config() is changed to receive
the auto_pin_cfg table to be parsed.  The passed auto_pin_cfg table
must have been initialized (typically by calling
snd_hda_gen_parse_auto_config()) beforehand by the caller.

Also together with this change, spec->parse_flags is also removed.
Since this was referred only at the place calling
snd_hda_parse_pin_defcfg(), no longer needed to be kept in spec.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 24 +++++++++++++++---------
 sound/pci/hda/hda_generic.h |  4 +---
 2 files changed, 16 insertions(+), 12 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 49e968c..e512cab 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -2785,21 +2785,23 @@ static int check_auto_mic_availability(struct hda_codec *codec)
 }
 
 
-/* parse the BIOS configuration and set up the hda_gen_spec */
-/* return 1 if successful, 0 if the proper config is not found,
+/*
+ * Parse the given BIOS configuration and set up the hda_gen_spec
+ *
+ * return 1 if successful, 0 if the proper config is not found,
  * or a negative error code
  */
 int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
-				  const hda_nid_t *ignore_nids)
+				  struct auto_pin_cfg *cfg)
 {
 	struct hda_gen_spec *spec = codec->spec;
-	struct auto_pin_cfg *cfg = &spec->autocfg;
 	int err;
 
-	err = snd_hda_parse_pin_defcfg(codec, cfg, ignore_nids,
-				       spec->parse_flags);
-	if (err < 0)
-		return err;
+	if (cfg != &spec->autocfg) {
+		spec->autocfg = *cfg;
+		cfg = &spec->autocfg;
+	}
+
 	if (!cfg->line_outs) {
 		if (cfg->dig_outs || cfg->dig_in_pin) {
 			spec->multiout.max_channels = 2;
@@ -3586,7 +3588,11 @@ int snd_hda_parse_generic_codec(struct hda_codec *codec)
 	snd_hda_gen_spec_init(spec);
 	codec->spec = spec;
 
-	err = snd_hda_gen_parse_auto_config(codec, NULL);
+	err = snd_hda_parse_pin_defcfg(codec, &spec->autocfg, NULL, 0);
+	if (err < 0)
+		return err;
+
+	err = snd_hda_gen_parse_auto_config(codec, &spec->autocfg);
 	if (err < 0)
 		goto error;
 
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index d71e86d..1a3b404 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -152,8 +152,6 @@ struct hda_gen_spec {
 	unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */
 	unsigned int own_eapd_ctl:1; /* set EAPD by own function */
 
-	unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */
-
 	/* for virtual master */
 	hda_nid_t vmaster_nid;
 	struct hda_vmaster_mute_hook vmaster_mute;
@@ -197,7 +195,7 @@ snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name,
 		     const struct snd_kcontrol_new *temp);
 
 int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
-				  const hda_nid_t *ignore_nids);
+				  struct auto_pin_cfg *cfg);
 int snd_hda_gen_build_controls(struct hda_codec *codec);
 int snd_hda_gen_build_pcms(struct hda_codec *codec);
 
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 050/112] ALSA: hda - Fix NULL dereference in snd_hda_gen_build_controls()
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (48 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 049/112] ALSA: hda - Move the call of snd_hda_parse_pin_defcfg() from snd_hda_gen_parse_auto_config() Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 051/112] ALSA: hda - Export standard jack event handlers for generic parser Takashi Iwai
                   ` (64 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

When no controls are assigned in the parser (e.g. no analog path),
spec->kctls.list is still NULL.  We need to check it before passing to
snd_hda_add_new_ctls().

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index e512cab..364ec06 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -2906,9 +2906,11 @@ int snd_hda_gen_build_controls(struct hda_codec *codec)
 	struct hda_gen_spec *spec = codec->spec;
 	int err;
 
-	err = snd_hda_add_new_ctls(codec, spec->kctls.list);
-	if (err < 0)
-		return err;
+	if (spec->kctls.used) {
+		err = snd_hda_add_new_ctls(codec, spec->kctls.list);
+		if (err < 0)
+			return err;
+	}
 
 	if (spec->multiout.dig_out_nid) {
 		err = snd_hda_create_dig_out_ctls(codec,
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 051/112] ALSA: hda - Export standard jack event handlers for generic parser
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (49 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 050/112] ALSA: hda - Fix NULL dereference in snd_hda_gen_build_controls() Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 052/112] ALSA: hda - Use generic parser codes for Realtek driver Takashi Iwai
                   ` (63 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

These handlers are supposed to be called externally from the codec
drivers once when they need to handle own jack events.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 26 +++++++++++++++-----------
 sound/pci/hda/hda_generic.h |  9 +++++++++
 2 files changed, 24 insertions(+), 11 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 364ec06..6914d70 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -2414,7 +2414,7 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
 }
 
 /* Toggle outputs muting */
-static void update_outputs(struct hda_codec *codec)
+void snd_hda_gen_update_outputs(struct hda_codec *codec)
 {
 	struct hda_gen_spec *spec = codec->spec;
 	int on;
@@ -2448,6 +2448,7 @@ static void update_outputs(struct hda_codec *codec)
 	do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins),
 		    spec->autocfg.line_out_pins, on, false);
 }
+EXPORT_SYMBOL_HDA(snd_hda_gen_update_outputs);
 
 static void call_update_outputs(struct hda_codec *codec)
 {
@@ -2455,11 +2456,11 @@ static void call_update_outputs(struct hda_codec *codec)
 	if (spec->automute_hook)
 		spec->automute_hook(codec);
 	else
-		update_outputs(codec);
+		snd_hda_gen_update_outputs(codec);
 }
 
 /* standard HP-automute helper */
-static void hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
+void snd_hda_gen_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
 {
 	struct hda_gen_spec *spec = codec->spec;
 
@@ -2470,9 +2471,10 @@ static void hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
 		return;
 	call_update_outputs(codec);
 }
+EXPORT_SYMBOL_HDA(snd_hda_gen_hp_automute);
 
 /* standard line-out-automute helper */
-static void line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
+void snd_hda_gen_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
 {
 	struct hda_gen_spec *spec = codec->spec;
 
@@ -2489,9 +2491,10 @@ static void line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
 		return;
 	call_update_outputs(codec);
 }
+EXPORT_SYMBOL_HDA(snd_hda_gen_line_automute);
 
 /* standard mic auto-switch helper */
-static void mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *jack)
+void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *jack)
 {
 	struct hda_gen_spec *spec = codec->spec;
 	int i;
@@ -2507,6 +2510,7 @@ static void mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *jack)
 	}
 	mux_select(codec, 0, spec->am_entry[0].idx);
 }
+EXPORT_SYMBOL_HDA(snd_hda_gen_mic_autoswitch);
 
 /*
  * Auto-Mute mode mixer enum support
@@ -2639,7 +2643,7 @@ static int check_auto_mute_availability(struct hda_codec *codec)
 		snd_printdd("hda-codec: Enable HP auto-muting on NID 0x%x\n",
 			    nid);
 		snd_hda_jack_detect_enable_callback(codec, nid, HDA_GEN_HP_EVENT,
-						    hp_automute);
+						    snd_hda_gen_hp_automute);
 		spec->detect_hp = 1;
 	}
 
@@ -2652,7 +2656,7 @@ static int check_auto_mute_availability(struct hda_codec *codec)
 				snd_printdd("hda-codec: Enable Line-Out auto-muting on NID 0x%x\n", nid);
 				snd_hda_jack_detect_enable_callback(codec, nid,
 								    HDA_GEN_FRONT_EVENT,
-								    line_automute);
+								    snd_hda_gen_line_automute);
 				spec->detect_lo = 1;
 			}
 		spec->automute_lo_possible = spec->detect_hp;
@@ -2704,7 +2708,7 @@ static bool auto_mic_check_imux(struct hda_codec *codec)
 		snd_hda_jack_detect_enable_callback(codec,
 						    spec->am_entry[i].pin,
 						    HDA_GEN_MIC_EVENT,
-						    mic_autoswitch);
+						    snd_hda_gen_mic_autoswitch);
 	return true;
 }
 
@@ -3536,9 +3540,9 @@ int snd_hda_gen_init(struct hda_codec *codec)
 	init_digital(codec);
 
 	/* call init functions of standard auto-mute helpers */
-	hp_automute(codec, NULL);
-	line_automute(codec, NULL);
-	mic_autoswitch(codec, NULL);
+	snd_hda_gen_hp_automute(codec, NULL);
+	snd_hda_gen_line_automute(codec, NULL);
+	snd_hda_gen_mic_autoswitch(codec, NULL);
 
 	if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook)
 		snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index 1a3b404..417ab65 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -199,4 +199,13 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
 int snd_hda_gen_build_controls(struct hda_codec *codec);
 int snd_hda_gen_build_pcms(struct hda_codec *codec);
 
+/* standard jack event callbacks */
+void snd_hda_gen_hp_automute(struct hda_codec *codec,
+			     struct hda_jack_tbl *jack);
+void snd_hda_gen_line_automute(struct hda_codec *codec,
+			       struct hda_jack_tbl *jack);
+void snd_hda_gen_mic_autoswitch(struct hda_codec *codec,
+				struct hda_jack_tbl *jack);
+void snd_hda_gen_update_outputs(struct hda_codec *codec);
+
 #endif /* __SOUND_HDA_GENERIC_H */
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 052/112] ALSA: hda - Use generic parser codes for Realtek driver
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (50 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 051/112] ALSA: hda - Export standard jack event handlers for generic parser Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 053/112] ALSA: hda - Use "Capture Source" for single sources Takashi Iwai
                   ` (62 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

The next migration step is to use the common code in generic driver
for Realtek driver.  This is no drastic change and there should be no
real functional changes, as the generic parser code comes from Realtek
driver originally.

As Realtek driver requires the generic parser code, it needs a
reverse-selection of CONFIG_SND_HDA_GENERIC kconfig.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/Kconfig         |    1 +
 sound/pci/hda/patch_realtek.c | 4102 +++--------------------------------------
 2 files changed, 298 insertions(+), 3805 deletions(-)

diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index 6eeb889..ebec1b7 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -86,6 +86,7 @@ config SND_HDA_PATCH_LOADER
 config SND_HDA_CODEC_REALTEK
 	bool "Build Realtek HD-audio codec support"
 	default y
+	select SND_HDA_GENERIC
 	help
 	  Say Y here to include Realtek HD-audio codec support in
 	  snd-hda-intel driver, such as ALC880.
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 2240ab6..17da84d 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -36,12 +36,10 @@
 #include "hda_auto_parser.h"
 #include "hda_beep.h"
 #include "hda_jack.h"
+#include "hda_generic.h"
 
 /* unsol event tags */
-#define ALC_FRONT_EVENT		0x01
-#define ALC_DCVOL_EVENT		0x02
-#define ALC_HP_EVENT		0x04
-#define ALC_MIC_EVENT		0x08
+#define ALC_DCVOL_EVENT		0x08
 
 /* for GPIO Poll */
 #define GPIO_MASK	0x03
@@ -68,12 +66,6 @@ struct alc_customize_define {
 	unsigned int  fixup:1; /* Means that this sku is set by driver, not read from hw */
 };
 
-struct alc_multi_io {
-	hda_nid_t pin;		/* multi-io widget pin NID */
-	hda_nid_t dac;		/* DAC to be connected */
-	unsigned int ctl_in;	/* cached input-pin control value */
-};
-
 /* make compatible with old code */
 #define alc_apply_pincfgs	snd_hda_apply_pincfgs
 #define alc_apply_fixup		snd_hda_apply_fixup
@@ -91,112 +83,21 @@ struct alc_multi_io {
 #define ALC_FIXUP_ACT_INIT	HDA_FIXUP_ACT_INIT
 #define ALC_FIXUP_ACT_BUILD	HDA_FIXUP_ACT_BUILD
 
-#define MAX_AUTO_MIC_PINS	3
-
-struct alc_automic_entry {
-	hda_nid_t pin;		/* pin */
-	int idx;		/* imux index, -1 = invalid */
-	unsigned int attr;	/* pin attribute (INPUT_PIN_ATTR_*) */
-};
-
-#define MAX_NID_PATH_DEPTH	5
-
-enum {
-	NID_PATH_VOL_CTL,
-	NID_PATH_MUTE_CTL,
-	NID_PATH_BOOST_CTL,
-	NID_PATH_NUM_CTLS
-};
-
-/* Widget connection path
- *
- * For output, stored in the order of DAC -> ... -> pin,
- * for input, pin -> ... -> ADC.
- *
- * idx[i] contains the source index number to select on of the widget path[i];
- * e.g. idx[1] is the index of the DAC (path[0]) selected by path[1] widget
- * multi[] indicates whether it's a selector widget with multi-connectors
- * (i.e. the connection selection is mandatory)
- * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
- */
-struct nid_path {
-	int depth;
-	hda_nid_t path[MAX_NID_PATH_DEPTH];
-	unsigned char idx[MAX_NID_PATH_DEPTH];
-	unsigned char multi[MAX_NID_PATH_DEPTH];
-	unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */
-	bool active;
-};
-
 struct alc_spec {
+	struct hda_gen_spec gen; /* must be at head */
+
 	/* codec parameterization */
 	const struct snd_kcontrol_new *mixers[5];	/* mixer arrays */
 	unsigned int num_mixers;
 	unsigned int beep_amp;	/* beep amp value, set via set_beep_amp() */
 
-	char stream_name_analog[32];	/* analog PCM stream */
-	const struct hda_pcm_stream *stream_analog_playback;
-	const struct hda_pcm_stream *stream_analog_capture;
-	const struct hda_pcm_stream *stream_analog_alt_playback;
-	const struct hda_pcm_stream *stream_analog_alt_capture;
-
-	char stream_name_digital[32];	/* digital PCM stream */
-	const struct hda_pcm_stream *stream_digital_playback;
-	const struct hda_pcm_stream *stream_digital_capture;
-
-	/* playback */
-	struct hda_multi_out multiout;	/* playback set-up
-					 * max_channels, dacs must be set
-					 * dig_out_nid and hp_nid are optional
-					 */
-	hda_nid_t alt_dac_nid;
-	hda_nid_t slave_dig_outs[3];	/* optional - for auto-parsing */
-	int dig_out_type;
-
-	/* capture */
-	unsigned int num_adc_nids;
-	hda_nid_t adc_nids[AUTO_CFG_MAX_OUTS];
-	hda_nid_t dig_in_nid;		/* digital-in NID; optional */
-	hda_nid_t mixer_nid;		/* analog-mixer NID */
-
-	/* capture setup for dynamic dual-adc switch */
-	hda_nid_t cur_adc;
-	unsigned int cur_adc_stream_tag;
-	unsigned int cur_adc_format;
-
-	/* capture source */
-	struct hda_input_mux input_mux;
-	unsigned int cur_mux[3];
-
-	/* channel model */
-	const struct hda_channel_mode *channel_mode;
-	int num_channel_mode;
-	int const_channel_count;	/* min. channel count (for speakers) */
-	int ext_channel_count;		/* current channel count for multi-io */
-
-	/* PCM information */
-	struct hda_pcm pcm_rec[3];	/* used in alc_build_pcms() */
-
-	/* dynamic controls, init_verbs and input_mux */
-	struct auto_pin_cfg autocfg;
 	struct alc_customize_define cdefine;
-	struct snd_array kctls;
-	hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
-	hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS];
-	unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS];
-	hda_nid_t inv_dmic_pin;
-	hda_nid_t shared_mic_vref_pin;
+	unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
 
-	/* DAC list */
-	int num_all_dacs;
-	hda_nid_t all_dacs[16];
-
-	/* path list */
-	struct snd_array paths;
-
-	/* auto-mic stuff */
-	int am_num_entries;
-	struct alc_automic_entry am_entry[MAX_AUTO_MIC_PINS];
+	/* inverted dmic fix */
+	unsigned int inv_dmic_fixup:1; /* has inverted digital-mic workaround */
+	unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */
+	hda_nid_t inv_dmic_pin;
 
 	/* hooks */
 	void (*init_hook)(struct hda_codec *codec);
@@ -204,224 +105,16 @@ struct alc_spec {
 	void (*power_hook)(struct hda_codec *codec);
 #endif
 	void (*shutup)(struct hda_codec *codec);
-	void (*automute_hook)(struct hda_codec *codec);
-
-	/* for pin sensing */
-	unsigned int hp_jack_present:1;
-	unsigned int line_jack_present:1;
-	unsigned int master_mute:1;
-	unsigned int auto_mic:1;
-	unsigned int automute_speaker:1; /* automute speaker outputs */
-	unsigned int automute_lo:1; /* automute LO outputs */
-	unsigned int detect_hp:1;	/* Headphone detection enabled */
-	unsigned int detect_lo:1;	/* Line-out detection enabled */
-	unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */
-	unsigned int automute_lo_possible:1;	  /* there are line outs and HP */
-	unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */
-	unsigned int line_in_auto_switch:1; /* allow line-in auto switch */
-
-	/* other flags */
-	unsigned int need_dac_fix:1; /* need to limit DACs for multi channels */
-	unsigned int no_analog :1; /* digital I/O only */
-	unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */
-	unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */
-	unsigned int inv_dmic_fixup:1; /* has inverted digital-mic workaround */
-	unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */
-	unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */
-	unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */
-	unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */
-
-	unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */
 
 	int init_amp;
 	int codec_variant;	/* flag for other variants */
 
-	/* for virtual master */
-	hda_nid_t vmaster_nid;
-	struct hda_vmaster_mute_hook vmaster_mute;
-#ifdef CONFIG_PM
-	struct hda_loopback_check loopback;
-	int num_loopbacks;
-	struct hda_amp_list loopback_list[8];
-#endif
-
 	/* for PLL fix */
 	hda_nid_t pll_nid;
 	unsigned int pll_coef_idx, pll_coef_bit;
 	unsigned int coef0;
-
-	/* multi-io */
-	int multi_ios;
-	struct alc_multi_io multi_io[4];
-
-	/* bind volumes */
-	struct snd_array bind_ctls;
 };
 
-static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid,
-			   int dir, unsigned int bits)
-{
-	if (!nid)
-		return false;
-	if (get_wcaps(codec, nid) & (1 << (dir + 1)))
-		if (query_amp_caps(codec, nid, dir) & bits)
-			return true;
-	return false;
-}
-
-#define nid_has_mute(codec, nid, dir) \
-	check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
-#define nid_has_volume(codec, nid, dir) \
-	check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS)
-
-static struct nid_path *
-get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid);
-static void activate_path(struct hda_codec *codec, struct nid_path *path,
-			  bool enable, bool add_aamix);
-
-/*
- * input MUX handling
- */
-static int alc_mux_enum_info(struct snd_kcontrol *kcontrol,
-			     struct snd_ctl_elem_info *uinfo)
-{
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct alc_spec *spec = codec->spec;
-	return snd_hda_input_mux_info(&spec->input_mux, uinfo);
-}
-
-static int alc_mux_enum_get(struct snd_kcontrol *kcontrol,
-			    struct snd_ctl_elem_value *ucontrol)
-{
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct alc_spec *spec = codec->spec;
-	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
-	ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
-	return 0;
-}
-
-static hda_nid_t get_adc_nid(struct hda_codec *codec, int adc_idx, int imux_idx)
-{
-	struct alc_spec *spec = codec->spec;
-	if (spec->dyn_adc_switch)
-		adc_idx = spec->dyn_adc_idx[imux_idx];
-	return spec->adc_nids[adc_idx];
-}
-
-static bool alc_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
-{
-	struct alc_spec *spec = codec->spec;
-	hda_nid_t new_adc = spec->adc_nids[spec->dyn_adc_idx[cur]];
-
-	if (spec->cur_adc && spec->cur_adc != new_adc) {
-		/* stream is running, let's swap the current ADC */
-		__snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
-		spec->cur_adc = new_adc;
-		snd_hda_codec_setup_stream(codec, new_adc,
-					   spec->cur_adc_stream_tag, 0,
-					   spec->cur_adc_format);
-		return true;
-	}
-	return false;
-}
-
-static void call_update_outputs(struct hda_codec *codec);
-static void alc_inv_dmic_sync(struct hda_codec *codec, bool force);
-static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx);
-
-/* for shared I/O, change the pin-control accordingly */
-static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic)
-{
-	struct alc_spec *spec = codec->spec;
-	unsigned int val;
-	hda_nid_t pin = spec->autocfg.inputs[1].pin;
-	/* NOTE: this assumes that there are only two inputs, the
-	 * first is the real internal mic and the second is HP/mic jack.
-	 */
-
-	val = snd_hda_get_default_vref(codec, pin);
-
-	/* This pin does not have vref caps - let's enable vref on pin 0x18
-	   instead, as suggested by Realtek */
-	if (val == AC_PINCTL_VREF_HIZ && spec->shared_mic_vref_pin) {
-		const hda_nid_t vref_pin = spec->shared_mic_vref_pin;
-		unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin);
-		if (vref_val != AC_PINCTL_VREF_HIZ)
-			snd_hda_set_pin_ctl(codec, vref_pin, PIN_IN | (set_as_mic ? vref_val : 0));
-	}
-
-	val = set_as_mic ? val | PIN_IN : PIN_HP;
-	snd_hda_set_pin_ctl(codec, pin, val);
-
-	spec->automute_speaker = !set_as_mic;
-	call_update_outputs(codec);
-}
-
-/* select the given imux item; either unmute exclusively or select the route */
-static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx,
-			  unsigned int idx)
-{
-	struct alc_spec *spec = codec->spec;
-	const struct hda_input_mux *imux;
-	struct nid_path *path;
-
-	imux = &spec->input_mux;
-	if (!imux->num_items)
-		return 0;
-
-	if (idx >= imux->num_items)
-		idx = imux->num_items - 1;
-	if (spec->cur_mux[adc_idx] == idx)
-		return 0;
-
-	path = get_nid_path(codec, spec->imux_pins[spec->cur_mux[adc_idx]],
-			    spec->adc_nids[adc_idx]);
-	if (!path)
-		return 0;
-	if (path->active)
-		activate_path(codec, path, false, false);
-
-	spec->cur_mux[adc_idx] = idx;
-
-	if (spec->shared_mic_hp)
-		update_shared_mic_hp(codec, spec->cur_mux[adc_idx]);
-
-	if (spec->dyn_adc_switch)
-		alc_dyn_adc_pcm_resetup(codec, idx);
-
-	path = get_nid_path(codec, spec->imux_pins[idx],
-			    get_adc_nid(codec, adc_idx, idx));
-	if (!path)
-		return 0;
-	if (path->active)
-		return 0;
-	activate_path(codec, path, true, false);
-	alc_inv_dmic_sync(codec, true);
-	return 1;
-}
-
-static int alc_mux_enum_put(struct snd_kcontrol *kcontrol,
-			    struct snd_ctl_elem_value *ucontrol)
-{
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-	return alc_mux_select(codec, adc_idx,
-			      ucontrol->value.enumerated.item[0]);
-}
-
-/*
- * set up the input pin config (depending on the given auto-pin type)
- */
-static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid,
-			      int auto_pin_type)
-{
-	unsigned int val = PIN_IN;
-	if (auto_pin_type == AUTO_PIN_MIC)
-		val |= snd_hda_get_default_vref(codec, nid);
-	snd_hda_set_pin_ctl(codec, nid, val);
-}
-
 /*
  * Append the given mixer and verb elements for the later use
  * The mixer array is referred in build_controls(), and init_verbs are
@@ -491,146 +184,6 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid,
 	alc_fix_pll(codec);
 }
 
-/*
- * Jack detections for HP auto-mute and mic-switch
- */
-
-/* check each pin in the given array; returns true if any of them is plugged */
-static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins)
-{
-	int i, present = 0;
-
-	for (i = 0; i < num_pins; i++) {
-		hda_nid_t nid = pins[i];
-		if (!nid)
-			break;
-		present |= snd_hda_jack_detect(codec, nid);
-	}
-	return present;
-}
-
-/* standard HP/line-out auto-mute helper */
-static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
-			bool mute, bool hp_out)
-{
-	struct alc_spec *spec = codec->spec;
-	unsigned int pin_bits = mute ? 0 : (hp_out ? PIN_HP : PIN_OUT);
-	int i;
-
-	for (i = 0; i < num_pins; i++) {
-		hda_nid_t nid = pins[i];
-		unsigned int val;
-		if (!nid)
-			break;
-		/* don't reset VREF value in case it's controlling
-		 * the amp (see alc861_fixup_asus_amp_vref_0f())
-		 */
-		if (spec->keep_vref_in_automute) {
-			val = snd_hda_codec_read(codec, nid, 0,
-					AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-			val &= ~PIN_HP;
-		} else
-			val = 0;
-		val |= pin_bits;
-		snd_hda_set_pin_ctl(codec, nid, val);
-	}
-}
-
-/* Toggle outputs muting */
-static void update_outputs(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	int on;
-
-	/* Control HP pins/amps depending on master_mute state;
-	 * in general, HP pins/amps control should be enabled in all cases,
-	 * but currently set only for master_mute, just to be safe
-	 */
-	if (!spec->shared_mic_hp) /* don't change HP-pin when shared with mic */
-		do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins),
-		    spec->autocfg.hp_pins, spec->master_mute, true);
-
-	if (!spec->automute_speaker)
-		on = 0;
-	else
-		on = spec->hp_jack_present | spec->line_jack_present;
-	on |= spec->master_mute;
-	do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins),
-		    spec->autocfg.speaker_pins, on, false);
-
-	/* toggle line-out mutes if needed, too */
-	/* if LO is a copy of either HP or Speaker, don't need to handle it */
-	if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0] ||
-	    spec->autocfg.line_out_pins[0] == spec->autocfg.speaker_pins[0])
-		return;
-	if (!spec->automute_lo)
-		on = 0;
-	else
-		on = spec->hp_jack_present;
-	on |= spec->master_mute;
-	do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins),
-		    spec->autocfg.line_out_pins, on, false);
-}
-
-static void call_update_outputs(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	if (spec->automute_hook)
-		spec->automute_hook(codec);
-	else
-		update_outputs(codec);
-}
-
-/* standard HP-automute helper */
-static void alc_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
-{
-	struct alc_spec *spec = codec->spec;
-
-	spec->hp_jack_present =
-		detect_jacks(codec, ARRAY_SIZE(spec->autocfg.hp_pins),
-			     spec->autocfg.hp_pins);
-	if (!spec->detect_hp || (!spec->automute_speaker && !spec->automute_lo))
-		return;
-	call_update_outputs(codec);
-}
-
-/* standard line-out-automute helper */
-static void alc_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
-{
-	struct alc_spec *spec = codec->spec;
-
-	if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
-		return;
-	/* check LO jack only when it's different from HP */
-	if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0])
-		return;
-
-	spec->line_jack_present =
-		detect_jacks(codec, ARRAY_SIZE(spec->autocfg.line_out_pins),
-			     spec->autocfg.line_out_pins);
-	if (!spec->automute_speaker || !spec->detect_lo)
-		return;
-	call_update_outputs(codec);
-}
-
-/* standard mic auto-switch helper */
-static void alc_mic_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
-{
-	struct alc_spec *spec = codec->spec;
-	int i;
-
-	if (!spec->auto_mic)
-		return;
-
-	for (i = spec->am_num_entries - 1; i > 0; i--) {
-		if (snd_hda_jack_detect(codec, spec->am_entry[i].pin)) {
-			alc_mux_select(codec, 0, spec->am_entry[i].idx);
-			return;
-		}
-	}
-	alc_mux_select(codec, 0, spec->am_entry[0].idx);
-}
-
 /* update the master volume per volume-knob's unsol event */
 static void alc_update_knob_master(struct hda_codec *codec, struct hda_jack_tbl *jack)
 {
@@ -780,356 +333,64 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type)
 	}
 }
 
+
 /*
- * Auto-Mute mode mixer enum support
+ * Realtek SSID verification
  */
-static int alc_automute_mode_info(struct snd_kcontrol *kcontrol,
-				  struct snd_ctl_elem_info *uinfo)
-{
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct alc_spec *spec = codec->spec;
-	static const char * const texts3[] = {
-		"Disabled", "Speaker Only", "Line Out+Speaker"
-	};
-
-	if (spec->automute_speaker_possible && spec->automute_lo_possible)
-		return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3);
-	return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
-}
-
-static int alc_automute_mode_get(struct snd_kcontrol *kcontrol,
-				 struct snd_ctl_elem_value *ucontrol)
-{
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct alc_spec *spec = codec->spec;
-	unsigned int val = 0;
-	if (spec->automute_speaker)
-		val++;
-	if (spec->automute_lo)
-		val++;
 
-	ucontrol->value.enumerated.item[0] = val;
-	return 0;
-}
+/* Could be any non-zero and even value. When used as fixup, tells
+ * the driver to ignore any present sku defines.
+ */
+#define ALC_FIXUP_SKU_IGNORE (2)
 
-static int alc_automute_mode_put(struct snd_kcontrol *kcontrol,
-				 struct snd_ctl_elem_value *ucontrol)
+static void alc_fixup_sku_ignore(struct hda_codec *codec,
+				 const struct hda_fixup *fix, int action)
 {
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct alc_spec *spec = codec->spec;
-
-	switch (ucontrol->value.enumerated.item[0]) {
-	case 0:
-		if (!spec->automute_speaker && !spec->automute_lo)
-			return 0;
-		spec->automute_speaker = 0;
-		spec->automute_lo = 0;
-		break;
-	case 1:
-		if (spec->automute_speaker_possible) {
-			if (!spec->automute_lo && spec->automute_speaker)
-				return 0;
-			spec->automute_speaker = 1;
-			spec->automute_lo = 0;
-		} else if (spec->automute_lo_possible) {
-			if (spec->automute_lo)
-				return 0;
-			spec->automute_lo = 1;
-		} else
-			return -EINVAL;
-		break;
-	case 2:
-		if (!spec->automute_lo_possible || !spec->automute_speaker_possible)
-			return -EINVAL;
-		if (spec->automute_speaker && spec->automute_lo)
-			return 0;
-		spec->automute_speaker = 1;
-		spec->automute_lo = 1;
-		break;
-	default:
-		return -EINVAL;
+	if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+		spec->cdefine.fixup = 1;
+		spec->cdefine.sku_cfg = ALC_FIXUP_SKU_IGNORE;
 	}
-	call_update_outputs(codec);
-	return 1;
-}
-
-static const struct snd_kcontrol_new alc_automute_mode_enum = {
-	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-	.name = "Auto-Mute Mode",
-	.info = alc_automute_mode_info,
-	.get = alc_automute_mode_get,
-	.put = alc_automute_mode_put,
-};
-
-static struct snd_kcontrol_new *
-alc_kcontrol_new(struct alc_spec *spec, const char *name,
-		 const struct snd_kcontrol_new *temp)
-{
-	struct snd_kcontrol_new *knew = snd_array_new(&spec->kctls);
-	if (!knew)
-		return NULL;
-	*knew = *temp;
-	if (name)
-		knew->name = kstrdup(name, GFP_KERNEL);
-	else if (knew->name)
-		knew->name = kstrdup(knew->name, GFP_KERNEL);
-	if (!knew->name)
-		return NULL;
-	return knew;
 }
 
-static int alc_add_automute_mode_enum(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-
-	if (!alc_kcontrol_new(spec, NULL, &alc_automute_mode_enum))
-		return -ENOMEM;
-	return 0;
-}
-
-/*
- * Check the availability of HP/line-out auto-mute;
- * Set up appropriately if really supported
- */
-static int alc_init_automute(struct hda_codec *codec)
+static int alc_auto_parse_customize_define(struct hda_codec *codec)
 {
+	unsigned int ass, tmp, i;
+	unsigned nid = 0;
 	struct alc_spec *spec = codec->spec;
-	struct auto_pin_cfg *cfg = &spec->autocfg;
-	int present = 0;
-	int i, err;
 
-	if (cfg->hp_pins[0])
-		present++;
-	if (cfg->line_out_pins[0])
-		present++;
-	if (cfg->speaker_pins[0])
-		present++;
-	if (present < 2) /* need two different output types */
-		return 0;
+	spec->cdefine.enable_pcbeep = 1; /* assume always enabled */
 
-	if (!cfg->speaker_pins[0] &&
-	    cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
-		memcpy(cfg->speaker_pins, cfg->line_out_pins,
-		       sizeof(cfg->speaker_pins));
-		cfg->speaker_outs = cfg->line_outs;
+	if (spec->cdefine.fixup) {
+		ass = spec->cdefine.sku_cfg;
+		if (ass == ALC_FIXUP_SKU_IGNORE)
+			return -1;
+		goto do_sku;
 	}
 
-	if (!cfg->hp_pins[0] &&
-	    cfg->line_out_type == AUTO_PIN_HP_OUT) {
-		memcpy(cfg->hp_pins, cfg->line_out_pins,
-		       sizeof(cfg->hp_pins));
-		cfg->hp_outs = cfg->line_outs;
-	}
+	ass = codec->subsystem_id & 0xffff;
+	if (ass != codec->bus->pci->subsystem_device && (ass & 1))
+		goto do_sku;
 
-	for (i = 0; i < cfg->hp_outs; i++) {
-		hda_nid_t nid = cfg->hp_pins[i];
-		if (!is_jack_detectable(codec, nid))
-			continue;
-		snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n",
-			    nid);
-		snd_hda_jack_detect_enable_callback(codec, nid, ALC_HP_EVENT,
-						    alc_hp_automute);
-		spec->detect_hp = 1;
-	}
+	nid = 0x1d;
+	if (codec->vendor_id == 0x10ec0260)
+		nid = 0x17;
+	ass = snd_hda_codec_get_pincfg(codec, nid);
 
-	if (cfg->line_out_type == AUTO_PIN_LINE_OUT && cfg->line_outs) {
-		if (cfg->speaker_outs)
-			for (i = 0; i < cfg->line_outs; i++) {
-				hda_nid_t nid = cfg->line_out_pins[i];
-				if (!is_jack_detectable(codec, nid))
-					continue;
-				snd_printdd("realtek: Enable Line-Out "
-					    "auto-muting on NID 0x%x\n", nid);
-				snd_hda_jack_detect_enable_callback(codec, nid, ALC_FRONT_EVENT,
-								    alc_line_automute);
-				spec->detect_lo = 1;
-			}
-		spec->automute_lo_possible = spec->detect_hp;
+	if (!(ass & 1)) {
+		printk(KERN_INFO "hda_codec: %s: SKU not ready 0x%08x\n",
+		       codec->chip_name, ass);
+		return -1;
 	}
 
-	spec->automute_speaker_possible = cfg->speaker_outs &&
-		(spec->detect_hp || spec->detect_lo);
-
-	spec->automute_lo = spec->automute_lo_possible;
-	spec->automute_speaker = spec->automute_speaker_possible;
-
-	if (spec->automute_speaker_possible || spec->automute_lo_possible) {
-		/* create a control for automute mode */
-		err = alc_add_automute_mode_enum(codec);
-		if (err < 0)
-			return err;
+	/* check sum */
+	tmp = 0;
+	for (i = 1; i < 16; i++) {
+		if ((ass >> i) & 1)
+			tmp++;
 	}
-	return 0;
-}
-
-/* return the position of NID in the list, or -1 if not found */
-static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
-{
-	int i;
-	for (i = 0; i < nums; i++)
-		if (list[i] == nid)
-			return i;
-	return -1;
-}
-
-/* check whether all auto-mic pins are valid; setup indices if OK */
-static bool alc_auto_mic_check_imux(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	const struct hda_input_mux *imux;
-	int i;
-
-	imux = &spec->input_mux;
-	for (i = 0; i < spec->am_num_entries; i++) {
-		spec->am_entry[i].idx =
-			find_idx_in_nid_list(spec->am_entry[i].pin,
-					     spec->imux_pins, imux->num_items);
-		if (spec->am_entry[i].idx < 0)
-			return false; /* no corresponding imux */
-	}
-
-	/* we don't need the jack detection for the first pin */
-	for (i = 1; i < spec->am_num_entries; i++)
-		snd_hda_jack_detect_enable_callback(codec,
-						    spec->am_entry[i].pin,
-						    ALC_MIC_EVENT,
-						    alc_mic_automute);
-	return true;
-}
-
-static int compare_attr(const void *ap, const void *bp)
-{
-	const struct alc_automic_entry *a = ap;
-	const struct alc_automic_entry *b = bp;
-	return (int)(a->attr - b->attr);
-}
-
-/*
- * Check the availability of auto-mic switch;
- * Set up if really supported
- */
-static int alc_init_auto_mic(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	struct auto_pin_cfg *cfg = &spec->autocfg;
-	unsigned int types;
-	int i, num_pins;
-
-	types = 0;
-	num_pins = 0;
-	for (i = 0; i < cfg->num_inputs; i++) {
-		hda_nid_t nid = cfg->inputs[i].pin;
-		unsigned int attr;
-		attr = snd_hda_codec_get_pincfg(codec, nid);
-		attr = snd_hda_get_input_pin_attr(attr);
-		if (types & (1 << attr))
-			return 0; /* already occupied */
-		switch (attr) {
-		case INPUT_PIN_ATTR_INT:
-			if (cfg->inputs[i].type != AUTO_PIN_MIC)
-				return 0; /* invalid type */
-			break;
-		case INPUT_PIN_ATTR_UNUSED:
-			return 0; /* invalid entry */
-		default:
-			if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
-				return 0; /* invalid type */
-			if (!spec->line_in_auto_switch &&
-			    cfg->inputs[i].type != AUTO_PIN_MIC)
-				return 0; /* only mic is allowed */
-			if (!is_jack_detectable(codec, nid))
-				return 0; /* no unsol support */
-			break;
-		}
-		if (num_pins >= MAX_AUTO_MIC_PINS)
-			return 0;
-		types |= (1 << attr);
-		spec->am_entry[num_pins].pin = nid;
-		spec->am_entry[num_pins].attr = attr;
-		num_pins++;
-	}
-
-	if (num_pins < 2)
-		return 0;
-
-	spec->am_num_entries = num_pins;
-	/* sort the am_entry in the order of attr so that the pin with a
-	 * higher attr will be selected when the jack is plugged.
-	 */
-	sort(spec->am_entry, num_pins, sizeof(spec->am_entry[0]),
-	     compare_attr, NULL);
-
-	if (!alc_auto_mic_check_imux(codec))
-		return 0;
-
-	spec->auto_mic = 1;
-	spec->num_adc_nids = 1;
-	spec->cur_mux[0] = spec->am_entry[0].idx;
-	snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n",
-		    spec->am_entry[0].pin,
-		    spec->am_entry[1].pin,
-		    spec->am_entry[2].pin);
-
-	return 0;
-}
-
-/*
- * Realtek SSID verification
- */
-
-/* Could be any non-zero and even value. When used as fixup, tells
- * the driver to ignore any present sku defines.
- */
-#define ALC_FIXUP_SKU_IGNORE (2)
-
-static void alc_fixup_sku_ignore(struct hda_codec *codec,
-				 const struct hda_fixup *fix, int action)
-{
-	struct alc_spec *spec = codec->spec;
-	if (action == HDA_FIXUP_ACT_PRE_PROBE) {
-		spec->cdefine.fixup = 1;
-		spec->cdefine.sku_cfg = ALC_FIXUP_SKU_IGNORE;
-	}
-}
-
-static int alc_auto_parse_customize_define(struct hda_codec *codec)
-{
-	unsigned int ass, tmp, i;
-	unsigned nid = 0;
-	struct alc_spec *spec = codec->spec;
-
-	spec->cdefine.enable_pcbeep = 1; /* assume always enabled */
-
-	if (spec->cdefine.fixup) {
-		ass = spec->cdefine.sku_cfg;
-		if (ass == ALC_FIXUP_SKU_IGNORE)
-			return -1;
-		goto do_sku;
-	}
-
-	ass = codec->subsystem_id & 0xffff;
-	if (ass != codec->bus->pci->subsystem_device && (ass & 1))
-		goto do_sku;
-
-	nid = 0x1d;
-	if (codec->vendor_id == 0x10ec0260)
-		nid = 0x17;
-	ass = snd_hda_codec_get_pincfg(codec, nid);
-
-	if (!(ass & 1)) {
-		printk(KERN_INFO "hda_codec: %s: SKU not ready 0x%08x\n",
-		       codec->chip_name, ass);
-		return -1;
-	}
-
-	/* check sum */
-	tmp = 0;
-	for (i = 1; i < 16; i++) {
-		if ((ass >> i) & 1)
-			tmp++;
-	}
-	if (((ass >> 16) & 0xf) != tmp)
-		return -1;
+	if (((ass >> 16) & 0xf) != tmp)
+		return -1;
 
 	spec->cdefine.port_connectivity = ass >> 30;
 	spec->cdefine.enable_pcbeep = (ass & 0x100000) >> 20;
@@ -1157,6 +418,15 @@ do_sku:
 	return 0;
 }
 
+/* return the position of NID in the list, or -1 if not found */
+static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
+{
+	int i;
+	for (i = 0; i < nums; i++)
+		if (list[i] == nid)
+			return i;
+	return -1;
+}
 /* return true if the given NID is found in the list */
 static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
 {
@@ -1259,9 +529,9 @@ do_sku:
 	 * 15   : 1 --> enable the function "Mute internal speaker
 	 *	        when the external headphone out jack is plugged"
 	 */
-	if (!spec->autocfg.hp_pins[0] &&
-	    !(spec->autocfg.line_out_pins[0] &&
-	      spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)) {
+	if (!spec->gen.autocfg.hp_pins[0] &&
+	    !(spec->gen.autocfg.line_out_pins[0] &&
+	      spec->gen.autocfg.line_out_type == AUTO_PIN_HP_OUT)) {
 		hda_nid_t nid;
 		tmp = (ass >> 11) & 0x3;	/* HP to chassis */
 		if (tmp == 0)
@@ -1274,10 +544,10 @@ do_sku:
 			nid = porti;
 		else
 			return 1;
-		if (found_in_nid_list(nid, spec->autocfg.line_out_pins,
-				      spec->autocfg.line_outs))
+		if (found_in_nid_list(nid, spec->gen.autocfg.line_out_pins,
+				      spec->gen.autocfg.line_outs))
 			return 1;
-		spec->autocfg.hp_pins[0] = nid;
+		spec->gen.autocfg.hp_pins[0] = nid;
 	}
 	return 1;
 }
@@ -1326,170 +596,35 @@ static unsigned int alc_get_coef0(struct hda_codec *codec)
 	return spec->coef0;
 }
 
-static void alc_auto_set_output_and_unmute(struct hda_codec *codec,
-					   hda_nid_t pin, int pin_type,
-					   hda_nid_t dac);
-static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin,
-				       bool is_digital);
-static bool parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
-			   hda_nid_t to_nid, int with_aa_mix,
-			   struct nid_path *path);
-static struct nid_path *add_new_nid_path(struct hda_codec *codec,
-					 hda_nid_t from_nid, hda_nid_t to_nid,
-					 int with_aa_mix);
-
-/*
- * Digital I/O handling
- */
-
-/* set right pin controls for digital I/O */
-static void alc_auto_init_digital(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	int i;
-	hda_nid_t pin;
-
-	for (i = 0; i < spec->autocfg.dig_outs; i++) {
-		pin = spec->autocfg.dig_out_pins[i];
-		if (!pin)
-			continue;
-		alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
-	}
-	pin = spec->autocfg.dig_in_pin;
-	if (pin)
-		snd_hda_set_pin_ctl(codec, pin, PIN_IN);
-}
-
-/* parse digital I/Os and set up NIDs in BIOS auto-parse mode */
-static void alc_auto_parse_digital(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	int i, nums;
-	hda_nid_t dig_nid;
-
-	/* support multiple SPDIFs; the secondary is set up as a slave */
-	nums = 0;
-	for (i = 0; i < spec->autocfg.dig_outs; i++) {
-		hda_nid_t pin = spec->autocfg.dig_out_pins[i];
-		dig_nid = alc_auto_look_for_dac(codec, pin, true);
-		if (!dig_nid)
-			continue;
-		if (!add_new_nid_path(codec, dig_nid, pin, 2))
-			continue;
-		if (!nums) {
-			spec->multiout.dig_out_nid = dig_nid;
-			spec->dig_out_type = spec->autocfg.dig_out_type[0];
-		} else {
-			spec->multiout.slave_dig_outs = spec->slave_dig_outs;
-			if (nums >= ARRAY_SIZE(spec->slave_dig_outs) - 1)
-				break;
-			spec->slave_dig_outs[nums - 1] = dig_nid;
-		}
-		nums++;
-	}
-
-	if (spec->autocfg.dig_in_pin) {
-		dig_nid = codec->start_nid;
-		for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
-			struct nid_path *path;
-			unsigned int wcaps = get_wcaps(codec, dig_nid);
-			if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
-				continue;
-			if (!(wcaps & AC_WCAP_DIGITAL))
-				continue;
-			path = add_new_nid_path(codec, spec->autocfg.dig_in_pin,
-						dig_nid, 2);
-			if (path) {
-				path->active = true;
-				spec->dig_in_nid = dig_nid;
-				break;
-			}
-		}
-	}
-}
-
 /*
- * capture mixer elements
  */
-#define alc_cap_vol_info	snd_hda_mixer_amp_volume_info
-#define alc_cap_vol_get		snd_hda_mixer_amp_volume_get
-#define alc_cap_vol_tlv		snd_hda_mixer_amp_tlv
-
-typedef int (*put_call_t)(struct snd_kcontrol *kcontrol,
-			  struct snd_ctl_elem_value *ucontrol);
-
-static int alc_cap_put_caller(struct snd_kcontrol *kcontrol,
-			      struct snd_ctl_elem_value *ucontrol,
-			      put_call_t func, int type)
-{
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct alc_spec *spec = codec->spec;
-	const struct hda_input_mux *imux;
-	struct nid_path *path;
-	int i, adc_idx, err = 0;
-
-	imux = &spec->input_mux;
-	adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-	mutex_lock(&codec->control_mutex);
-	codec->cached_write = 1;
-	for (i = 0; i < imux->num_items; i++) {
-		path = get_nid_path(codec, spec->imux_pins[i],
-				    get_adc_nid(codec, adc_idx, i));
-		if (!path->ctls[type])
-			continue;
-		kcontrol->private_value = path->ctls[type];
-		err = func(kcontrol, ucontrol);
-		if (err < 0)
-			goto error;
-	}
- error:
-	codec->cached_write = 0;
-	mutex_unlock(&codec->control_mutex);
-	snd_hda_codec_resume_amp(codec);
-	if (err >= 0 && type == NID_PATH_MUTE_CTL &&
-	    spec->inv_dmic_fixup && spec->inv_dmic_muted)
-		alc_inv_dmic_sync_adc(codec, adc_idx);
-	return err;
-}
-
-static int alc_cap_vol_put(struct snd_kcontrol *kcontrol,
-			   struct snd_ctl_elem_value *ucontrol)
-{
-	return alc_cap_put_caller(kcontrol, ucontrol,
-				  snd_hda_mixer_amp_volume_put,
-				  NID_PATH_VOL_CTL);
-}
-
-/* capture mixer elements */
-#define alc_cap_sw_info		snd_ctl_boolean_stereo_info
-#define alc_cap_sw_get		snd_hda_mixer_amp_switch_get
 
-static int alc_cap_sw_put(struct snd_kcontrol *kcontrol,
-			  struct snd_ctl_elem_value *ucontrol)
+static hda_nid_t get_adc_nid(struct hda_codec *codec, int adc_idx, int imux_idx)
 {
-	return alc_cap_put_caller(kcontrol, ucontrol,
-				  snd_hda_mixer_amp_switch_put,
-				  NID_PATH_MUTE_CTL);
+	struct hda_gen_spec *spec = codec->spec;
+	if (spec->dyn_adc_switch)
+		adc_idx = spec->dyn_adc_idx[imux_idx];
+	return spec->adc_nids[adc_idx];
 }
 
 static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx)
 {
 	struct alc_spec *spec = codec->spec;
-	struct hda_input_mux *imux = &spec->input_mux;
+	struct hda_input_mux *imux = &spec->gen.input_mux;
 	struct nid_path *path;
 	hda_nid_t nid;
 	int i, dir, parm;
 	unsigned int val;
 
 	for (i = 0; i < imux->num_items; i++) {
-		if (spec->imux_pins[i] == spec->inv_dmic_pin)
+		if (spec->gen.imux_pins[i] == spec->inv_dmic_pin)
 			break;
 	}
 	if (i >= imux->num_items)
 		return;
 
-	path = get_nid_path(codec, spec->inv_dmic_pin,
-			    get_adc_nid(codec, adc_idx, i));
+	path = snd_hda_get_nid_path(codec, spec->inv_dmic_pin,
+				    get_adc_nid(codec, adc_idx, i));
 	val = path->ctls[NID_PATH_MUTE_CTL];
 	if (!val)
 		return;
@@ -1531,12 +666,12 @@ static void alc_inv_dmic_sync(struct hda_codec *codec, bool force)
 		return;
 	if (!spec->inv_dmic_muted && !force)
 		return;
-	nums = spec->dyn_adc_switch ? 1 : spec->num_adc_nids;
+	nums = spec->gen.dyn_adc_switch ? 1 : spec->gen.num_adc_nids;
 	for (src = 0; src < nums; src++) {
 		bool dmic_fixup = false;
 
 		if (spec->inv_dmic_muted &&
-		    spec->imux_pins[spec->cur_mux[src]] == spec->inv_dmic_pin)
+		    spec->gen.imux_pins[spec->gen.cur_mux[src]] == spec->inv_dmic_pin)
 			dmic_fixup = true;
 		if (!dmic_fixup && !force)
 			continue;
@@ -1544,6 +679,11 @@ static void alc_inv_dmic_sync(struct hda_codec *codec, bool force)
 	}
 }
 
+static void alc_inv_dmic_hook(struct hda_codec *codec)
+{
+	alc_inv_dmic_sync(codec, false);
+}
+
 static int alc_inv_dmic_sw_get(struct snd_kcontrol *kcontrol,
 			       struct snd_ctl_elem_value *ucontrol)
 {
@@ -1580,11 +720,12 @@ static int alc_add_inv_dmic_mixer(struct hda_codec *codec, hda_nid_t nid)
 {
 	struct alc_spec *spec = codec->spec;
 
-	if (!alc_kcontrol_new(spec, NULL, &alc_inv_dmic_sw))
+	if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &alc_inv_dmic_sw))
 		return -ENOMEM;
 	spec->inv_dmic_fixup = 1;
 	spec->inv_dmic_muted = 0;
 	spec->inv_dmic_pin = nid;
+	spec->gen.cap_sync_hook = alc_inv_dmic_hook;
 	return 0;
 }
 
@@ -1594,2807 +735,208 @@ static void alc_fixup_inv_dmic_0x12(struct hda_codec *codec,
 {
 	if (action == ALC_FIXUP_ACT_PROBE)
 		alc_add_inv_dmic_mixer(codec, 0x12);
-}
-
-/*
- * virtual master controls
- */
-
-/*
- * slave controls for virtual master
- */
-static const char * const alc_slave_pfxs[] = {
-	"Front", "Surround", "Center", "LFE", "Side",
-	"Headphone", "Speaker", "Mono", "Line Out",
-	"CLFE", "Bass Speaker", "PCM",
-	NULL,
-};
-
-/*
- * build control elements
- */
-
-static void alc_free_kctls(struct hda_codec *codec);
-
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-/* additional beep mixers; the actual parameters are overwritten at build */
-static const struct snd_kcontrol_new alc_beep_mixer[] = {
-	HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT),
-	HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT),
-	{ } /* end */
-};
-#endif
-
-static int alc_build_controls(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	int i, err;
-
-	for (i = 0; i < spec->num_mixers; i++) {
-		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
-		if (err < 0)
-			return err;
-	}
-	if (spec->multiout.dig_out_nid) {
-		err = snd_hda_create_dig_out_ctls(codec,
-						  spec->multiout.dig_out_nid,
-						  spec->multiout.dig_out_nid,
-						  spec->pcm_rec[1].pcm_type);
-		if (err < 0)
-			return err;
-		if (!spec->no_analog) {
-			err = snd_hda_create_spdif_share_sw(codec,
-							    &spec->multiout);
-			if (err < 0)
-				return err;
-			spec->multiout.share_spdif = 1;
-		}
-	}
-	if (spec->dig_in_nid) {
-		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
-		if (err < 0)
-			return err;
-	}
-
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-	/* create beep controls if needed */
-	if (spec->beep_amp) {
-		const struct snd_kcontrol_new *knew;
-		for (knew = alc_beep_mixer; knew->name; knew++) {
-			struct snd_kcontrol *kctl;
-			kctl = snd_ctl_new1(knew, codec);
-			if (!kctl)
-				return -ENOMEM;
-			kctl->private_value = spec->beep_amp;
-			err = snd_hda_ctl_add(codec, 0, kctl);
-			if (err < 0)
-				return err;
-		}
-	}
-#endif
-
-	/* if we have no master control, let's create it */
-	if (!spec->no_analog &&
-	    !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
-		unsigned int vmaster_tlv[4];
-		snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
-					HDA_OUTPUT, vmaster_tlv);
-		err = snd_hda_add_vmaster(codec, "Master Playback Volume",
-					  vmaster_tlv, alc_slave_pfxs,
-					  "Playback Volume");
-		if (err < 0)
-			return err;
-	}
-	if (!spec->no_analog &&
-	    !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
-		err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
-					    NULL, alc_slave_pfxs,
-					    "Playback Switch",
-					    true, &spec->vmaster_mute.sw_kctl);
-		if (err < 0)
-			return err;
-		if (spec->vmaster_mute.hook)
-			snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true);
-	}
-
-	alc_free_kctls(codec); /* no longer needed */
-
-	if (spec->shared_mic_hp) {
-		int err;
-		int nid = spec->autocfg.inputs[1].pin;
-		err = snd_hda_jack_add_kctl(codec, nid, "Headphone Mic", 0);
-		if (err < 0)
-			return err;
-		err = snd_hda_jack_detect_enable(codec, nid, 0);
-		if (err < 0)
-			return err;
-	}
-
-	err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
-	if (err < 0)
-		return err;
-
-	alc_apply_fixup(codec, ALC_FIXUP_ACT_BUILD);
-	return 0;
-}
-
-
-/*
- * Common callbacks
- */
-
-static void alc_auto_init_std(struct hda_codec *codec);
-
-static int alc_init(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-
-	if (spec->init_hook)
-		spec->init_hook(codec);
-
-	alc_fix_pll(codec);
-	alc_auto_init_amp(codec, spec->init_amp);
-
-	snd_hda_apply_verbs(codec);
-	alc_auto_init_std(codec);
-
-	if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook)
-		snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
-
-	alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT);
-
-	hda_call_check_power_status(codec, 0x01);
-	return 0;
-}
-
-#ifdef CONFIG_PM
-static int alc_check_power_status(struct hda_codec *codec, hda_nid_t nid)
-{
-	struct alc_spec *spec = codec->spec;
-	return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
-}
-#endif
-
-/*
- * Analog playback callbacks
- */
-static int alc_playback_pcm_open(struct hda_pcm_stream *hinfo,
-				    struct hda_codec *codec,
-				    struct snd_pcm_substream *substream)
-{
-	struct alc_spec *spec = codec->spec;
-	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
-					     hinfo);
-}
-
-static int alc_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
-				       struct hda_codec *codec,
-				       unsigned int stream_tag,
-				       unsigned int format,
-				       struct snd_pcm_substream *substream)
-{
-	struct alc_spec *spec = codec->spec;
-	return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
-						stream_tag, format, substream);
-}
-
-static int alc_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
-				       struct hda_codec *codec,
-				       struct snd_pcm_substream *substream)
-{
-	struct alc_spec *spec = codec->spec;
-	return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
-}
-
-/*
- * Digital out
- */
-static int alc_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
-					struct hda_codec *codec,
-					struct snd_pcm_substream *substream)
-{
-	struct alc_spec *spec = codec->spec;
-	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int alc_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
-					   struct hda_codec *codec,
-					   unsigned int stream_tag,
-					   unsigned int format,
-					   struct snd_pcm_substream *substream)
-{
-	struct alc_spec *spec = codec->spec;
-	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
-					     stream_tag, format, substream);
-}
-
-static int alc_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
-					   struct hda_codec *codec,
-					   struct snd_pcm_substream *substream)
-{
-	struct alc_spec *spec = codec->spec;
-	return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
-}
-
-static int alc_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
-					 struct hda_codec *codec,
-					 struct snd_pcm_substream *substream)
-{
-	struct alc_spec *spec = codec->spec;
-	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-/*
- * Analog capture
- */
-static int alc_alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
-				      struct hda_codec *codec,
-				      unsigned int stream_tag,
-				      unsigned int format,
-				      struct snd_pcm_substream *substream)
-{
-	struct alc_spec *spec = codec->spec;
-
-	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1],
-				   stream_tag, 0, format);
-	return 0;
-}
-
-static int alc_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
-				      struct hda_codec *codec,
-				      struct snd_pcm_substream *substream)
-{
-	struct alc_spec *spec = codec->spec;
-
-	snd_hda_codec_cleanup_stream(codec,
-				     spec->adc_nids[substream->number + 1]);
-	return 0;
-}
-
-/* analog capture with dynamic dual-adc changes */
-static int dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
-				       struct hda_codec *codec,
-				       unsigned int stream_tag,
-				       unsigned int format,
-				       struct snd_pcm_substream *substream)
-{
-	struct alc_spec *spec = codec->spec;
-	spec->cur_adc = spec->adc_nids[spec->dyn_adc_idx[spec->cur_mux[0]]];
-	spec->cur_adc_stream_tag = stream_tag;
-	spec->cur_adc_format = format;
-	snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
-	return 0;
-}
-
-static int dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
-				       struct hda_codec *codec,
-				       struct snd_pcm_substream *substream)
-{
-	struct alc_spec *spec = codec->spec;
-	snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
-	spec->cur_adc = 0;
-	return 0;
-}
-
-static const struct hda_pcm_stream dyn_adc_pcm_analog_capture = {
-	.substreams = 1,
-	.channels_min = 2,
-	.channels_max = 2,
-	.nid = 0, /* fill later */
-	.ops = {
-		.prepare = dyn_adc_capture_pcm_prepare,
-		.cleanup = dyn_adc_capture_pcm_cleanup
-	},
-};
-
-/*
- */
-static const struct hda_pcm_stream alc_pcm_analog_playback = {
-	.substreams = 1,
-	.channels_min = 2,
-	.channels_max = 8,
-	/* NID is set in alc_build_pcms */
-	.ops = {
-		.open = alc_playback_pcm_open,
-		.prepare = alc_playback_pcm_prepare,
-		.cleanup = alc_playback_pcm_cleanup
-	},
-};
-
-static const struct hda_pcm_stream alc_pcm_analog_capture = {
-	.substreams = 1,
-	.channels_min = 2,
-	.channels_max = 2,
-	/* NID is set in alc_build_pcms */
-};
-
-static const struct hda_pcm_stream alc_pcm_analog_alt_playback = {
-	.substreams = 1,
-	.channels_min = 2,
-	.channels_max = 2,
-	/* NID is set in alc_build_pcms */
-};
-
-static const struct hda_pcm_stream alc_pcm_analog_alt_capture = {
-	.substreams = 2, /* can be overridden */
-	.channels_min = 2,
-	.channels_max = 2,
-	/* NID is set in alc_build_pcms */
-	.ops = {
-		.prepare = alc_alt_capture_pcm_prepare,
-		.cleanup = alc_alt_capture_pcm_cleanup
-	},
-};
-
-static const struct hda_pcm_stream alc_pcm_digital_playback = {
-	.substreams = 1,
-	.channels_min = 2,
-	.channels_max = 2,
-	/* NID is set in alc_build_pcms */
-	.ops = {
-		.open = alc_dig_playback_pcm_open,
-		.close = alc_dig_playback_pcm_close,
-		.prepare = alc_dig_playback_pcm_prepare,
-		.cleanup = alc_dig_playback_pcm_cleanup
-	},
-};
-
-static const struct hda_pcm_stream alc_pcm_digital_capture = {
-	.substreams = 1,
-	.channels_min = 2,
-	.channels_max = 2,
-	/* NID is set in alc_build_pcms */
-};
-
-/* Used by alc_build_pcms to flag that a PCM has no playback stream */
-static const struct hda_pcm_stream alc_pcm_null_stream = {
-	.substreams = 0,
-	.channels_min = 0,
-	.channels_max = 0,
-};
-
-static int alc_build_pcms(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	struct hda_pcm *info = spec->pcm_rec;
-	const struct hda_pcm_stream *p;
-	bool have_multi_adcs;
-	int i;
-
-	codec->num_pcms = 1;
-	codec->pcm_info = info;
-
-	if (spec->no_analog)
-		goto skip_analog;
-
-	snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
-		 "%s Analog", codec->chip_name);
-	info->name = spec->stream_name_analog;
-
-	if (spec->multiout.num_dacs > 0) {
-		p = spec->stream_analog_playback;
-		if (!p)
-			p = &alc_pcm_analog_playback;
-		info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p;
-		info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
-		info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
-			spec->multiout.max_channels;
-		if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT &&
-		    spec->autocfg.line_outs == 2)
-			info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap =
-				snd_pcm_2_1_chmaps;
-	}
-	if (spec->num_adc_nids) {
-		p = spec->stream_analog_capture;
-		if (!p) {
-			if (spec->dyn_adc_switch)
-				p = &dyn_adc_pcm_analog_capture;
-			else
-				p = &alc_pcm_analog_capture;
-		}
-		info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p;
-		info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
-	}
-
-	if (spec->channel_mode) {
-		info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 0;
-		for (i = 0; i < spec->num_channel_mode; i++) {
-			if (spec->channel_mode[i].channels > info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max) {
-				info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->channel_mode[i].channels;
-			}
-		}
-	}
-
- skip_analog:
-	/* SPDIF for stream index #1 */
-	if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
-		snprintf(spec->stream_name_digital,
-			 sizeof(spec->stream_name_digital),
-			 "%s Digital", codec->chip_name);
-		codec->num_pcms = 2;
-	        codec->slave_dig_outs = spec->multiout.slave_dig_outs;
-		info = spec->pcm_rec + 1;
-		info->name = spec->stream_name_digital;
-		if (spec->dig_out_type)
-			info->pcm_type = spec->dig_out_type;
-		else
-			info->pcm_type = HDA_PCM_TYPE_SPDIF;
-		if (spec->multiout.dig_out_nid) {
-			p = spec->stream_digital_playback;
-			if (!p)
-				p = &alc_pcm_digital_playback;
-			info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p;
-			info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
-		}
-		if (spec->dig_in_nid) {
-			p = spec->stream_digital_capture;
-			if (!p)
-				p = &alc_pcm_digital_capture;
-			info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p;
-			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
-		}
-		/* FIXME: do we need this for all Realtek codec models? */
-		codec->spdif_status_reset = 1;
-	}
-
-	if (spec->no_analog)
-		return 0;
-
-	/* If the use of more than one ADC is requested for the current
-	 * model, configure a second analog capture-only PCM.
-	 */
-	have_multi_adcs = (spec->num_adc_nids > 1) &&
-		!spec->dyn_adc_switch && !spec->auto_mic;
-	/* Additional Analaog capture for index #2 */
-	if (spec->alt_dac_nid || have_multi_adcs) {
-		codec->num_pcms = 3;
-		info = spec->pcm_rec + 2;
-		info->name = spec->stream_name_analog;
-		if (spec->alt_dac_nid) {
-			p = spec->stream_analog_alt_playback;
-			if (!p)
-				p = &alc_pcm_analog_alt_playback;
-			info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p;
-			info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
-				spec->alt_dac_nid;
-		} else {
-			info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
-				alc_pcm_null_stream;
-			info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0;
-		}
-		if (have_multi_adcs) {
-			p = spec->stream_analog_alt_capture;
-			if (!p)
-				p = &alc_pcm_analog_alt_capture;
-			info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p;
-			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
-				spec->adc_nids[1];
-			info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
-				spec->num_adc_nids - 1;
-		} else {
-			info->stream[SNDRV_PCM_STREAM_CAPTURE] =
-				alc_pcm_null_stream;
-			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 0;
-		}
-	}
-
-	return 0;
-}
-
-static inline void alc_shutup(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-
-	if (spec && spec->shutup)
-		spec->shutup(codec);
-	snd_hda_shutup_pins(codec);
-}
-
-static void alc_free_kctls(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-
-	if (spec->kctls.list) {
-		struct snd_kcontrol_new *kctl = spec->kctls.list;
-		int i;
-		for (i = 0; i < spec->kctls.used; i++)
-			kfree(kctl[i].name);
-	}
-	snd_array_free(&spec->kctls);
-}
-
-static void alc_free_bind_ctls(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	if (spec->bind_ctls.list) {
-		struct hda_bind_ctls **ctl = spec->bind_ctls.list;
-		int i;
-		for (i = 0; i < spec->bind_ctls.used; i++)
-			kfree(ctl[i]);
-	}
-	snd_array_free(&spec->bind_ctls);
-}
-
-static void alc_free(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-
-	if (!spec)
-		return;
-
-	alc_free_kctls(codec);
-	alc_free_bind_ctls(codec);
-	snd_array_free(&spec->paths);
-	kfree(spec);
-	snd_hda_detach_beep_device(codec);
-}
-
-#ifdef CONFIG_PM
-static void alc_power_eapd(struct hda_codec *codec)
-{
-	alc_auto_setup_eapd(codec, false);
-}
-
-static int alc_suspend(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	alc_shutup(codec);
-	if (spec && spec->power_hook)
-		spec->power_hook(codec);
-	return 0;
-}
-#endif
-
-#ifdef CONFIG_PM
-static int alc_resume(struct hda_codec *codec)
-{
-	msleep(150); /* to avoid pop noise */
-	codec->patch_ops.init(codec);
-	snd_hda_codec_resume_amp(codec);
-	snd_hda_codec_resume_cache(codec);
-	alc_inv_dmic_sync(codec, true);
-	hda_call_check_power_status(codec, 0x01);
-	return 0;
-}
-#endif
-
-/*
- */
-static const struct hda_codec_ops alc_patch_ops = {
-	.build_controls = alc_build_controls,
-	.build_pcms = alc_build_pcms,
-	.init = alc_init,
-	.free = alc_free,
-	.unsol_event = snd_hda_jack_unsol_event,
-#ifdef CONFIG_PM
-	.resume = alc_resume,
-#endif
-#ifdef CONFIG_PM
-	.suspend = alc_suspend,
-	.check_power_status = alc_check_power_status,
-#endif
-	.reboot_notify = alc_shutup,
-};
-
-
-/* replace the codec chip_name with the given string */
-static int alc_codec_rename(struct hda_codec *codec, const char *name)
-{
-	kfree(codec->chip_name);
-	codec->chip_name = kstrdup(name, GFP_KERNEL);
-	if (!codec->chip_name) {
-		alc_free(codec);
-		return -ENOMEM;
-	}
-	return 0;
-}
-
-/*
- * Rename codecs appropriately from COEF value
- */
-struct alc_codec_rename_table {
-	unsigned int vendor_id;
-	unsigned short coef_mask;
-	unsigned short coef_bits;
-	const char *name;
-};
-
-static struct alc_codec_rename_table rename_tbl[] = {
-	{ 0x10ec0269, 0xfff0, 0x3010, "ALC277" },
-	{ 0x10ec0269, 0xf0f0, 0x2010, "ALC259" },
-	{ 0x10ec0269, 0xf0f0, 0x3010, "ALC258" },
-	{ 0x10ec0269, 0x00f0, 0x0010, "ALC269VB" },
-	{ 0x10ec0269, 0xffff, 0xa023, "ALC259" },
-	{ 0x10ec0269, 0xffff, 0x6023, "ALC281X" },
-	{ 0x10ec0269, 0x00f0, 0x0020, "ALC269VC" },
-	{ 0x10ec0269, 0x00f0, 0x0030, "ALC269VD" },
-	{ 0x10ec0887, 0x00f0, 0x0030, "ALC887-VD" },
-	{ 0x10ec0888, 0x00f0, 0x0030, "ALC888-VD" },
-	{ 0x10ec0888, 0xf0f0, 0x3020, "ALC886" },
-	{ 0x10ec0899, 0x2000, 0x2000, "ALC899" },
-	{ 0x10ec0892, 0xffff, 0x8020, "ALC661" },
-	{ 0x10ec0892, 0xffff, 0x8011, "ALC661" },
-	{ 0x10ec0892, 0xffff, 0x4011, "ALC656" },
-	{ } /* terminator */
-};
-
-static int alc_codec_rename_from_preset(struct hda_codec *codec)
-{
-	const struct alc_codec_rename_table *p;
-
-	for (p = rename_tbl; p->vendor_id; p++) {
-		if (p->vendor_id != codec->vendor_id)
-			continue;
-		if ((alc_get_coef0(codec) & p->coef_mask) == p->coef_bits)
-			return alc_codec_rename(codec, p->name);
-	}
-	return 0;
-}
-
-/*
- * Automatic parse of I/O pins from the BIOS configuration
- */
-
-enum {
-	ALC_CTL_WIDGET_VOL,
-	ALC_CTL_WIDGET_MUTE,
-	ALC_CTL_BIND_MUTE,
-	ALC_CTL_BIND_VOL,
-	ALC_CTL_BIND_SW,
-};
-static const struct snd_kcontrol_new alc_control_templates[] = {
-	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
-	HDA_CODEC_MUTE(NULL, 0, 0, 0),
-	HDA_BIND_MUTE(NULL, 0, 0, 0),
-	HDA_BIND_VOL(NULL, 0),
-	HDA_BIND_SW(NULL, 0),
-};
-
-/* add dynamic controls */
-static int add_control(struct alc_spec *spec, int type, const char *name,
-		       int cidx, unsigned long val)
-{
-	struct snd_kcontrol_new *knew;
-
-	knew = alc_kcontrol_new(spec, name, &alc_control_templates[type]);
-	if (!knew)
-		return -ENOMEM;
-	knew->index = cidx;
-	if (get_amp_nid_(val))
-		knew->subdevice = HDA_SUBDEV_AMP_FLAG;
-	knew->private_value = val;
-	return 0;
-}
-
-static int add_control_with_pfx(struct alc_spec *spec, int type,
-				const char *pfx, const char *dir,
-				const char *sfx, int cidx, unsigned long val)
-{
-	char name[32];
-	snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx);
-	return add_control(spec, type, name, cidx, val);
-}
-
-#define add_pb_vol_ctrl(spec, type, pfx, val)			\
-	add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val)
-#define add_pb_sw_ctrl(spec, type, pfx, val)			\
-	add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val)
-#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val)			\
-	add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val)
-#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val)			\
-	add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val)
-
-static const char * const channel_name[4] = {
-	"Front", "Surround", "CLFE", "Side"
-};
-
-static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch,
-					bool can_be_master, int *index)
-{
-	struct auto_pin_cfg *cfg = &spec->autocfg;
-
-	*index = 0;
-	if (cfg->line_outs == 1 && !spec->multi_ios &&
-	    !cfg->hp_outs && !cfg->speaker_outs && can_be_master)
-		return spec->vmaster_mute.hook ? "PCM" : "Master";
-
-	/* if there is really a single DAC used in the whole output paths,
-	 * use it master (or "PCM" if a vmaster hook is present)
-	 */
-	if (spec->multiout.num_dacs == 1 && !spec->mixer_nid &&
-	    !spec->multiout.hp_out_nid[0] && !spec->multiout.extra_out_nid[0])
-		return spec->vmaster_mute.hook ? "PCM" : "Master";
-
-	switch (cfg->line_out_type) {
-	case AUTO_PIN_SPEAKER_OUT:
-		if (cfg->line_outs == 1)
-			return "Speaker";
-		if (cfg->line_outs == 2)
-			return ch ? "Bass Speaker" : "Speaker";
-		break;
-	case AUTO_PIN_HP_OUT:
-		/* for multi-io case, only the primary out */
-		if (ch && spec->multi_ios)
-			break;
-		*index = ch;
-		return "Headphone";
-	default:
-		if (cfg->line_outs == 1 && !spec->multi_ios)
-			return "PCM";
-		break;
-	}
-	if (ch >= ARRAY_SIZE(channel_name)) {
-		snd_BUG();
-		return "PCM";
-	}
-
-	return channel_name[ch];
-}
-
-#ifdef CONFIG_PM
-/* add the powersave loopback-list entry */
-static void add_loopback_list(struct alc_spec *spec, hda_nid_t mix, int idx)
-{
-	struct hda_amp_list *list;
-
-	if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
-		return;
-	list = spec->loopback_list + spec->num_loopbacks;
-	list->nid = mix;
-	list->dir = HDA_INPUT;
-	list->idx = idx;
-	spec->num_loopbacks++;
-	spec->loopback.amplist = spec->loopback_list;
-}
-#else
-#define add_loopback_list(spec, mix, idx) /* NOP */
-#endif
-
-/* create input playback/capture controls for the given pin */
-static int new_analog_input(struct hda_codec *codec, hda_nid_t pin,
-			    const char *ctlname, int ctlidx,
-			    hda_nid_t mix_nid)
-{
-	struct alc_spec *spec = codec->spec;
-	struct nid_path *path;
-	unsigned int val;
-	int err, idx;
-
-	if (!nid_has_volume(codec, mix_nid, HDA_INPUT) &&
-	    !nid_has_mute(codec, mix_nid, HDA_INPUT))
-		return 0; /* no need for analog loopback */
-
-	path = add_new_nid_path(codec, pin, mix_nid, 2);
-	if (!path)
-		return -EINVAL;
-
-	idx = path->idx[path->depth - 1];
-	if (nid_has_volume(codec, mix_nid, HDA_INPUT)) {
-		val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT);
-		err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx, val);
-		if (err < 0)
-			return err;
-		path->ctls[NID_PATH_VOL_CTL] = val;
-	}
-
-	if (nid_has_mute(codec, mix_nid, HDA_INPUT)) {
-		val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT);
-		err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx, val);
-		if (err < 0)
-			return err;
-		path->ctls[NID_PATH_MUTE_CTL] = val;
-	}
-
-	path->active = true;
-	add_loopback_list(spec, mix_nid, idx);
-	return 0;
-}
-
-static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid)
-{
-	unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
-	return (pincap & AC_PINCAP_IN) != 0;
-}
-
-/* check whether the given two widgets can be connected */
-static bool is_reachable_path(struct hda_codec *codec,
-			      hda_nid_t from_nid, hda_nid_t to_nid)
-{
-	if (!from_nid || !to_nid)
-		return false;
-	return snd_hda_get_conn_index(codec, to_nid, from_nid, true) >= 0;
-}
-
-/* Parse the codec tree and retrieve ADCs */
-static int alc_auto_fill_adc_nids(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	hda_nid_t nid;
-	hda_nid_t *adc_nids = spec->adc_nids;
-	int max_nums = ARRAY_SIZE(spec->adc_nids);
-	int i, nums = 0;
-
-	nid = codec->start_nid;
-	for (i = 0; i < codec->num_nodes; i++, nid++) {
-		unsigned int caps = get_wcaps(codec, nid);
-		int type = get_wcaps_type(caps);
-
-		if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL))
-			continue;
-		adc_nids[nums] = nid;
-		if (++nums >= max_nums)
-			break;
-	}
-	spec->num_adc_nids = nums;
-	return nums;
-}
-
-/* filter out invalid adc_nids that don't give all active input pins;
- * if needed, check whether dynamic ADC-switching is available
- */
-static int check_dyn_adc_switch(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	struct hda_input_mux *imux = &spec->input_mux;
-	hda_nid_t adc_nids[ARRAY_SIZE(spec->adc_nids)];
-	int i, n, nums;
-	hda_nid_t pin, adc;
-
- again:
-	nums = 0;
-	for (n = 0; n < spec->num_adc_nids; n++) {
-		adc = spec->adc_nids[n];
-		for (i = 0; i < imux->num_items; i++) {
-			pin = spec->imux_pins[i];
-			if (!is_reachable_path(codec, pin, adc))
-				break;
-		}
-		if (i >= imux->num_items)
-			adc_nids[nums++] = adc;
-	}
-
-	if (!nums) {
-		if (spec->shared_mic_hp) {
-			spec->shared_mic_hp = 0;
-			imux->num_items = 1;
-			goto again;
-		}
-
-		/* check whether ADC-switch is possible */
-		for (i = 0; i < imux->num_items; i++) {
-			pin = spec->imux_pins[i];
-			for (n = 0; n < spec->num_adc_nids; n++) {
-				adc = spec->adc_nids[n];
-				if (is_reachable_path(codec, pin, adc)) {
-					spec->dyn_adc_idx[i] = n;
-					break;
-				}
-			}
-		}
-
-		snd_printdd("realtek: enabling ADC switching\n");
-		spec->dyn_adc_switch = 1;
-	} else if (nums != spec->num_adc_nids) {
-		memcpy(spec->adc_nids, adc_nids, nums * sizeof(hda_nid_t));
-		spec->num_adc_nids = nums;
-	}
-
-	if (imux->num_items == 1 || spec->shared_mic_hp) {
-		snd_printdd("realtek: reducing to a single ADC\n");
-		spec->num_adc_nids = 1; /* reduce to a single ADC */
-	}
-
-	/* single index for individual volumes ctls */
-	if (!spec->dyn_adc_switch && spec->multi_cap_vol)
-		spec->num_adc_nids = 1;
-
-	return 0;
-}
-
-/* templates for capture controls */
-static const struct snd_kcontrol_new cap_src_temp = {
-	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-	.name = "Input Source",
-	.info = alc_mux_enum_info,
-	.get = alc_mux_enum_get,
-	.put = alc_mux_enum_put,
-};
-
-static const struct snd_kcontrol_new cap_vol_temp = {
-	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-	.name = "Capture Volume",
-	.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
-		   SNDRV_CTL_ELEM_ACCESS_TLV_READ |
-		   SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK),
-	.info = alc_cap_vol_info,
-	.get = alc_cap_vol_get,
-	.put = alc_cap_vol_put,
-	.tlv = { .c = alc_cap_vol_tlv },
-};
-
-static const struct snd_kcontrol_new cap_sw_temp = {
-	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-	.name = "Capture Switch",
-	.info = alc_cap_sw_info,
-	.get = alc_cap_sw_get,
-	.put = alc_cap_sw_put,
-};
-
-static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path)
-{
-	hda_nid_t nid;
-	int i, depth;
-
-	path->ctls[NID_PATH_VOL_CTL] = path->ctls[NID_PATH_MUTE_CTL] = 0;
-	for (depth = 0; depth < 3; depth++) {
-		if (depth >= path->depth)
-			return -EINVAL;
-		i = path->depth - depth - 1;
-		nid = path->path[i];
-		if (!path->ctls[NID_PATH_VOL_CTL]) {
-			if (nid_has_volume(codec, nid, HDA_OUTPUT))
-				path->ctls[NID_PATH_VOL_CTL] =
-					HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
-			else if (nid_has_volume(codec, nid, HDA_INPUT)) {
-				int idx = path->idx[i];
-				if (!depth && codec->single_adc_amp)
-					idx = 0;
-				path->ctls[NID_PATH_VOL_CTL] =
-					HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT);
-			}
-		}
-		if (!path->ctls[NID_PATH_MUTE_CTL]) {
-			if (nid_has_mute(codec, nid, HDA_OUTPUT))
-				path->ctls[NID_PATH_MUTE_CTL] =
-					HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
-			else if (nid_has_mute(codec, nid, HDA_INPUT)) {
-				int idx = path->idx[i];
-				if (!depth && codec->single_adc_amp)
-					idx = 0;
-				path->ctls[NID_PATH_MUTE_CTL] =
-					HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT);
-			}
-		}
-	}
-	return 0;
-}
-
-static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs);
-
-static int add_single_cap_ctl(struct hda_codec *codec, const char *label,
-			      int idx, bool is_switch, unsigned int ctl,
-			      bool inv_dmic)
-{
-	struct alc_spec *spec = codec->spec;
-	char tmpname[44];
-	int type = is_switch ? ALC_CTL_WIDGET_MUTE : ALC_CTL_WIDGET_VOL;
-	const char *sfx = is_switch ? "Switch" : "Volume";
-	unsigned int chs = inv_dmic ? 1 : 3;
-	int err;
-
-	if (!ctl)
-		return 0;
-
-	if (label)
-		snprintf(tmpname, sizeof(tmpname),
-			 "%s Capture %s", label, sfx);
-	else
-		snprintf(tmpname, sizeof(tmpname),
-			 "Capture %s", sfx);
-	err = add_control(spec, type, tmpname, idx,
-			  amp_val_replace_channels(ctl, chs));
-	if (err < 0 || chs == 3)
-		return err;
-
-	/* Make independent right kcontrol */
-	if (label)
-		snprintf(tmpname, sizeof(tmpname),
-			 "Inverted %s Capture %s", label, sfx);
-	else
-		snprintf(tmpname, sizeof(tmpname),
-			 "Inverted Capture %s", sfx);
-	return add_control(spec, type, tmpname, idx,
-			   amp_val_replace_channels(ctl, 2));
-}
-
-static bool is_inv_dmic_pin(struct hda_codec *codec, hda_nid_t nid)
-{
-	struct alc_spec *spec = codec->spec;
-	struct auto_pin_cfg *cfg = &spec->autocfg;
-	unsigned int val;
-	int i;
-
-	if (!spec->inv_dmic_split)
-		return false;
-	for (i = 0; i < cfg->num_inputs; i++) {
-		if (cfg->inputs[i].pin != nid)
-			continue;
-		if (cfg->inputs[i].type != AUTO_PIN_MIC)
-			return false;
-		val = snd_hda_codec_get_pincfg(codec, nid);
-		return snd_hda_get_input_pin_attr(val) == INPUT_PIN_ATTR_INT;
-	}
-	return false;
-}
-
-/* create single (and simple) capture volume and switch controls */
-static int create_single_cap_vol_ctl(struct hda_codec *codec, int idx,
-				     unsigned int vol_ctl, unsigned int sw_ctl,
-				     bool inv_dmic)
-{
-	int err;
-	err = add_single_cap_ctl(codec, NULL, idx, false, vol_ctl, inv_dmic);
-	if (err < 0)
-		return err;
-	err = add_single_cap_ctl(codec, NULL, idx, true, sw_ctl, inv_dmic);
-	if (err < 0)
-		return err;
-	return 0;
-}
-
-/* create bound capture volume and switch controls */
-static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx,
-				   unsigned int vol_ctl, unsigned int sw_ctl)
-{
-	struct alc_spec *spec = codec->spec;
-	struct snd_kcontrol_new *knew;
-
-	if (vol_ctl) {
-		knew = alc_kcontrol_new(spec, NULL, &cap_vol_temp);
-		if (!knew)
-			return -ENOMEM;
-		knew->index = idx;
-		knew->private_value = vol_ctl;
-		knew->subdevice = HDA_SUBDEV_AMP_FLAG;
-	}
-	if (sw_ctl) {
-		knew = alc_kcontrol_new(spec, NULL, &cap_sw_temp);
-		if (!knew)
-			return -ENOMEM;
-		knew->index = idx;
-		knew->private_value = sw_ctl;
-		knew->subdevice = HDA_SUBDEV_AMP_FLAG;
-	}
-	return 0;
-}
-
-/* return the vol ctl when used first in the imux list */
-static unsigned int get_first_cap_ctl(struct hda_codec *codec, int idx, int type)
-{
-	struct alc_spec *spec = codec->spec;
-	struct nid_path *path;
-	unsigned int ctl;
-	int i;
-
-	path = get_nid_path(codec, spec->imux_pins[idx],
-			    get_adc_nid(codec, 0, idx));
-	if (!path)
-		return 0;
-	ctl = path->ctls[type];
-	if (!ctl)
-		return 0;
-	for (i = 0; i < idx - 1; i++) {
-		path = get_nid_path(codec, spec->imux_pins[i],
-				    get_adc_nid(codec, 0, i));
-		if (path && path->ctls[type] == ctl)
-			return 0;
-	}
-	return ctl;
-}
-
-/* create individual capture volume and switch controls per input */
-static int create_multi_cap_vol_ctl(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	struct hda_input_mux *imux = &spec->input_mux;
-	int i, err, type, type_idx = 0;
-	const char *prev_label = NULL;
-
-	for (i = 0; i < imux->num_items; i++) {
-		const char *label;
-		bool inv_dmic;
-		label = hda_get_autocfg_input_label(codec, &spec->autocfg, i);
-		if (prev_label && !strcmp(label, prev_label))
-			type_idx++;
-		else
-			type_idx = 0;
-		prev_label = label;
-		inv_dmic = is_inv_dmic_pin(codec, spec->imux_pins[i]);
-
-		for (type = 0; type < 2; type++) {
-			err = add_single_cap_ctl(codec, label, type_idx, type,
-						 get_first_cap_ctl(codec, i, type),
-						 inv_dmic);
-			if (err < 0)
-				return err;
-		}
-	}
-	return 0;
-}
-
-static int create_capture_mixers(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	struct hda_input_mux *imux = &spec->input_mux;
-	int i, n, nums, err;
-
-	if (spec->dyn_adc_switch)
-		nums = 1;
-	else
-		nums = spec->num_adc_nids;
-
-	if (!spec->auto_mic && imux->num_items > 1) {
-		struct snd_kcontrol_new *knew;
-		knew = alc_kcontrol_new(spec, NULL, &cap_src_temp);
-		if (!knew)
-			return -ENOMEM;
-		knew->count = nums;
-	}
-
-	for (n = 0; n < nums; n++) {
-		bool multi = false;
-		bool inv_dmic = false;
-		int vol, sw;
-
-		vol = sw = 0;
-		for (i = 0; i < imux->num_items; i++) {
-			struct nid_path *path;
-			path = get_nid_path(codec, spec->imux_pins[i],
-					    get_adc_nid(codec, n, i));
-			if (!path)
-				continue;
-			parse_capvol_in_path(codec, path);
-			if (!vol)
-				vol = path->ctls[NID_PATH_VOL_CTL];
-			else if (vol != path->ctls[NID_PATH_VOL_CTL])
-				multi = true;
-			if (!sw)
-				sw = path->ctls[NID_PATH_MUTE_CTL];
-			else if (sw != path->ctls[NID_PATH_MUTE_CTL])
-				multi = true;
-			if (is_inv_dmic_pin(codec, spec->imux_pins[i]))
-				inv_dmic = true;
-		}
-
-		if (!multi)
-			err = create_single_cap_vol_ctl(codec, n, vol, sw,
-							inv_dmic);
-		else if (!spec->multi_cap_vol)
-			err = create_bind_cap_vol_ctl(codec, n, vol, sw);
-		else
-			err = create_multi_cap_vol_ctl(codec);
-		if (err < 0)
-			return err;
-	}
-
-	return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int alc_auto_create_input_ctls(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	const struct auto_pin_cfg *cfg = &spec->autocfg;
-	hda_nid_t mixer = spec->mixer_nid;
-	struct hda_input_mux *imux = &spec->input_mux;
-	int num_adcs;
-	int i, c, err, type_idx = 0;
-	const char *prev_label = NULL;
-
-	num_adcs = alc_auto_fill_adc_nids(codec);
-	if (num_adcs < 0)
-		return 0;
-
-	for (i = 0; i < cfg->num_inputs; i++) {
-		hda_nid_t pin;
-		const char *label;
-		bool imux_added;
-
-		pin = cfg->inputs[i].pin;
-		if (!alc_is_input_pin(codec, pin))
-			continue;
-
-		label = hda_get_autocfg_input_label(codec, cfg, i);
-		if (spec->shared_mic_hp && !strcmp(label, "Misc"))
-			label = "Headphone Mic";
-		if (prev_label && !strcmp(label, prev_label))
-			type_idx++;
-		else
-			type_idx = 0;
-		prev_label = label;
-
-		if (mixer) {
-			if (is_reachable_path(codec, pin, mixer)) {
-				err = new_analog_input(codec, pin,
-						       label, type_idx, mixer);
-				if (err < 0)
-					return err;
-			}
-		}
-
-		imux_added = false;
-		for (c = 0; c < num_adcs; c++) {
-			struct nid_path *path;
-			hda_nid_t adc = spec->adc_nids[c];
-
-			if (!is_reachable_path(codec, pin, adc))
-				continue;
-			path = snd_array_new(&spec->paths);
-			if (!path)
-				return -ENOMEM;
-			memset(path, 0, sizeof(*path));
-			if (!parse_nid_path(codec, pin, adc, 2, path)) {
-				snd_printd(KERN_ERR
-					   "invalid input path 0x%x -> 0x%x\n",
-					   pin, adc);
-				spec->paths.used--;
-				continue;
-			}
-
-			if (!imux_added) {
-				spec->imux_pins[imux->num_items] = pin;
-				snd_hda_add_imux_item(imux, label,
-						      imux->num_items, NULL);
-				imux_added = true;
-			}
-		}
-	}
-
-	return 0;
-}
-
-/* create a shared input with the headphone out */
-static int alc_auto_create_shared_input(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	struct auto_pin_cfg *cfg = &spec->autocfg;
-	unsigned int defcfg;
-	hda_nid_t nid;
-
-	/* only one internal input pin? */
-	if (cfg->num_inputs != 1)
-		return 0;
-	defcfg = snd_hda_codec_get_pincfg(codec, cfg->inputs[0].pin);
-	if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT)
-		return 0;
-
-	if (cfg->hp_outs == 1 && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
-		nid = cfg->hp_pins[0]; /* OK, we have a single HP-out */
-	else if (cfg->line_outs == 1 && cfg->line_out_type == AUTO_PIN_HP_OUT)
-		nid = cfg->line_out_pins[0]; /* OK, we have a single line-out */
-	else
-		return 0; /* both not available */
-
-	if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_IN))
-		return 0; /* no input */
-
-	cfg->inputs[1].pin = nid;
-	cfg->inputs[1].type = AUTO_PIN_MIC;
-	cfg->num_inputs = 2;
-	spec->shared_mic_hp = 1;
-	snd_printdd("realtek: Enable shared I/O jack on NID 0x%x\n", nid);
-	return 0;
-}
-
-static int get_pin_type(int line_out_type)
-{
-	if (line_out_type == AUTO_PIN_HP_OUT)
-		return PIN_HP;
-	else
-		return PIN_OUT;
-}
-
-static void alc_auto_init_analog_input(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	struct auto_pin_cfg *cfg = &spec->autocfg;
-	int i;
-
-	for (i = 0; i < cfg->num_inputs; i++) {
-		hda_nid_t nid = cfg->inputs[i].pin;
-		if (alc_is_input_pin(codec, nid))
-			alc_set_input_pin(codec, nid, cfg->inputs[i].type);
-
-		/* mute loopback inputs */
-		if (spec->mixer_nid) {
-			struct nid_path *path;
-			path = get_nid_path(codec, nid, spec->mixer_nid);
-			if (path)
-				activate_path(codec, path, path->active, false);
-		}
-	}
-}
-
-static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid)
-{
-	struct alc_spec *spec = codec->spec;
-	int i;
-
-	for (i = 0; i < spec->paths.used; i++) {
-		struct nid_path *path = snd_array_elem(&spec->paths, i);
-		if (path->path[0] == nid)
-			return true;
-	}
-	return false;
-}
-
-/* look for an empty DAC slot */
-static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin,
-				       bool is_digital)
-{
-	struct alc_spec *spec = codec->spec;
-	bool cap_digital;
-	int i;
-
-	for (i = 0; i < spec->num_all_dacs; i++) {
-		hda_nid_t nid = spec->all_dacs[i];
-		if (!nid || alc_is_dac_already_used(codec, nid))
-			continue;
-		cap_digital = !!(get_wcaps(codec, nid) & AC_WCAP_DIGITAL);
-		if (is_digital != cap_digital)
-			continue;
-		if (is_reachable_path(codec, nid, pin))
-			return nid;
-	}
-	return 0;
-}
-
-/* called recursively */
-static bool __parse_nid_path(struct hda_codec *codec,
-			     hda_nid_t from_nid, hda_nid_t to_nid,
-			     int with_aa_mix, struct nid_path *path, int depth)
-{
-	struct alc_spec *spec = codec->spec;
-	hda_nid_t conn[16];
-	int i, nums;
-
-	if (to_nid == spec->mixer_nid) {
-		if (!with_aa_mix)
-			return false;
-		with_aa_mix = 2; /* mark aa-mix is included */
-	}
-
-	nums = snd_hda_get_connections(codec, to_nid, conn, ARRAY_SIZE(conn));
-	for (i = 0; i < nums; i++) {
-		if (conn[i] != from_nid) {
-			/* special case: when from_nid is 0,
-			 * try to find an empty DAC
-			 */
-			if (from_nid ||
-			    get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT ||
-			    alc_is_dac_already_used(codec, conn[i]))
-				continue;
-		}
-		/* aa-mix is requested but not included? */
-		if (!(spec->mixer_nid && with_aa_mix == 1))
-			goto found;
-	}
-	if (depth >= MAX_NID_PATH_DEPTH)
-		return false;
-	for (i = 0; i < nums; i++) {
-		unsigned int type;
-		type = get_wcaps_type(get_wcaps(codec, conn[i]));
-		if (type == AC_WID_AUD_OUT || type == AC_WID_AUD_IN ||
-		    type == AC_WID_PIN)
-			continue;
-		if (__parse_nid_path(codec, from_nid, conn[i],
-				     with_aa_mix, path, depth + 1))
-			goto found;
-	}
-	return false;
-
- found:
-	path->path[path->depth] = conn[i];
-	path->idx[path->depth + 1] = i;
-	if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX)
-		path->multi[path->depth + 1] = 1;
-	path->depth++;
-	return true;
-}
-
-/* parse the widget path from the given nid to the target nid;
- * when @from_nid is 0, try to find an empty DAC;
- * when @with_aa_mix is 0, paths with spec->mixer_nid are excluded.
- * when @with_aa_mix is 1, paths without spec->mixer_nid are excluded.
- * when @with_aa_mix is 2, no special handling about spec->mixer_nid.
- */
-static bool parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
-			   hda_nid_t to_nid, int with_aa_mix,
-			   struct nid_path *path)
-{
-	if (__parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path, 1)) {
-		path->path[path->depth] = to_nid;
-		path->depth++;
-#if 0
-		snd_printdd("path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
-			    path->depth, path->path[0], path->path[1],
-			    path->path[2], path->path[3], path->path[4]);
-#endif
-		return true;
-	}
-	return false;
-}
-
-static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin)
-{
-	struct alc_spec *spec = codec->spec;
-	int i;
-	hda_nid_t nid_found = 0;
-
-	for (i = 0; i < spec->num_all_dacs; i++) {
-		hda_nid_t nid = spec->all_dacs[i];
-		if (!nid || alc_is_dac_already_used(codec, nid))
-			continue;
-		if (is_reachable_path(codec, nid, pin)) {
-			if (nid_found)
-				return 0;
-			nid_found = nid;
-		}
-	}
-	return nid_found;
-}
-
-static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type)
-{
-	struct alc_spec *spec = codec->spec;
-	int i;
-
-	for (i = 0; i < spec->paths.used; i++) {
-		struct nid_path *path = snd_array_elem(&spec->paths, i);
-		if (path->ctls[type] == val)
-			return true;
-	}
-	return false;
-}
-
-/* badness definition */
-enum {
-	/* No primary DAC is found for the main output */
-	BAD_NO_PRIMARY_DAC = 0x10000,
-	/* No DAC is found for the extra output */
-	BAD_NO_DAC = 0x4000,
-	/* No possible multi-ios */
-	BAD_MULTI_IO = 0x103,
-	/* No individual DAC for extra output */
-	BAD_NO_EXTRA_DAC = 0x102,
-	/* No individual DAC for extra surrounds */
-	BAD_NO_EXTRA_SURR_DAC = 0x101,
-	/* Primary DAC shared with main surrounds */
-	BAD_SHARED_SURROUND = 0x100,
-	/* Primary DAC shared with main CLFE */
-	BAD_SHARED_CLFE = 0x10,
-	/* Primary DAC shared with extra surrounds */
-	BAD_SHARED_EXTRA_SURROUND = 0x10,
-	/* Volume widget is shared */
-	BAD_SHARED_VOL = 0x10,
-};
-
-static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec,
-					   struct nid_path *path);
-static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec,
-					  struct nid_path *path);
-
-static struct nid_path *add_new_nid_path(struct hda_codec *codec,
-					 hda_nid_t from_nid, hda_nid_t to_nid,
-					 int with_aa_mix)
-{
-	struct alc_spec *spec = codec->spec;
-	struct nid_path *path;
-
-	if (from_nid && to_nid && !is_reachable_path(codec, from_nid, to_nid))
-		return NULL;
-
-	path = snd_array_new(&spec->paths);
-	if (!path)
-		return NULL;
-	memset(path, 0, sizeof(*path));
-	if (parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path))
-		return path;
-	/* push back */
-	spec->paths.used--;
-	return NULL;
-}
-
-/* get the path between the given NIDs;
- * passing 0 to either @pin or @dac behaves as a wildcard
- */
-static struct nid_path *
-get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid)
-{
-	struct alc_spec *spec = codec->spec;
-	int i;
-
-	for (i = 0; i < spec->paths.used; i++) {
-		struct nid_path *path = snd_array_elem(&spec->paths, i);
-		if (path->depth <= 0)
-			continue;
-		if ((!from_nid || path->path[0] == from_nid) &&
-		    (!to_nid || path->path[path->depth - 1] == to_nid))
-			return path;
-	}
-	return NULL;
-}
-
-/* look for widgets in the path between the given NIDs appropriate for
- * volume and mute controls, and assign the values to ctls[].
- *
- * When no appropriate widget is found in the path, the badness value
- * is incremented depending on the situation.  The function returns the
- * total badness for both volume and mute controls.
- */
-static int assign_out_path_ctls(struct hda_codec *codec, hda_nid_t pin,
-				hda_nid_t dac)
-{
-	struct nid_path *path = get_nid_path(codec, dac, pin);
-	hda_nid_t nid;
-	unsigned int val;
-	int badness = 0;
-
-	if (!path)
-		return BAD_SHARED_VOL * 2;
-	nid = alc_look_for_out_vol_nid(codec, path);
-	if (nid) {
-		val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
-		if (is_ctl_used(codec, val, NID_PATH_VOL_CTL))
-			badness += BAD_SHARED_VOL;
-		else
-			path->ctls[NID_PATH_VOL_CTL] = val;
-	} else
-		badness += BAD_SHARED_VOL;
-	nid = alc_look_for_out_mute_nid(codec, path);
-	if (nid) {
-		unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid));
-		if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT ||
-		    nid_has_mute(codec, nid, HDA_OUTPUT))
-			val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
-		else
-			val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT);
-		if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL))
-			badness += BAD_SHARED_VOL;
-		else
-			path->ctls[NID_PATH_MUTE_CTL] = val;
-	} else
-		badness += BAD_SHARED_VOL;
-	return badness;
-}
-
-struct badness_table {
-	int no_primary_dac;	/* no primary DAC */
-	int no_dac;		/* no secondary DACs */
-	int shared_primary;	/* primary DAC is shared with main output */
-	int shared_surr;	/* secondary DAC shared with main or primary */
-	int shared_clfe;	/* third DAC shared with main or primary */
-	int shared_surr_main;	/* secondary DAC sahred with main/DAC0 */
-};
-
-static struct badness_table main_out_badness = {
-	.no_primary_dac = BAD_NO_PRIMARY_DAC,
-	.no_dac = BAD_NO_DAC,
-	.shared_primary = BAD_NO_PRIMARY_DAC,
-	.shared_surr = BAD_SHARED_SURROUND,
-	.shared_clfe = BAD_SHARED_CLFE,
-	.shared_surr_main = BAD_SHARED_SURROUND,
-};
-
-static struct badness_table extra_out_badness = {
-	.no_primary_dac = BAD_NO_DAC,
-	.no_dac = BAD_NO_DAC,
-	.shared_primary = BAD_NO_EXTRA_DAC,
-	.shared_surr = BAD_SHARED_EXTRA_SURROUND,
-	.shared_clfe = BAD_SHARED_EXTRA_SURROUND,
-	.shared_surr_main = BAD_NO_EXTRA_SURR_DAC,
-};
-
-/* try to assign DACs to pins and return the resultant badness */
-static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs,
-			      const hda_nid_t *pins, hda_nid_t *dacs,
-			      const struct badness_table *bad)
-{
-	struct alc_spec *spec = codec->spec;
-	struct auto_pin_cfg *cfg = &spec->autocfg;
-	int i, j;
-	int badness = 0;
-	hda_nid_t dac;
-
-	if (!num_outs)
-		return 0;
-
-	for (i = 0; i < num_outs; i++) {
-		hda_nid_t pin = pins[i];
-		if (!dacs[i])
-			dacs[i] = alc_auto_look_for_dac(codec, pin, false);
-		if (!dacs[i] && !i) {
-			for (j = 1; j < num_outs; j++) {
-				if (is_reachable_path(codec, dacs[j], pin)) {
-					dacs[0] = dacs[j];
-					dacs[j] = 0;
-					break;
-				}
-			}
-		}
-		dac = dacs[i];
-		if (!dac) {
-			if (is_reachable_path(codec, dacs[0], pin))
-				dac = dacs[0];
-			else if (cfg->line_outs > i &&
-				 is_reachable_path(codec, spec->private_dac_nids[i], pin))
-				dac = spec->private_dac_nids[i];
-			if (dac) {
-				if (!i)
-					badness += bad->shared_primary;
-				else if (i == 1)
-					badness += bad->shared_surr;
-				else
-					badness += bad->shared_clfe;
-			} else if (is_reachable_path(codec, spec->private_dac_nids[0], pin)) {
-				dac = spec->private_dac_nids[0];
-				badness += bad->shared_surr_main;
-			} else if (!i)
-				badness += bad->no_primary_dac;
-			else
-				badness += bad->no_dac;
-		}
-		if (!add_new_nid_path(codec, dac, pin, 0))
-			dac = dacs[i] = 0;
-		if (dac)
-			badness += assign_out_path_ctls(codec, pin, dac);
-	}
-
-	return badness;
-}
-
-static int alc_auto_fill_multi_ios(struct hda_codec *codec,
-				   hda_nid_t reference_pin,
-				   bool hardwired, int offset);
-
-static bool alc_map_singles(struct hda_codec *codec, int outs,
-			    const hda_nid_t *pins, hda_nid_t *dacs)
-{
-	int i;
-	bool found = false;
-	for (i = 0; i < outs; i++) {
-		hda_nid_t dac;
-		if (dacs[i])
-			continue;
-		dac = get_dac_if_single(codec, pins[i]);
-		if (!dac)
-			continue;
-		if (add_new_nid_path(codec, dac, pins[i], 0)) {
-			dacs[i] = dac;
-			found = true;
-		}
-	}
-	return found;
-}
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int fill_and_eval_dacs(struct hda_codec *codec,
-			      bool fill_hardwired,
-			      bool fill_mio_first)
-{
-	struct alc_spec *spec = codec->spec;
-	struct auto_pin_cfg *cfg = &spec->autocfg;
-	int i, err, badness;
-
-	/* set num_dacs once to full for alc_auto_look_for_dac() */
-	spec->multiout.num_dacs = cfg->line_outs;
-	spec->multiout.dac_nids = spec->private_dac_nids;
-	memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids));
-	memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid));
-	memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid));
-	spec->multi_ios = 0;
-	snd_array_free(&spec->paths);
-	badness = 0;
-
-	/* fill hard-wired DACs first */
-	if (fill_hardwired) {
-		bool mapped;
-		do {
-			mapped = alc_map_singles(codec, cfg->line_outs,
-						 cfg->line_out_pins,
-						 spec->private_dac_nids);
-			mapped |= alc_map_singles(codec, cfg->hp_outs,
-						  cfg->hp_pins,
-						  spec->multiout.hp_out_nid);
-			mapped |= alc_map_singles(codec, cfg->speaker_outs,
-						  cfg->speaker_pins,
-						  spec->multiout.extra_out_nid);
-			if (fill_mio_first && cfg->line_outs == 1 &&
-			    cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
-				err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], true, 0);
-				if (!err)
-					mapped = true;
-			}
-		} while (mapped);
-	}
-
-	badness += alc_auto_fill_dacs(codec, cfg->line_outs, cfg->line_out_pins,
-				      spec->private_dac_nids,
-				      &main_out_badness);
-
-	/* re-count num_dacs and squash invalid entries */
-	spec->multiout.num_dacs = 0;
-	for (i = 0; i < cfg->line_outs; i++) {
-		if (spec->private_dac_nids[i])
-			spec->multiout.num_dacs++;
-		else {
-			memmove(spec->private_dac_nids + i,
-				spec->private_dac_nids + i + 1,
-				sizeof(hda_nid_t) * (cfg->line_outs - i - 1));
-			spec->private_dac_nids[cfg->line_outs - 1] = 0;
-		}
-	}
-
-	if (fill_mio_first &&
-	    cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
-		/* try to fill multi-io first */
-		err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], false, 0);
-		if (err < 0)
-			return err;
-		/* we don't count badness at this stage yet */
-	}
-
-	if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
-		err = alc_auto_fill_dacs(codec, cfg->hp_outs, cfg->hp_pins,
-					 spec->multiout.hp_out_nid,
-					 &extra_out_badness);
-		if (err < 0)
-			return err;
-		badness += err;
-	}
-	if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
-		err = alc_auto_fill_dacs(codec, cfg->speaker_outs,
-					 cfg->speaker_pins,
-					 spec->multiout.extra_out_nid,
-					 &extra_out_badness);
-		if (err < 0)
-			return err;
-		badness += err;
-	}
-	if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
-		err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], false, 0);
-		if (err < 0)
-			return err;
-		badness += err;
-	}
-	if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
-		/* try multi-ios with HP + inputs */
-		int offset = 0;
-		if (cfg->line_outs >= 3)
-			offset = 1;
-		err = alc_auto_fill_multi_ios(codec, cfg->hp_pins[0], false,
-					      offset);
-		if (err < 0)
-			return err;
-		badness += err;
-	}
-
-	if (spec->multi_ios == 2) {
-		for (i = 0; i < 2; i++)
-			spec->private_dac_nids[spec->multiout.num_dacs++] =
-				spec->multi_io[i].dac;
-		spec->ext_channel_count = 2;
-	} else if (spec->multi_ios) {
-		spec->multi_ios = 0;
-		badness += BAD_MULTI_IO;
-	}
-
-	return badness;
-}
-
-#define DEBUG_BADNESS
-
-#ifdef DEBUG_BADNESS
-#define debug_badness	snd_printdd
-#else
-#define debug_badness(...)
-#endif
-
-static void debug_show_configs(struct alc_spec *spec, struct auto_pin_cfg *cfg)
-{
-	debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
-		      cfg->line_out_pins[0], cfg->line_out_pins[1],
-		      cfg->line_out_pins[2], cfg->line_out_pins[2],
-		      spec->multiout.dac_nids[0],
-		      spec->multiout.dac_nids[1],
-		      spec->multiout.dac_nids[2],
-		      spec->multiout.dac_nids[3]);
-	if (spec->multi_ios > 0)
-		debug_badness("multi_ios(%d) = %x/%x : %x/%x\n",
-			      spec->multi_ios,
-			      spec->multi_io[0].pin, spec->multi_io[1].pin,
-			      spec->multi_io[0].dac, spec->multi_io[1].dac);
-	debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
-		      cfg->hp_pins[0], cfg->hp_pins[1],
-		      cfg->hp_pins[2], cfg->hp_pins[2],
-		      spec->multiout.hp_out_nid[0],
-		      spec->multiout.hp_out_nid[1],
-		      spec->multiout.hp_out_nid[2],
-		      spec->multiout.hp_out_nid[3]);
-	debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
-		      cfg->speaker_pins[0], cfg->speaker_pins[1],
-		      cfg->speaker_pins[2], cfg->speaker_pins[3],
-		      spec->multiout.extra_out_nid[0],
-		      spec->multiout.extra_out_nid[1],
-		      spec->multiout.extra_out_nid[2],
-		      spec->multiout.extra_out_nid[3]);
-}
-
-/* find all available DACs of the codec */
-static void alc_fill_all_nids(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	int i;
-	hda_nid_t nid = codec->start_nid;
-
-	spec->num_all_dacs = 0;
-	memset(spec->all_dacs, 0, sizeof(spec->all_dacs));
-	for (i = 0; i < codec->num_nodes; i++, nid++) {
-		if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_OUT)
-			continue;
-		if (spec->num_all_dacs >= ARRAY_SIZE(spec->all_dacs)) {
-			snd_printk(KERN_ERR "hda: Too many DACs!\n");
-			break;
-		}
-		spec->all_dacs[spec->num_all_dacs++] = nid;
-	}
-}
-
-static int alc_auto_fill_dac_nids(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	struct auto_pin_cfg *cfg = &spec->autocfg;
-	struct auto_pin_cfg *best_cfg;
-	int best_badness = INT_MAX;
-	int badness;
-	bool fill_hardwired = true, fill_mio_first = true;
-	bool best_wired = true, best_mio = true;
-	bool hp_spk_swapped = false;
-
-	alc_fill_all_nids(codec);
-
-	best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL);
-	if (!best_cfg)
-		return -ENOMEM;
-	*best_cfg = *cfg;
-
-	for (;;) {
-		badness = fill_and_eval_dacs(codec, fill_hardwired,
-					     fill_mio_first);
-		if (badness < 0) {
-			kfree(best_cfg);
-			return badness;
-		}
-		debug_badness("==> lo_type=%d, wired=%d, mio=%d, badness=0x%x\n",
-			      cfg->line_out_type, fill_hardwired, fill_mio_first,
-			      badness);
-		debug_show_configs(spec, cfg);
-		if (badness < best_badness) {
-			best_badness = badness;
-			*best_cfg = *cfg;
-			best_wired = fill_hardwired;
-			best_mio = fill_mio_first;
-		}
-		if (!badness)
-			break;
-		fill_mio_first = !fill_mio_first;
-		if (!fill_mio_first)
-			continue;
-		fill_hardwired = !fill_hardwired;
-		if (!fill_hardwired)
-			continue;
-		if (hp_spk_swapped)
-			break;
-		hp_spk_swapped = true;
-		if (cfg->speaker_outs > 0 &&
-		    cfg->line_out_type == AUTO_PIN_HP_OUT) {
-			cfg->hp_outs = cfg->line_outs;
-			memcpy(cfg->hp_pins, cfg->line_out_pins,
-			       sizeof(cfg->hp_pins));
-			cfg->line_outs = cfg->speaker_outs;
-			memcpy(cfg->line_out_pins, cfg->speaker_pins,
-			       sizeof(cfg->speaker_pins));
-			cfg->speaker_outs = 0;
-			memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
-			cfg->line_out_type = AUTO_PIN_SPEAKER_OUT;
-			fill_hardwired = true;
-			continue;
-		}
-		if (cfg->hp_outs > 0 &&
-		    cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
-			cfg->speaker_outs = cfg->line_outs;
-			memcpy(cfg->speaker_pins, cfg->line_out_pins,
-			       sizeof(cfg->speaker_pins));
-			cfg->line_outs = cfg->hp_outs;
-			memcpy(cfg->line_out_pins, cfg->hp_pins,
-			       sizeof(cfg->hp_pins));
-			cfg->hp_outs = 0;
-			memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
-			cfg->line_out_type = AUTO_PIN_HP_OUT;
-			fill_hardwired = true;
-			continue;
-		}
-		break;
-	}
-
-	if (badness) {
-		*cfg = *best_cfg;
-		fill_and_eval_dacs(codec, best_wired, best_mio);
-	}
-	debug_badness("==> Best config: lo_type=%d, wired=%d, mio=%d\n",
-		      cfg->line_out_type, best_wired, best_mio);
-	debug_show_configs(spec, cfg);
-
-	if (cfg->line_out_pins[0]) {
-		struct nid_path *path = get_nid_path(codec,
-						     spec->multiout.dac_nids[0],
-						     cfg->line_out_pins[0]);
-		if (path)
-			spec->vmaster_nid = alc_look_for_out_vol_nid(codec, path);
-	}
-
-	kfree(best_cfg);
-	return 0;
-}
-
-/* replace the channels in the composed amp value with the given number */
-static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs)
-{
-	val &= ~(0x3U << 16);
-	val |= chs << 16;
-	return val;
-}
-
-static int alc_auto_add_vol_ctl(struct hda_codec *codec,
-				const char *pfx, int cidx,
-				unsigned int chs,
-				struct nid_path *path)
-{
-	unsigned int val;
-	if (!path)
-		return 0;
-	val = path->ctls[NID_PATH_VOL_CTL];
-	if (!val)
-		return 0;
-	val = amp_val_replace_channels(val, chs);
-	return __add_pb_vol_ctrl(codec->spec, ALC_CTL_WIDGET_VOL, pfx, cidx, val);
-}
-
-/* return the channel bits suitable for the given path->ctls[] */
-static int get_default_ch_nums(struct hda_codec *codec, struct nid_path *path,
-			       int type)
-{
-	int chs = 1; /* mono (left only) */
-	if (path) {
-		hda_nid_t nid = get_amp_nid_(path->ctls[type]);
-		if (nid && (get_wcaps(codec, nid) & AC_WCAP_STEREO))
-			chs = 3; /* stereo */
-	}
-	return chs;
-}
-
-static int alc_auto_add_stereo_vol(struct hda_codec *codec,
-				   const char *pfx, int cidx,
-				   struct nid_path *path)
-{
-	int chs = get_default_ch_nums(codec, path, NID_PATH_VOL_CTL);
-	return alc_auto_add_vol_ctl(codec, pfx, cidx, chs, path);
-}
-
-/* create a mute-switch for the given mixer widget;
- * if it has multiple sources (e.g. DAC and loopback), create a bind-mute
- */
-static int alc_auto_add_sw_ctl(struct hda_codec *codec,
-			       const char *pfx, int cidx,
-			       unsigned int chs,
-			       struct nid_path *path)
-{
-	unsigned int val;
-	int type = ALC_CTL_WIDGET_MUTE;
-
-	if (!path)
-		return 0;
-	val = path->ctls[NID_PATH_MUTE_CTL];
-	if (!val)
-		return 0;
-	val = amp_val_replace_channels(val, chs);
-	if (get_amp_direction_(val) == HDA_INPUT) {
-		hda_nid_t nid = get_amp_nid_(val);
-		int nums = snd_hda_get_num_conns(codec, nid);
-		if (nums > 1) {
-			type = ALC_CTL_BIND_MUTE;
-			val |= nums << 19;
-		}
-	}
-	return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val);
-}
-
-static int alc_auto_add_stereo_sw(struct hda_codec *codec, const char *pfx,
-				  int cidx, struct nid_path *path)
-{
-	int chs = get_default_ch_nums(codec, path, NID_PATH_MUTE_CTL);
-	return alc_auto_add_sw_ctl(codec, pfx, cidx, chs, path);
-}
-
-static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec,
-					   struct nid_path *path)
-{
-	int i;
-
-	for (i = path->depth - 1; i >= 0; i--) {
-		if (nid_has_mute(codec, path->path[i], HDA_OUTPUT))
-			return path->path[i];
-		if (i != path->depth - 1 && i != 0 &&
-		    nid_has_mute(codec, path->path[i], HDA_INPUT))
-			return path->path[i];
-	}
-	return 0;
-}
-
-static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec,
-					  struct nid_path *path)
-{
-	int i;
-
-	for (i = path->depth - 1; i >= 0; i--) {
-		if (nid_has_volume(codec, path->path[i], HDA_OUTPUT))
-			return path->path[i];
-	}
-	return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int alc_auto_create_multi_out_ctls(struct hda_codec *codec,
-					     const struct auto_pin_cfg *cfg)
-{
-	struct alc_spec *spec = codec->spec;
-	int i, err, noutputs;
-
-	noutputs = cfg->line_outs;
-	if (spec->multi_ios > 0 && cfg->line_outs < 3)
-		noutputs += spec->multi_ios;
-
-	for (i = 0; i < noutputs; i++) {
-		const char *name;
-		int index;
-		hda_nid_t dac, pin;
-		struct nid_path *path;
-
-		dac = spec->multiout.dac_nids[i];
-		if (!dac)
-			continue;
-		if (i >= cfg->line_outs) {
-			pin = spec->multi_io[i - 1].pin;
-			index = 0;
-			name = channel_name[i];
-		} else {
-			pin = cfg->line_out_pins[i];
-			name = alc_get_line_out_pfx(spec, i, true, &index);
-		}
-
-		path = get_nid_path(codec, dac, pin);
-		if (!path)
-			continue;
-		if (!name || !strcmp(name, "CLFE")) {
-			/* Center/LFE */
-			err = alc_auto_add_vol_ctl(codec, "Center", 0, 1, path);
-			if (err < 0)
-				return err;
-			err = alc_auto_add_vol_ctl(codec, "LFE", 0, 2, path);
-			if (err < 0)
-				return err;
-			err = alc_auto_add_sw_ctl(codec, "Center", 0, 1, path);
-			if (err < 0)
-				return err;
-			err = alc_auto_add_sw_ctl(codec, "LFE", 0, 2, path);
-			if (err < 0)
-				return err;
-		} else {
-			err = alc_auto_add_stereo_vol(codec, name, index, path);
-			if (err < 0)
-				return err;
-			err = alc_auto_add_stereo_sw(codec, name, index, path);
-			if (err < 0)
-				return err;
-		}
-	}
-	return 0;
-}
-
-static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
-				     hda_nid_t dac, const char *pfx,
-				     int cidx)
-{
-	struct nid_path *path;
-	int err;
-
-	path = get_nid_path(codec, dac, pin);
-	if (!path)
-		return 0;
-	/* bind volume control will be created in the case of dac = 0 */
-	if (dac) {
-		err = alc_auto_add_stereo_vol(codec, pfx, cidx, path);
-		if (err < 0)
-			return err;
-	}
-	err = alc_auto_add_stereo_sw(codec, pfx, cidx, path);
-	if (err < 0)
-		return err;
-	return 0;
-}
-
-static struct hda_bind_ctls *new_bind_ctl(struct hda_codec *codec,
-					  unsigned int nums,
-					  struct hda_ctl_ops *ops)
-{
-	struct alc_spec *spec = codec->spec;
-	struct hda_bind_ctls **ctlp, *ctl;
-	ctlp = snd_array_new(&spec->bind_ctls);
-	if (!ctlp)
-		return NULL;
-	ctl = kzalloc(sizeof(*ctl) + sizeof(long) * (nums + 1), GFP_KERNEL);
-	*ctlp = ctl;
-	if (ctl)
-		ctl->ops = ops;
-	return ctl;
-}
-
-/* add playback controls for speaker and HP outputs */
-static int alc_auto_create_extra_outs(struct hda_codec *codec, int num_pins,
-				      const hda_nid_t *pins,
-				      const hda_nid_t *dacs,
-				      const char *pfx)
-{
-	struct alc_spec *spec = codec->spec;
-	struct hda_bind_ctls *ctl;
-	char name[32];
-	int i, n, err;
-
-	if (!num_pins || !pins[0])
-		return 0;
-
-	if (num_pins == 1) {
-		hda_nid_t dac = *dacs;
-		if (!dac)
-			dac = spec->multiout.dac_nids[0];
-		return alc_auto_create_extra_out(codec, *pins, dac, pfx, 0);
-	}
-
-	for (i = 0; i < num_pins; i++) {
-		hda_nid_t dac;
-		if (dacs[num_pins - 1])
-			dac = dacs[i]; /* with individual volumes */
-		else
-			dac = 0;
-		if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) {
-			err = alc_auto_create_extra_out(codec, pins[i], dac,
-							"Bass Speaker", 0);
-		} else if (num_pins >= 3) {
-			snprintf(name, sizeof(name), "%s %s",
-				 pfx, channel_name[i]);
-			err = alc_auto_create_extra_out(codec, pins[i], dac,
-							name, 0);
-		} else {
-			err = alc_auto_create_extra_out(codec, pins[i], dac,
-							pfx, i);
-		}
-		if (err < 0)
-			return err;
-	}
-	if (dacs[num_pins - 1])
-		return 0;
-
-	/* Let's create a bind-controls for volumes */
-	ctl = new_bind_ctl(codec, num_pins, &snd_hda_bind_vol);
-	if (!ctl)
-		return -ENOMEM;
-	n = 0;
-	for (i = 0; i < num_pins; i++) {
-		hda_nid_t vol;
-		struct nid_path *path;
-		if (!pins[i] || !dacs[i])
-			continue;
-		path = get_nid_path(codec, dacs[i], pins[i]);
-		if (!path)
-			continue;
-		vol = alc_look_for_out_vol_nid(codec, path);
-		if (vol)
-			ctl->values[n++] =
-				HDA_COMPOSE_AMP_VAL(vol, 3, 0, HDA_OUTPUT);
-	}
-	if (n) {
-		snprintf(name, sizeof(name), "%s Playback Volume", pfx);
-		err = add_control(spec, ALC_CTL_BIND_VOL, name, 0, (long)ctl);
-		if (err < 0)
-			return err;
-	}
-	return 0;
-}
-
-static int alc_auto_create_hp_out(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	return alc_auto_create_extra_outs(codec, spec->autocfg.hp_outs,
-					  spec->autocfg.hp_pins,
-					  spec->multiout.hp_out_nid,
-					  "Headphone");
-}
-
-static int alc_auto_create_speaker_out(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	return alc_auto_create_extra_outs(codec, spec->autocfg.speaker_outs,
-					  spec->autocfg.speaker_pins,
-					  spec->multiout.extra_out_nid,
-					  "Speaker");
-}
-
-/* check whether a control with the given (nid, dir, idx) was assigned */
-static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid,
-			      int dir, int idx)
-{
-	struct alc_spec *spec = codec->spec;
-	int i, type;
-
-	for (i = 0; i < spec->paths.used; i++) {
-		struct nid_path *p = snd_array_elem(&spec->paths, i);
-		if (p->depth <= 0)
-			continue;
-		for (type = 0; type < NID_PATH_NUM_CTLS; type++) {
-			unsigned int val = p->ctls[type];
-			if (get_amp_nid_(val) == nid &&
-			    get_amp_direction_(val) == dir &&
-			    get_amp_index_(val) == idx)
-				return true;
-		}
-	}
-	return false;
-}
-
-/* can have the amp-in capability? */
-static bool has_amp_in(struct hda_codec *codec, struct nid_path *path, int idx)
-{
-	hda_nid_t nid = path->path[idx];
-	unsigned int caps = get_wcaps(codec, nid);
-	unsigned int type = get_wcaps_type(caps);
-
-	if (!(caps & AC_WCAP_IN_AMP))
-		return false;
-	if (type == AC_WID_PIN && idx > 0) /* only for input pins */
-		return false;
-	return true;
-}
-
-/* can have the amp-out capability? */
-static bool has_amp_out(struct hda_codec *codec, struct nid_path *path, int idx)
-{
-	hda_nid_t nid = path->path[idx];
-	unsigned int caps = get_wcaps(codec, nid);
-	unsigned int type = get_wcaps_type(caps);
-
-	if (!(caps & AC_WCAP_OUT_AMP))
-		return false;
-	if (type == AC_WID_PIN && !idx) /* only for output pins */
-		return false;
-	return true;
-}
-
-/* check whether the given (nid,dir,idx) is active */
-static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid,
-			  unsigned int idx, unsigned int dir)
-{
-	struct alc_spec *spec = codec->spec;
-	int i, n;
-
-	for (n = 0; n < spec->paths.used; n++) {
-		struct nid_path *path = snd_array_elem(&spec->paths, n);
-		if (!path->active)
-			continue;
-		for (i = 0; i < path->depth; i++) {
-			if (path->path[i] == nid) {
-				if (dir == HDA_OUTPUT || path->idx[i] == idx)
-					return true;
-				break;
-			}
-		}
-	}
-	return false;
-}
-
-/* get the default amp value for the target state */
-static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid,
-				   int dir, bool enable)
-{
-	unsigned int caps;
-	unsigned int val = 0;
-
-	caps = query_amp_caps(codec, nid, dir);
-	if (caps & AC_AMPCAP_NUM_STEPS) {
-		/* set to 0dB */
-		if (enable)
-			val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
-	}
-	if (caps & AC_AMPCAP_MUTE) {
-		if (!enable)
-			val |= HDA_AMP_MUTE;
-	}
-	return val;
-}
-
-/* initialize the amp value (only at the first time) */
-static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx)
-{
-	int val = get_amp_val_to_activate(codec, nid, dir, false);
-	snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val);
-}
-
-static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir,
-			 int idx, bool enable)
-{
-	int val;
-	if (is_ctl_associated(codec, nid, dir, idx) ||
-	    is_active_nid(codec, nid, dir, idx))
-		return;
-	val = get_amp_val_to_activate(codec, nid, dir, enable);
-	snd_hda_codec_amp_stereo(codec, nid, dir, idx, 0xff, val);
-}
-
-static void activate_amp_out(struct hda_codec *codec, struct nid_path *path,
-			     int i, bool enable)
-{
-	hda_nid_t nid = path->path[i];
-	init_amp(codec, nid, HDA_OUTPUT, 0);
-	activate_amp(codec, nid, HDA_OUTPUT, 0, enable);
-}
-
-static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
-			    int i, bool enable, bool add_aamix)
-{
-	struct alc_spec *spec = codec->spec;
-	hda_nid_t conn[16];
-	int n, nums, idx;
-	int type;
-	hda_nid_t nid = path->path[i];
-
-	nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
-	type = get_wcaps_type(get_wcaps(codec, nid));
-	if (type == AC_WID_PIN ||
-	    (type == AC_WID_AUD_IN && codec->single_adc_amp)) {
-		nums = 1;
-		idx = 0;
-	} else
-		idx = path->idx[i];
-
-	for (n = 0; n < nums; n++)
-		init_amp(codec, nid, HDA_INPUT, n);
-
-	if (is_ctl_associated(codec, nid, HDA_INPUT, idx))
-		return;
-
-	/* here is a little bit tricky in comparison with activate_amp_out();
-	 * when aa-mixer is available, we need to enable the path as well
-	 */
-	for (n = 0; n < nums; n++) {
-		if (n != idx && (!add_aamix || conn[n] != spec->mixer_nid))
-			continue;
-		activate_amp(codec, nid, HDA_INPUT, n, enable);
-	}
-}
-
-static void activate_path(struct hda_codec *codec, struct nid_path *path,
-			  bool enable, bool add_aamix)
-{
-	int i;
-
-	if (!enable)
-		path->active = false;
-
-	for (i = path->depth - 1; i >= 0; i--) {
-		if (enable && path->multi[i])
-			snd_hda_codec_write_cache(codec, path->path[i], 0,
-					    AC_VERB_SET_CONNECT_SEL,
-					    path->idx[i]);
-		if (has_amp_in(codec, path, i))
-			activate_amp_in(codec, path, i, enable, add_aamix);
-		if (has_amp_out(codec, path, i))
-			activate_amp_out(codec, path, i, enable);
-	}
-
-	if (enable)
-		path->active = true;
-}
-
-/* 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)
-{
-	struct nid_path *path;
-
-	snd_hda_set_pin_ctl_cache(codec, pin, pin_type);
-	path = get_nid_path(codec, dac, pin);
-	if (!path)
-		return;
-	if (path->active)
-		return;
-	activate_path(codec, path, true, true);
-}
-
-static void alc_auto_init_multi_out(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	int pin_type = get_pin_type(spec->autocfg.line_out_type);
-	int i;
+}
 
-	for (i = 0; i <= HDA_SIDE; i++) {
-		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]);
 
-	}
-}
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+/* additional beep mixers; the actual parameters are overwritten at build */
+static const struct snd_kcontrol_new alc_beep_mixer[] = {
+	HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT),
+	HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT),
+	{ } /* end */
+};
+#endif
 
-static void alc_auto_init_extra_out(struct hda_codec *codec)
+static int alc_build_controls(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
-	int i;
-	hda_nid_t pin, dac;
+	int i, err;
 
-	for (i = 0; i < spec->autocfg.hp_outs; i++) {
-		if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
-			break;
-		pin = spec->autocfg.hp_pins[i];
-		if (!pin)
-			break;
-		dac = spec->multiout.hp_out_nid[i];
-		if (!dac) {
-			if (i > 0 && spec->multiout.hp_out_nid[0])
-				dac = spec->multiout.hp_out_nid[0];
-			else
-				dac = spec->multiout.dac_nids[0];
-		}
-		alc_auto_set_output_and_unmute(codec, pin, PIN_HP, dac);
+	err = snd_hda_gen_build_controls(codec);
+	if (err < 0)
+		return err;
+
+	for (i = 0; i < spec->num_mixers; i++) {
+		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
+		if (err < 0)
+			return err;
 	}
-	for (i = 0; i < spec->autocfg.speaker_outs; i++) {
-		if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
-			break;
-		pin = spec->autocfg.speaker_pins[i];
-		if (!pin)
-			break;
-		dac = spec->multiout.extra_out_nid[i];
-		if (!dac) {
-			if (i > 0 && spec->multiout.extra_out_nid[0])
-				dac = spec->multiout.extra_out_nid[0];
-			else
-				dac = spec->multiout.dac_nids[0];
+
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+	/* create beep controls if needed */
+	if (spec->beep_amp) {
+		const struct snd_kcontrol_new *knew;
+		for (knew = alc_beep_mixer; knew->name; knew++) {
+			struct snd_kcontrol *kctl;
+			kctl = snd_ctl_new1(knew, codec);
+			if (!kctl)
+				return -ENOMEM;
+			kctl->private_value = spec->beep_amp;
+			err = snd_hda_ctl_add(codec, 0, kctl);
+			if (err < 0)
+				return err;
 		}
-		alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac);
 	}
-}
+#endif
 
-/* check whether the given pin can be a multi-io pin */
-static bool can_be_multiio_pin(struct hda_codec *codec,
-			       unsigned int location, hda_nid_t nid)
-{
-	unsigned int defcfg, caps;
-
-	defcfg = snd_hda_codec_get_pincfg(codec, nid);
-	if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX)
-		return false;
-	if (location && get_defcfg_location(defcfg) != location)
-		return false;
-	caps = snd_hda_query_pin_caps(codec, nid);
-	if (!(caps & AC_PINCAP_OUT))
-		return false;
-	return true;
+	alc_apply_fixup(codec, ALC_FIXUP_ACT_BUILD);
+	return 0;
 }
 
+
 /*
- * multi-io helper
- *
- * When hardwired is set, try to fill ony hardwired pins, and returns
- * zero if any pins are filled, non-zero if nothing found.
- * When hardwired is off, try to fill possible input pins, and returns
- * the badness value.
+ * Common callbacks
  */
-static int alc_auto_fill_multi_ios(struct hda_codec *codec,
-				   hda_nid_t reference_pin,
-				   bool hardwired, int offset)
+
+static int alc_init(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
-	struct auto_pin_cfg *cfg = &spec->autocfg;
-	int type, i, j, dacs, num_pins, old_pins;
-	unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin);
-	unsigned int location = get_defcfg_location(defcfg);
-	int badness = 0;
-
-	old_pins = spec->multi_ios;
-	if (old_pins >= 2)
-		goto end_fill;
-
-	num_pins = 0;
-	for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
-		for (i = 0; i < cfg->num_inputs; i++) {
-			if (cfg->inputs[i].type != type)
-				continue;
-			if (can_be_multiio_pin(codec, location,
-					       cfg->inputs[i].pin))
-				num_pins++;
-		}
-	}
-	if (num_pins < 2)
-		goto end_fill;
-
-	dacs = spec->multiout.num_dacs;
-	for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
-		for (i = 0; i < cfg->num_inputs; i++) {
-			hda_nid_t nid = cfg->inputs[i].pin;
-			hda_nid_t dac = 0;
-
-			if (cfg->inputs[i].type != type)
-				continue;
-			if (!can_be_multiio_pin(codec, location, nid))
-				continue;
-			for (j = 0; j < spec->multi_ios; j++) {
-				if (nid == spec->multi_io[j].pin)
-					break;
-			}
-			if (j < spec->multi_ios)
-				continue;
-
-			if (offset && offset + spec->multi_ios < dacs) {
-				dac = spec->private_dac_nids[offset + spec->multi_ios];
-				if (!is_reachable_path(codec, dac, nid))
-					dac = 0;
-			}
-			if (hardwired)
-				dac = get_dac_if_single(codec, nid);
-			else if (!dac)
-				dac = alc_auto_look_for_dac(codec, nid, false);
-			if (!dac) {
-				badness++;
-				continue;
-			}
-			if (!add_new_nid_path(codec, dac, nid, 0)) {
-				badness++;
-				continue;
-			}
-			spec->multi_io[spec->multi_ios].pin = nid;
-			spec->multi_io[spec->multi_ios].dac = dac;
-			spec->multi_ios++;
-			if (spec->multi_ios >= 2)
-				break;
-		}
-	}
- end_fill:
-	if (badness)
-		badness = BAD_MULTI_IO;
-	if (old_pins == spec->multi_ios) {
-		if (hardwired)
-			return 1; /* nothing found */
-		else
-			return badness; /* no badness if nothing found */
-	}
-	if (!hardwired && spec->multi_ios < 2) {
-		/* cancel newly assigned paths */
-		spec->paths.used -= spec->multi_ios - old_pins;
-		spec->multi_ios = old_pins;
-		return badness;
-	}
 
-	/* assign volume and mute controls */
-	for (i = old_pins; i < spec->multi_ios; i++)
-		badness += assign_out_path_ctls(codec, spec->multi_io[i].pin,
-						spec->multi_io[i].dac);
+	if (spec->init_hook)
+		spec->init_hook(codec);
 
-	return badness;
-}
+	alc_fix_pll(codec);
+	alc_auto_init_amp(codec, spec->init_amp);
 
-static int alc_auto_ch_mode_info(struct snd_kcontrol *kcontrol,
-				 struct snd_ctl_elem_info *uinfo)
-{
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct alc_spec *spec = codec->spec;
+	snd_hda_gen_init(codec);
+
+	alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT);
 
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-	uinfo->count = 1;
-	uinfo->value.enumerated.items = spec->multi_ios + 1;
-	if (uinfo->value.enumerated.item > spec->multi_ios)
-		uinfo->value.enumerated.item = spec->multi_ios;
-	sprintf(uinfo->value.enumerated.name, "%dch",
-		(uinfo->value.enumerated.item + 1) * 2);
 	return 0;
 }
 
-static int alc_auto_ch_mode_get(struct snd_kcontrol *kcontrol,
-				struct snd_ctl_elem_value *ucontrol)
+#ifdef CONFIG_PM
+static int alc_check_power_status(struct hda_codec *codec, hda_nid_t nid)
 {
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct alc_spec *spec = codec->spec;
-	ucontrol->value.enumerated.item[0] = (spec->ext_channel_count - 1) / 2;
-	return 0;
+	return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid);
 }
+#endif
 
-static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output)
+static inline void alc_shutup(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
-	hda_nid_t nid = spec->multi_io[idx].pin;
-	struct nid_path *path;
-
-	path = get_nid_path(codec, spec->multi_io[idx].dac, nid);
-	if (!path)
-		return -EINVAL;
-
-	if (path->active == output)
-		return 0;
 
-	if (output) {
-		snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT);
-		activate_path(codec, path, true, true);
-	} else {
-		activate_path(codec, path, false, true);
-		snd_hda_set_pin_ctl_cache(codec, nid,
-					  spec->multi_io[idx].ctl_in);
-	}
-	return 0;
+	if (spec && spec->shutup)
+		spec->shutup(codec);
+	snd_hda_shutup_pins(codec);
 }
 
-static int alc_auto_ch_mode_put(struct snd_kcontrol *kcontrol,
-				struct snd_ctl_elem_value *ucontrol)
+static void alc_free(struct hda_codec *codec)
 {
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct alc_spec *spec = codec->spec;
-	int i, ch;
 
-	ch = ucontrol->value.enumerated.item[0];
-	if (ch < 0 || ch > spec->multi_ios)
-		return -EINVAL;
-	if (ch == (spec->ext_channel_count - 1) / 2)
-		return 0;
-	spec->ext_channel_count = (ch + 1) * 2;
-	for (i = 0; i < spec->multi_ios; i++)
-		alc_set_multi_io(codec, i, i < ch);
-	spec->multiout.max_channels = max(spec->ext_channel_count,
-					  spec->const_channel_count);
-	if (spec->need_dac_fix)
-		spec->multiout.num_dacs = spec->multiout.max_channels / 2;
-	return 1;
+	if (!spec)
+		return;
+
+	snd_hda_gen_spec_free(&spec->gen);
+	snd_hda_detach_beep_device(codec);
+	kfree(spec);
 }
 
-static const struct snd_kcontrol_new alc_auto_channel_mode_enum = {
-	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-	.name = "Channel Mode",
-	.info = alc_auto_ch_mode_info,
-	.get = alc_auto_ch_mode_get,
-	.put = alc_auto_ch_mode_put,
-};
+#ifdef CONFIG_PM
+static void alc_power_eapd(struct hda_codec *codec)
+{
+	alc_auto_setup_eapd(codec, false);
+}
 
-static int alc_auto_add_multi_channel_mode(struct hda_codec *codec)
+static int alc_suspend(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
-
-	if (spec->multi_ios > 0) {
-		if (!alc_kcontrol_new(spec, NULL, &alc_auto_channel_mode_enum))
-			return -ENOMEM;
-	}
+	alc_shutup(codec);
+	if (spec && spec->power_hook)
+		spec->power_hook(codec);
 	return 0;
 }
+#endif
 
-static void alc_auto_init_multi_io(struct hda_codec *codec)
+#ifdef CONFIG_PM
+static int alc_resume(struct hda_codec *codec)
 {
-	struct alc_spec *spec = codec->spec;
-	int i;
-
-	for (i = 0; i < spec->multi_ios; i++) {
-		hda_nid_t pin = spec->multi_io[i].pin;
-		struct nid_path *path;
-		path = get_nid_path(codec, spec->multi_io[i].dac, pin);
-		if (!path)
-			continue;
-		if (!spec->multi_io[i].ctl_in)
-			spec->multi_io[i].ctl_in =
-				snd_hda_codec_update_cache(codec, pin, 0,
-					   AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-		activate_path(codec, path, path->active, true);
-	}
+	msleep(150); /* to avoid pop noise */
+	codec->patch_ops.init(codec);
+	snd_hda_codec_resume_amp(codec);
+	snd_hda_codec_resume_cache(codec);
+	alc_inv_dmic_sync(codec, true);
+	hda_call_check_power_status(codec, 0x01);
+	return 0;
 }
+#endif
 
 /*
- * initialize ADC paths
  */
-static void alc_auto_init_input_src(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	struct hda_input_mux *imux = &spec->input_mux;
-	struct nid_path *path;
-	int i, c, nums;
-
-	if (spec->dyn_adc_switch)
-		nums = 1;
-	else
-		nums = spec->num_adc_nids;
-
-	for (c = 0; c < nums; c++) {
-		for (i = 0; i < imux->num_items; i++) {
-			path = get_nid_path(codec, spec->imux_pins[i],
-					    get_adc_nid(codec, c, i));
-			if (path) {
-				bool active = path->active;
-				if (i == spec->cur_mux[c])
-					active = true;
-				activate_path(codec, path, active, false);
-			}
-		}
-	}
+static const struct hda_codec_ops alc_patch_ops = {
+	.build_controls = alc_build_controls,
+	.build_pcms = snd_hda_gen_build_pcms,
+	.init = alc_init,
+	.free = alc_free,
+	.unsol_event = snd_hda_jack_unsol_event,
+#ifdef CONFIG_PM
+	.resume = alc_resume,
+#endif
+#ifdef CONFIG_PM
+	.suspend = alc_suspend,
+	.check_power_status = alc_check_power_status,
+#endif
+	.reboot_notify = alc_shutup,
+};
 
-	alc_inv_dmic_sync(codec, true);
-	if (spec->shared_mic_hp)
-		update_shared_mic_hp(codec, spec->cur_mux[0]);
-}
 
-/* add mic boosts if needed */
-static int alc_auto_add_mic_boost(struct hda_codec *codec)
+/* replace the codec chip_name with the given string */
+static int alc_codec_rename(struct hda_codec *codec, const char *name)
 {
-	struct alc_spec *spec = codec->spec;
-	struct auto_pin_cfg *cfg = &spec->autocfg;
-	int i, err;
-	int type_idx = 0;
-	hda_nid_t nid;
-	const char *prev_label = NULL;
-
-	for (i = 0; i < cfg->num_inputs; i++) {
-		if (cfg->inputs[i].type > AUTO_PIN_MIC)
-			break;
-		nid = cfg->inputs[i].pin;
-		if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) {
-			const char *label;
-			char boost_label[32];
-			struct nid_path *path;
-			unsigned int val;
-
-			label = hda_get_autocfg_input_label(codec, cfg, i);
-			if (spec->shared_mic_hp && !strcmp(label, "Misc"))
-				label = "Headphone Mic";
-			if (prev_label && !strcmp(label, prev_label))
-				type_idx++;
-			else
-				type_idx = 0;
-			prev_label = label;
-
-			snprintf(boost_label, sizeof(boost_label),
-				 "%s Boost Volume", label);
-			val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT);
-			err = add_control(spec, ALC_CTL_WIDGET_VOL,
-					  boost_label, type_idx, val);
-			if (err < 0)
-				return err;
-
-			path = get_nid_path(codec, nid, 0);
-			if (path)
-				path->ctls[NID_PATH_BOOST_CTL] = val;
-		}
+	kfree(codec->chip_name);
+	codec->chip_name = kstrdup(name, GFP_KERNEL);
+	if (!codec->chip_name) {
+		alc_free(codec);
+		return -ENOMEM;
 	}
 	return 0;
 }
 
 /*
- * standard auto-parser initializations
+ * Rename codecs appropriately from COEF value
  */
-static void alc_auto_init_std(struct hda_codec *codec)
+struct alc_codec_rename_table {
+	unsigned int vendor_id;
+	unsigned short coef_mask;
+	unsigned short coef_bits;
+	const char *name;
+};
+
+static struct alc_codec_rename_table rename_tbl[] = {
+	{ 0x10ec0269, 0xfff0, 0x3010, "ALC277" },
+	{ 0x10ec0269, 0xf0f0, 0x2010, "ALC259" },
+	{ 0x10ec0269, 0xf0f0, 0x3010, "ALC258" },
+	{ 0x10ec0269, 0x00f0, 0x0010, "ALC269VB" },
+	{ 0x10ec0269, 0xffff, 0xa023, "ALC259" },
+	{ 0x10ec0269, 0xffff, 0x6023, "ALC281X" },
+	{ 0x10ec0269, 0x00f0, 0x0020, "ALC269VC" },
+	{ 0x10ec0269, 0x00f0, 0x0030, "ALC269VD" },
+	{ 0x10ec0887, 0x00f0, 0x0030, "ALC887-VD" },
+	{ 0x10ec0888, 0x00f0, 0x0030, "ALC888-VD" },
+	{ 0x10ec0888, 0xf0f0, 0x3020, "ALC886" },
+	{ 0x10ec0899, 0x2000, 0x2000, "ALC899" },
+	{ 0x10ec0892, 0xffff, 0x8020, "ALC661" },
+	{ 0x10ec0892, 0xffff, 0x8011, "ALC661" },
+	{ 0x10ec0892, 0xffff, 0x4011, "ALC656" },
+	{ } /* terminator */
+};
+
+static int alc_codec_rename_from_preset(struct hda_codec *codec)
 {
-	alc_auto_init_multi_out(codec);
-	alc_auto_init_extra_out(codec);
-	alc_auto_init_multi_io(codec);
-	alc_auto_init_analog_input(codec);
-	alc_auto_init_input_src(codec);
-	alc_auto_init_digital(codec);
-	/* call init functions of standard auto-mute helpers */
-	alc_hp_automute(codec, NULL);
-	alc_line_automute(codec, NULL);
-	alc_mic_automute(codec, NULL);
+	const struct alc_codec_rename_table *p;
+
+	for (p = rename_tbl; p->vendor_id; p++) {
+		if (p->vendor_id != codec->vendor_id)
+			continue;
+		if ((alc_get_coef0(codec) & p->coef_mask) == p->coef_bits)
+			return alc_codec_rename(codec, p->name);
+	}
+	return 0;
 }
 
+
 /*
  * Digital-beep handlers
  */
@@ -4436,102 +978,20 @@ static int alc_parse_auto_config(struct hda_codec *codec,
 				 const hda_nid_t *ssid_nids)
 {
 	struct alc_spec *spec = codec->spec;
-	struct auto_pin_cfg *cfg = &spec->autocfg;
+	struct auto_pin_cfg *cfg = &spec->gen.autocfg;
 	int err;
 
 	err = snd_hda_parse_pin_defcfg(codec, cfg, ignore_nids,
 				       spec->parse_flags);
 	if (err < 0)
 		return err;
-	if (!cfg->line_outs) {
-		if (cfg->dig_outs || cfg->dig_in_pin) {
-			spec->multiout.max_channels = 2;
-			spec->no_analog = 1;
-			goto dig_only;
-		}
-		return 0; /* can't find valid BIOS pin config */
-	}
-
-	if (!spec->no_primary_hp &&
-	    cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
-	    cfg->line_outs <= cfg->hp_outs) {
-		/* use HP as primary out */
-		cfg->speaker_outs = cfg->line_outs;
-		memcpy(cfg->speaker_pins, cfg->line_out_pins,
-		       sizeof(cfg->speaker_pins));
-		cfg->line_outs = cfg->hp_outs;
-		memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins));
-		cfg->hp_outs = 0;
-		memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
-		cfg->line_out_type = AUTO_PIN_HP_OUT;
-	}
-
-	err = alc_auto_fill_dac_nids(codec);
-	if (err < 0)
-		return err;
-	err = alc_auto_add_multi_channel_mode(codec);
-	if (err < 0)
-		return err;
-	err = alc_auto_create_multi_out_ctls(codec, cfg);
-	if (err < 0)
-		return err;
-	err = alc_auto_create_hp_out(codec);
-	if (err < 0)
-		return err;
-	err = alc_auto_create_speaker_out(codec);
-	if (err < 0)
-		return err;
-	err = alc_auto_create_shared_input(codec);
-	if (err < 0)
-		return err;
-	err = alc_auto_create_input_ctls(codec);
-	if (err < 0)
-		return err;
-
-	/* check the multiple speaker pins */
-	if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
-		spec->const_channel_count = cfg->line_outs * 2;
-	else
-		spec->const_channel_count = cfg->speaker_outs * 2;
-
-	if (spec->multi_ios > 0)
-		spec->multiout.max_channels = max(spec->ext_channel_count,
-						  spec->const_channel_count);
-	else
-		spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- dig_only:
-	alc_auto_parse_digital(codec);
 
 	if (ssid_nids)
 		alc_ssid_check(codec, ssid_nids);
 
-	if (!spec->no_analog) {
-		err = alc_init_automute(codec);
-		if (err < 0)
-			return err;
-
-		err = check_dyn_adc_switch(codec);
-		if (err < 0)
-			return err;
-
-		if (!spec->shared_mic_hp) {
-			err = alc_init_auto_mic(codec);
-			if (err < 0)
-			return err;
-		}
-
-		err = create_capture_mixers(codec);
-		if (err < 0)
-			return err;
-
-		err = alc_auto_add_mic_boost(codec);
-		if (err < 0)
-			return err;
-	}
-
-	if (spec->kctls.list)
-		add_mixer(spec, spec->kctls.list);
+	err = snd_hda_gen_parse_auto_config(codec, cfg);
+	if (err < 0)
+		return err;
 
 	return 1;
 }
@@ -4545,11 +1005,12 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid)
 	if (!spec)
 		return -ENOMEM;
 	codec->spec = spec;
+	snd_hda_gen_spec_init(&spec->gen);
+	spec->gen.mixer_nid = mixer_nid;
+	spec->gen.own_eapd_ctl = 1;
 	codec->single_adc_amp = 1;
-	spec->mixer_nid = mixer_nid;
-	snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32);
-	snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8);
-	snd_array_init(&spec->paths, sizeof(struct nid_path), 8);
+	/* FIXME: do we need this for all Realtek codec models? */
+	codec->spdif_status_reset = 1;
 
 	err = alc_codec_rename_from_preset(codec);
 	if (err < 0) {
@@ -4945,7 +1406,7 @@ static int patch_alc880(struct hda_codec *codec)
 		return err;
 
 	spec = codec->spec;
-	spec->need_dac_fix = 1;
+	spec->gen.need_dac_fix = 1;
 
 	alc_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl,
 		       alc880_fixups);
@@ -4956,7 +1417,7 @@ static int patch_alc880(struct hda_codec *codec)
 	if (err < 0)
 		goto error;
 
-	if (!spec->no_analog) {
+	if (!spec->gen.no_analog) {
 		err = snd_hda_attach_beep_device(codec, 0x1);
 		if (err < 0)
 			goto error;
@@ -5005,7 +1466,7 @@ static void alc260_gpio1_automute(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
 	snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
-			    spec->hp_jack_present);
+			    spec->gen.hp_jack_present);
 }
 
 static void alc260_fixup_gpio1_toggle(struct hda_codec *codec,
@@ -5016,12 +1477,12 @@ static void alc260_fixup_gpio1_toggle(struct hda_codec *codec,
 		/* although the machine has only one output pin, we need to
 		 * toggle GPIO1 according to the jack state
 		 */
-		spec->automute_hook = alc260_gpio1_automute;
-		spec->detect_hp = 1;
-		spec->automute_speaker = 1;
-		spec->autocfg.hp_pins[0] = 0x0f; /* copy it for automute */
-		snd_hda_jack_detect_enable_callback(codec, 0x0f, ALC_HP_EVENT,
-						    alc_hp_automute);
+		spec->gen.automute_hook = alc260_gpio1_automute;
+		spec->gen.detect_hp = 1;
+		spec->gen.automute_speaker = 1;
+		spec->gen.autocfg.hp_pins[0] = 0x0f; /* copy it for automute */
+		snd_hda_jack_detect_enable_callback(codec, 0x0f, HDA_GEN_HP_EVENT,
+						    snd_hda_gen_hp_automute);
 		snd_hda_add_verbs(codec, alc_gpio1_init_verbs);
 	}
 }
@@ -5147,7 +1608,7 @@ static int patch_alc260(struct hda_codec *codec)
 	if (err < 0)
 		goto error;
 
-	if (!spec->no_analog) {
+	if (!spec->gen.no_analog) {
 		err = snd_hda_attach_beep_device(codec, 0x1);
 		if (err < 0)
 			goto error;
@@ -5304,7 +1765,7 @@ static void alc889_fixup_mbp_vref(struct hda_codec *codec,
 					 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
 		val |= AC_PINCTL_VREF_80;
 		snd_hda_set_pin_ctl(codec, nids[i], val);
-		spec->keep_vref_in_automute = 1;
+		spec->gen.keep_vref_in_automute = 1;
 		break;
 	}
 }
@@ -5326,7 +1787,7 @@ static void alc889_fixup_imac91_vref(struct hda_codec *codec,
 		val |= AC_PINCTL_VREF_50;
 		snd_hda_set_pin_ctl(codec, nids[i], val);
 	}
-	spec->keep_vref_in_automute = 1;
+	spec->gen.keep_vref_in_automute = 1;
 }
 
 /* Don't take HP output as primary
@@ -5337,7 +1798,7 @@ static void alc882_fixup_no_primary_hp(struct hda_codec *codec,
 {
 	struct alc_spec *spec = codec->spec;
 	if (action == ALC_FIXUP_ACT_PRE_PROBE)
-		spec->no_primary_hp = 1;
+		spec->gen.no_primary_hp = 1;
 }
 
 static const struct alc_fixup alc882_fixups[] = {
@@ -5656,7 +2117,7 @@ static int patch_alc882(struct hda_codec *codec)
 	if (err < 0)
 		goto error;
 
-	if (!spec->no_analog && has_cdefine_beep(codec)) {
+	if (!spec->gen.no_analog && has_cdefine_beep(codec)) {
 		err = snd_hda_attach_beep_device(codec, 0x1);
 		if (err < 0)
 			goto error;
@@ -5782,7 +2243,7 @@ static int patch_alc262(struct hda_codec *codec)
 		return err;
 
 	spec = codec->spec;
-	spec->shared_mic_vref_pin = 0x18;
+	spec->gen.shared_mic_vref_pin = 0x18;
 
 #if 0
 	/* pshou 07/11/05  set a zero PCM sample to DAC when FIFO is
@@ -5809,7 +2270,7 @@ static int patch_alc262(struct hda_codec *codec)
 	if (err < 0)
 		goto error;
 
-	if (!spec->no_analog && has_cdefine_beep(codec)) {
+	if (!spec->gen.no_analog && has_cdefine_beep(codec)) {
 		err = snd_hda_attach_beep_device(codec, 0x1);
 		if (err < 0)
 			goto error;
@@ -5897,7 +2358,8 @@ static int alc268_parse_auto_config(struct hda_codec *codec)
 	struct alc_spec *spec = codec->spec;
 	int err = alc_parse_auto_config(codec, NULL, alc268_ssids);
 	if (err > 0) {
-		if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d) {
+		if (!spec->gen.no_analog &&
+		    spec->gen.autocfg.speaker_pins[0] != 0x1d) {
 			add_mixer(spec, alc268_beep_mixer);
 			snd_hda_add_verbs(codec, alc268_beep_init_verbs);
 		}
@@ -5963,6 +2425,35 @@ static int patch_alc268(struct hda_codec *codec)
 /*
  * ALC269
  */
+
+static int playback_pcm_open(struct hda_pcm_stream *hinfo,
+			     struct hda_codec *codec,
+			     struct snd_pcm_substream *substream)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+					     hinfo);
+}
+
+static int playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+				struct hda_codec *codec,
+				unsigned int stream_tag,
+				unsigned int format,
+				struct snd_pcm_substream *substream)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
+						stream_tag, format, substream);
+}
+
+static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+				struct hda_codec *codec,
+				struct snd_pcm_substream *substream)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+}
+
 static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = {
 	.substreams = 1,
 	.channels_min = 2,
@@ -5970,9 +2461,9 @@ static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = {
 	.rates = SNDRV_PCM_RATE_44100, /* fixed rate */
 	/* NID is set in alc_build_pcms */
 	.ops = {
-		.open = alc_playback_pcm_open,
-		.prepare = alc_playback_pcm_prepare,
-		.cleanup = alc_playback_pcm_cleanup
+		.open = playback_pcm_open,
+		.prepare = playback_pcm_prepare,
+		.cleanup = playback_pcm_cleanup
 	},
 };
 
@@ -6121,8 +2612,8 @@ static void alc269_fixup_pcm_44k(struct hda_codec *codec,
 	/* Due to a hardware problem on Lenovo Ideadpad, we need to
 	 * fix the sample rate of analog I/O to 44.1kHz
 	 */
-	spec->stream_analog_playback = &alc269_44k_pcm_analog_playback;
-	spec->stream_analog_capture = &alc269_44k_pcm_analog_capture;
+	spec->gen.stream_analog_playback = &alc269_44k_pcm_analog_playback;
+	spec->gen.stream_analog_capture = &alc269_44k_pcm_analog_capture;
 }
 
 static void alc269_fixup_stereo_dmic(struct hda_codec *codec,
@@ -6143,7 +2634,7 @@ static void alc269_fixup_stereo_dmic(struct hda_codec *codec,
 
 static void alc269_quanta_automute(struct hda_codec *codec)
 {
-	update_outputs(codec);
+	snd_hda_gen_update_outputs(codec);
 
 	snd_hda_codec_write(codec, 0x20, 0,
 			AC_VERB_SET_COEF_INDEX, 0x0c);
@@ -6162,7 +2653,7 @@ static void alc269_fixup_quanta_mute(struct hda_codec *codec,
 	struct alc_spec *spec = codec->spec;
 	if (action != ALC_FIXUP_ACT_PROBE)
 		return;
-	spec->automute_hook = alc269_quanta_automute;
+	spec->gen.automute_hook = alc269_quanta_automute;
 }
 
 /* update mute-LED according to the speaker mute state via mic1 VREF pin */
@@ -6179,7 +2670,7 @@ static void alc269_fixup_mic1_mute(struct hda_codec *codec,
 {
 	struct alc_spec *spec = codec->spec;
 	if (action == ALC_FIXUP_ACT_PROBE)
-		spec->vmaster_mute.hook = alc269_fixup_mic1_mute_hook;
+		spec->gen.vmaster_mute.hook = alc269_fixup_mic1_mute_hook;
 }
 
 /* update mute-LED according to the speaker mute state via mic2 VREF pin */
@@ -6195,7 +2686,7 @@ static void alc269_fixup_mic2_mute(struct hda_codec *codec,
 {
 	struct alc_spec *spec = codec->spec;
 	if (action == ALC_FIXUP_ACT_PROBE)
-		spec->vmaster_mute.hook = alc269_fixup_mic2_mute_hook;
+		spec->gen.vmaster_mute.hook = alc269_fixup_mic2_mute_hook;
 }
 
 static void alc271_hp_gate_mic_jack(struct hda_codec *codec,
@@ -6204,11 +2695,12 @@ static void alc271_hp_gate_mic_jack(struct hda_codec *codec,
 {
 	struct alc_spec *spec = codec->spec;
 
-	if (snd_BUG_ON(!spec->am_entry[1].pin || !spec->autocfg.hp_pins[0]))
+	if (snd_BUG_ON(!spec->gen.am_entry[1].pin ||
+		       !spec->gen.autocfg.hp_pins[0]))
 		return;
 	if (action == ALC_FIXUP_ACT_PROBE)
-		snd_hda_jack_set_gating_jack(codec, spec->am_entry[1].pin,
-					     spec->autocfg.hp_pins[0]);
+		snd_hda_jack_set_gating_jack(codec, spec->gen.am_entry[1].pin,
+					     spec->gen.autocfg.hp_pins[0]);
 }
 
 enum {
@@ -6554,7 +3046,7 @@ static int patch_alc269(struct hda_codec *codec)
 		return err;
 
 	spec = codec->spec;
-	spec->shared_mic_vref_pin = 0x18;
+	spec->gen.shared_mic_vref_pin = 0x18;
 
 	alc_pick_fixup(codec, alc269_fixup_models,
 		       alc269_fixup_tbl, alc269_fixups);
@@ -6594,7 +3086,7 @@ static int patch_alc269(struct hda_codec *codec)
 	if (err < 0)
 		goto error;
 
-	if (!spec->no_analog && has_cdefine_beep(codec)) {
+	if (!spec->gen.no_analog && has_cdefine_beep(codec)) {
 		err = snd_hda_attach_beep_device(codec, 0x1);
 		if (err < 0)
 			goto error;
@@ -6650,7 +3142,7 @@ static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec,
 		val |= AC_PINCTL_IN_EN;
 	val |= AC_PINCTL_VREF_50;
 	snd_hda_set_pin_ctl(codec, 0x0f, val);
-	spec->keep_vref_in_automute = 1;
+	spec->gen.keep_vref_in_automute = 1;
 }
 
 /* suppress the jack-detection */
@@ -6717,7 +3209,7 @@ static int patch_alc861(struct hda_codec *codec)
 	if (err < 0)
 		goto error;
 
-	if (!spec->no_analog) {
+	if (!spec->gen.no_analog) {
 		err = snd_hda_attach_beep_device(codec, 0x23);
 		if (err < 0)
 			goto error;
@@ -6812,7 +3304,7 @@ static int patch_alc861vd(struct hda_codec *codec)
 	if (err < 0)
 		goto error;
 
-	if (!spec->no_analog) {
+	if (!spec->gen.no_analog) {
 		err = snd_hda_attach_beep_device(codec, 0x23);
 		if (err < 0)
 			goto error;
@@ -7211,7 +3703,7 @@ static int patch_alc662(struct hda_codec *codec)
 	if (err < 0)
 		goto error;
 
-	if (!spec->no_analog && has_cdefine_beep(codec)) {
+	if (!spec->gen.no_analog && has_cdefine_beep(codec)) {
 		err = snd_hda_attach_beep_device(codec, 0x1);
 		if (err < 0)
 			goto error;
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 053/112] ALSA: hda - Use "Capture Source" for single sources
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (51 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 052/112] ALSA: hda - Use generic parser codes for Realtek driver Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 054/112] ALSA: hda - Allow one chance for zero NID in connection list Takashi Iwai
                   ` (61 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

In general we prefer "Capture Source" to "Input Source".
The latter was chosen in many places just because "Capture Source"
label doesn't work well with the current alsa-lib mixer abstraction
when multiple instances are present.  But when we know that there is a
single input-source element, we can safely choose "Capture Source"
label.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 6914d70..f8d1d03 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -2172,7 +2172,9 @@ static int create_capture_mixers(struct hda_codec *codec)
 
 	if (!spec->auto_mic && imux->num_items > 1) {
 		struct snd_kcontrol_new *knew;
-		knew = snd_hda_gen_add_kctl(spec, NULL, &cap_src_temp);
+		const char *name;
+		name = nums > 1 ? "Input Source" : "Capture Source";
+		knew = snd_hda_gen_add_kctl(spec, name, &cap_src_temp);
 		if (!knew)
 			return -ENOMEM;
 		knew->count = nums;
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 054/112] ALSA: hda - Allow one chance for zero NID in connection list
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (52 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 053/112] ALSA: hda - Use "Capture Source" for single sources Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 055/112] ALSA: hda - Clear dirty flag upon cache write Takashi Iwai
                   ` (60 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

The commit [2e9bf24: ALSA: hda_codec: Check for invalid zero
connections] trims the whole connection list when an invalid value is
reported by the hardware.  But some codecs (at least AD1986A) may give
a zero NID in the middle of the connection list, so dropping the whole
list isn't good for such cases.

In this patch, as a workaround, allow a single zero NID in the read
connection list.  If it hits zero twice, it's handled as an error, so
that we can avoid "too many connections" errors.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_codec.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 12b88a2..650498b 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -424,6 +424,7 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
 	unsigned int shift, num_elems, mask;
 	unsigned int wcaps;
 	hda_nid_t prev_nid;
+	int null_count = 0;
 
 	if (snd_BUG_ON(!conn_list || max_conns <= 0))
 		return -EINVAL;
@@ -474,7 +475,7 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
 		}
 		range_val = !!(parm & (1 << (shift-1))); /* ranges */
 		val = parm & mask;
-		if (val == 0) {
+		if (val == 0 && null_count++) {  /* no second chance */
 			snd_printk(KERN_WARNING "hda_codec: "
 				   "invalid CONNECT_LIST verb %x[%i]:%x\n",
 				    nid, i, parm);
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 055/112] ALSA: hda - Clear dirty flag upon cache write
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (53 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 054/112] ALSA: hda - Allow one chance for zero NID in connection list Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 056/112] ALSA: hda - Clear cached_write flag in snd_hda_codec_resume_*() Takashi Iwai
                   ` (59 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

When verbs or amps are actually written to the hardware, we can clear
dirty flag so that the later snd_hda_codec_resume_*() calls can skip
these verbs / amps.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_codec.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 650498b..44b525f 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1848,6 +1848,7 @@ static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
 			    bool init_only)
 {
 	struct hda_amp_info *info;
+	unsigned int cache_only;
 
 	if (snd_BUG_ON(mask & ~0xff))
 		mask &= 0xff;
@@ -1865,10 +1866,9 @@ static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
 		return 0;
 	}
 	info->vol[ch] = val;
-	if (codec->cached_write)
-		info->head.dirty = 1;
+	cache_only = info->head.dirty = codec->cached_write;
 	mutex_unlock(&codec->hash_mutex);
-	if (!codec->cached_write)
+	if (!cache_only)
 		put_vol_mute(codec, info, nid, ch, direction, idx, val);
 	return 1;
 }
@@ -3450,8 +3450,10 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
 	int err;
 	struct hda_cache_head *c;
 	u32 key;
+	unsigned int cache_only;
 
-	if (!codec->cached_write) {
+	cache_only = codec->cached_write;
+	if (!cache_only) {
 		err = snd_hda_codec_write(codec, nid, direct, verb, parm);
 		if (err < 0)
 			return err;
@@ -3465,8 +3467,7 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
 	c = get_alloc_hash(&codec->cmd_cache, key);
 	if (c)
 		c->val = parm;
-	if (codec->cached_write)
-		c->dirty = 1;
+	c->dirty = cache_only;
 	mutex_unlock(&codec->bus->cmd_mutex);
 	return 0;
 }
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 056/112] ALSA: hda - Clear cached_write flag in snd_hda_codec_resume_*()
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (54 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 055/112] ALSA: hda - Clear dirty flag upon cache write Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 057/112] ALSA: hda - Check CORB overflow Takashi Iwai
                   ` (58 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

These functions are supposed to be called at finishing the cached
sequential writes, so clear the flag properly for lazy developers who
often forget details.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_codec.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 44b525f..df4abbe 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1955,6 +1955,7 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec)
 	int i;
 
 	mutex_lock(&codec->hash_mutex);
+	codec->cached_write = 0;
 	for (i = 0; i < codec->amp_cache.buf.used; i++) {
 		struct hda_amp_info *buffer;
 		u32 key;
@@ -3519,6 +3520,7 @@ void snd_hda_codec_resume_cache(struct hda_codec *codec)
 	int i;
 
 	mutex_lock(&codec->hash_mutex);
+	codec->cached_write = 0;
 	for (i = 0; i < codec->cmd_cache.buf.used; i++) {
 		struct hda_cache_head *buffer;
 		u32 key;
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 057/112] ALSA: hda - Check CORB overflow
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (55 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 056/112] ALSA: hda - Clear cached_write flag in snd_hda_codec_resume_*() Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 058/112] ALSA: hda - Flush dirty amp caches before writing inv_dmic fix Takashi Iwai
                   ` (57 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

Add an overflow check of CORB in HD-audio controller and codec drivers
so that flood of sequential writes would work properly.
In the controller side, add a check of CORB read-pointer to make
returning -EAGAIN when it's full.  Meanwhile in the codec side, when
-EAGAIN error is received, it retries the write after flushing the
pending verbs (calling get_response() essentially does it).

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_codec.c | 10 ++++++++--
 sound/pci/hda/hda_intel.c | 11 +++++++++--
 2 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index df4abbe..f362ef7 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -222,8 +222,14 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
  again:
 	snd_hda_power_up(codec);
 	mutex_lock(&bus->cmd_mutex);
-	trace_hda_send_cmd(codec, cmd);
-	err = bus->ops.command(bus, cmd);
+	for (;;) {
+		trace_hda_send_cmd(codec, cmd);
+		err = bus->ops.command(bus, cmd);
+		if (err != -EAGAIN)
+			break;
+		/* process pending verbs */
+		bus->ops.get_response(bus, codec->addr);
+	}
 	if (!err && res) {
 		*res = bus->ops.get_response(bus, codec->addr);
 		trace_hda_get_response(codec, *res);
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index cca8727..d0f42d1 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -794,7 +794,7 @@ static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
 {
 	struct azx *chip = bus->private_data;
 	unsigned int addr = azx_command_addr(val);
-	unsigned int wp;
+	unsigned int wp, rp;
 
 	spin_lock_irq(&chip->reg_lock);
 
@@ -803,11 +803,18 @@ static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
 	if (wp == 0xffff) {
 		/* something wrong, controller likely turned to D3 */
 		spin_unlock_irq(&chip->reg_lock);
-		return -1;
+		return -EIO;
 	}
 	wp++;
 	wp %= ICH6_MAX_CORB_ENTRIES;
 
+	rp = azx_readw(chip, CORBRP);
+	if (wp == rp) {
+		/* oops, it's full */
+		spin_unlock_irq(&chip->reg_lock);
+		return -EAGAIN;
+	}
+
 	chip->rirb.cmds[addr]++;
 	chip->corb.buf[wp] = cpu_to_le32(val);
 	azx_writel(chip, CORBWP, wp);
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 058/112] ALSA: hda - Flush dirty amp caches before writing inv_dmic fix
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (56 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 057/112] ALSA: hda - Check CORB overflow Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 059/112] ALSA: hda - Add snd_hda_codec_flush_*_cache() aliases Takashi Iwai
                   ` (56 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

The inverted dmic fix overwrites the right channel amp value, but it
would work only when the amp values have been already actually
written.  Put snd_hda_codec_resume_amp() before the amp write for
flushing caches.

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

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 17da84d..20e657b 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -633,6 +633,9 @@ static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx)
 	parm = AC_AMP_SET_RIGHT |
 		(dir == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT);
 
+	/* flush all cached amps at first */
+	snd_hda_codec_resume_amp(codec);
+
 	/* we care only right channel */
 	val = snd_hda_codec_amp_read(codec, nid, 1, dir, 0);
 	if (val & 0x80) /* if already muted, we don't need to touch */
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 059/112] ALSA: hda - Add snd_hda_codec_flush_*_cache() aliases
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (57 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 058/112] ALSA: hda - Flush dirty amp caches before writing inv_dmic fix Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 060/112] ALSA: hda - Add missing amp cache flush for bound capture vol/sw ctls Takashi Iwai
                   ` (55 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

It makes easier to understand although these are identical with
snd_hda_codec_resume_*() functions.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_codec.h     | 4 ++++
 sound/pci/hda/hda_local.h     | 4 ++++
 sound/pci/hda/patch_realtek.c | 2 +-
 3 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index a1cb28f..2d9a51c 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -970,6 +970,10 @@ int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid,
 			      int direct, unsigned int verb, unsigned int parm);
 void snd_hda_codec_resume_cache(struct hda_codec *codec);
 
+/* it's alias but a bit clearer meaning */
+#define snd_hda_codec_flush_cmd_cache(codec) \
+	snd_hda_codec_resume_cache(codec)
+
 /* the struct for codec->pin_configs */
 struct hda_pincfg {
 	hda_nid_t nid;
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index de12dcc..fec0e2d 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -139,6 +139,10 @@ int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid,
 				  int dir, int idx, int mask, int val);
 void snd_hda_codec_resume_amp(struct hda_codec *codec);
 
+/* it's alias but a bit clearer meaning */
+#define snd_hda_codec_flush_amp_cache(codec) \
+	snd_hda_codec_resume_amp(codec)
+
 void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
 			     unsigned int *tlv);
 struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 20e657b..0b32729 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -634,7 +634,7 @@ static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx)
 		(dir == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT);
 
 	/* flush all cached amps at first */
-	snd_hda_codec_resume_amp(codec);
+	snd_hda_codec_flush_amp_cache(codec);
 
 	/* we care only right channel */
 	val = snd_hda_codec_amp_read(codec, nid, 1, dir, 0);
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 060/112] ALSA: hda - Add missing amp cache flush for bound capture vol/sw ctls
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (58 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 059/112] ALSA: hda - Add snd_hda_codec_flush_*_cache() aliases Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 061/112] ALSA: hda - Add / fix comments about capture vol/sw controls in hda_generic.c Takashi Iwai
                   ` (54 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

The bound capture volume and switch controls use the cached amp
updates, but it's missing the flushing at the end.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index f8d1d03..3e818b6 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -1919,6 +1919,7 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol,
  error:
 	codec->cached_write = 0;
 	mutex_unlock(&codec->control_mutex);
+	snd_hda_codec_flush_amp_cache(codec);
 	if (err >= 0 && spec->cap_sync_hook)
 		spec->cap_sync_hook(codec);
 	return err;
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 061/112] ALSA: hda - Add / fix comments about capture vol/sw controls in hda_generic.c
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (59 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 060/112] ALSA: hda - Add missing amp cache flush for bound capture vol/sw ctls Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 062/112] ALSA: hda - Do sequential writes in snd_hda_gen_init() Takashi Iwai
                   ` (53 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

A bit of details won't hurt.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 3e818b6..f5eb57c 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -1877,10 +1877,6 @@ static int mux_enum_put(struct snd_kcontrol *kcontrol,
 			  ucontrol->value.enumerated.item[0]);
 }
 
-/*
- * capture volume and capture switch ctls
- */
-
 static const struct snd_kcontrol_new cap_src_temp = {
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	.name = "Input Source",
@@ -1889,9 +1885,14 @@ static const struct snd_kcontrol_new cap_src_temp = {
 	.put = mux_enum_put,
 };
 
+/*
+ * capture volume and capture switch ctls
+ */
+
 typedef int (*put_call_t)(struct snd_kcontrol *kcontrol,
 			  struct snd_ctl_elem_value *ucontrol);
 
+/* call the given amp update function for all amps in the imux list at once */
 static int cap_put_caller(struct snd_kcontrol *kcontrol,
 			  struct snd_ctl_elem_value *ucontrol,
 			  put_call_t func, int type)
@@ -1905,6 +1906,10 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol,
 	imux = &spec->input_mux;
 	adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	mutex_lock(&codec->control_mutex);
+	/* we use the cache-only update at first since multiple input paths
+	 * may shared the same amp; by updating only caches, the redundant
+	 * writes to hardware can be reduced.
+	 */
 	codec->cached_write = 1;
 	for (i = 0; i < imux->num_items; i++) {
 		path = snd_hda_get_nid_path(codec, spec->imux_pins[i],
@@ -1919,7 +1924,7 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol,
  error:
 	codec->cached_write = 0;
 	mutex_unlock(&codec->control_mutex);
-	snd_hda_codec_flush_amp_cache(codec);
+	snd_hda_codec_flush_amp_cache(codec); /* flush the updates */
 	if (err >= 0 && spec->cap_sync_hook)
 		spec->cap_sync_hook(codec);
 	return err;
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 062/112] ALSA: hda - Do sequential writes in snd_hda_gen_init()
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (60 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 061/112] ALSA: hda - Add / fix comments about capture vol/sw controls in hda_generic.c Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 063/112] ALSA: hda - Fix wrong dirty check in snd_hda_codec_resume_amp() Takashi Iwai
                   ` (52 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

This would reduce the number of actually executed verbs.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index f5eb57c..6fb454e 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -3540,6 +3540,8 @@ int snd_hda_gen_init(struct hda_codec *codec)
 
 	snd_hda_apply_verbs(codec);
 
+	codec->cached_write = 1;
+
 	init_multi_out(codec);
 	init_extra_out(codec);
 	init_multi_io(codec);
@@ -3552,6 +3554,9 @@ int snd_hda_gen_init(struct hda_codec *codec)
 	snd_hda_gen_line_automute(codec, NULL);
 	snd_hda_gen_mic_autoswitch(codec, NULL);
 
+	snd_hda_codec_flush_amp_cache(codec);
+	snd_hda_codec_flush_cmd_cache(codec);
+
 	if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook)
 		snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
 
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 063/112] ALSA: hda - Fix wrong dirty check in snd_hda_codec_resume_amp()
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (61 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 062/112] ALSA: hda - Do sequential writes in snd_hda_gen_init() Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 064/112] ALSA: hda - Avoid access of amp cache element outside mutex Takashi Iwai
                   ` (51 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

The dirty entry has to be checked at the beginning in the loop, not in
the inner loop for channels.  This caused a regression that the right
channel isn't properly written.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_codec.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index f362ef7..48591ab 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1969,6 +1969,9 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec)
 		unsigned int idx, dir, ch;
 
 		buffer = snd_array_elem(&codec->amp_cache.buf, i);
+		if (!buffer->head.dirty)
+			continue;
+		buffer->head.dirty = 0;
 		key = buffer->head.key;
 		if (!key)
 			continue;
@@ -1978,9 +1981,6 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec)
 		for (ch = 0; ch < 2; ch++) {
 			if (!(buffer->head.val & INFO_AMP_VOL(ch)))
 				continue;
-			if (!buffer->head.dirty)
-				continue;
-			buffer->head.dirty = 0;
 			mutex_unlock(&codec->hash_mutex);
 			put_vol_mute(codec, buffer, nid, ch, dir, idx,
 				     buffer->vol[ch]);
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 064/112] ALSA: hda - Avoid access of amp cache element outside mutex
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (62 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 063/112] ALSA: hda - Fix wrong dirty check in snd_hda_codec_resume_amp() Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 065/112] ALSA: hda - Increase the max depth of widget connections Takashi Iwai
                   ` (50 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

The access to a cache array element could be invalid outside the
mutex, so copy locally for the later references.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_codec.c | 20 ++++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)

diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 48591ab..9eb73b0 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1807,7 +1807,7 @@ update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch,
 /*
  * write the current volume in info to the h/w
  */
-static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
+static void put_vol_mute(struct hda_codec *codec, unsigned int amp_caps,
 			 hda_nid_t nid, int ch, int direction, int index,
 			 int val)
 {
@@ -1816,8 +1816,8 @@ static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
 	parm = ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT;
 	parm |= direction == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT;
 	parm |= index << AC_AMP_SET_INDEX_SHIFT;
-	if ((val & HDA_AMP_MUTE) && !(info->amp_caps & AC_AMPCAP_MUTE) &&
-	    (info->amp_caps & AC_AMPCAP_MIN_MUTE))
+	if ((val & HDA_AMP_MUTE) && !(amp_caps & AC_AMPCAP_MUTE) &&
+	    (amp_caps & AC_AMPCAP_MIN_MUTE))
 		; /* set the zero value as a fake mute */
 	else
 		parm |= val;
@@ -1854,6 +1854,7 @@ static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
 			    bool init_only)
 {
 	struct hda_amp_info *info;
+	unsigned int caps;
 	unsigned int cache_only;
 
 	if (snd_BUG_ON(mask & ~0xff))
@@ -1873,9 +1874,10 @@ static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
 	}
 	info->vol[ch] = val;
 	cache_only = info->head.dirty = codec->cached_write;
+	caps = info->amp_caps;
 	mutex_unlock(&codec->hash_mutex);
 	if (!cache_only)
-		put_vol_mute(codec, info, nid, ch, direction, idx, val);
+		put_vol_mute(codec, caps, nid, ch, direction, idx, val);
 	return 1;
 }
 
@@ -1967,23 +1969,25 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec)
 		u32 key;
 		hda_nid_t nid;
 		unsigned int idx, dir, ch;
+		struct hda_amp_info info;
 
 		buffer = snd_array_elem(&codec->amp_cache.buf, i);
 		if (!buffer->head.dirty)
 			continue;
 		buffer->head.dirty = 0;
-		key = buffer->head.key;
+		info = *buffer;
+		key = info.head.key;
 		if (!key)
 			continue;
 		nid = key & 0xff;
 		idx = (key >> 16) & 0xff;
 		dir = (key >> 24) & 0xff;
 		for (ch = 0; ch < 2; ch++) {
-			if (!(buffer->head.val & INFO_AMP_VOL(ch)))
+			if (!(info.head.val & INFO_AMP_VOL(ch)))
 				continue;
 			mutex_unlock(&codec->hash_mutex);
-			put_vol_mute(codec, buffer, nid, ch, dir, idx,
-				     buffer->vol[ch]);
+			put_vol_mute(codec, info.amp_caps, nid, ch, dir, idx,
+				     info.vol[ch]);
 			mutex_lock(&codec->hash_mutex);
 		}
 	}
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 065/112] ALSA: hda - Increase the max depth of widget connections
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (63 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 064/112] ALSA: hda - Avoid access of amp cache element outside mutex Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:38 ` [PATCH 066/112] ALSA: hda - Begin HDA_GEN_* event tag from 1 Takashi Iwai
                   ` (49 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

Old codecs like AD1986A tend to have long paths as they were just made
to be the way like AC97.  The current max depth 5 can be too short for
such devices.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_codec.c   | 2 +-
 sound/pci/hda/hda_generic.h | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 9eb73b0..380c9ed 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -591,7 +591,7 @@ int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
 			return i;
 	if (!recursive)
 		return -1;
-	if (recursive > 5) {
+	if (recursive > 10) {
 		snd_printd("hda_codec: too deep connection for 0x%x\n", nid);
 		return -1;
 	}
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index 417ab65..89ad877 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -37,7 +37,7 @@ struct hda_multi_io {
  * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
  */
 
-#define MAX_NID_PATH_DEPTH	5
+#define MAX_NID_PATH_DEPTH	10
 
 enum {
 	NID_PATH_VOL_CTL,
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 066/112] ALSA: hda - Begin HDA_GEN_* event tag from 1
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (64 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 065/112] ALSA: hda - Increase the max depth of widget connections Takashi Iwai
@ 2013-01-08 11:38 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 067/112] ALSA: hda - Add spec->vmaster_mute_enum flag to generic parser Takashi Iwai
                   ` (48 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:38 UTC (permalink / raw)
  To: alsa-devel

... to distinguish from the invalid event type.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index 89ad877..b598899 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -14,7 +14,7 @@
 
 /* unsol event tags */
 enum {
-	HDA_GEN_HP_EVENT, HDA_GEN_FRONT_EVENT, HDA_GEN_MIC_EVENT,
+	HDA_GEN_HP_EVENT = 1, HDA_GEN_FRONT_EVENT, HDA_GEN_MIC_EVENT,
 	HDA_GEN_LAST_EVENT = HDA_GEN_MIC_EVENT
 };
 
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 067/112] ALSA: hda - Add spec->vmaster_mute_enum flag to generic parser
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (65 preceding siblings ...)
  2013-01-08 11:38 ` [PATCH 066/112] ALSA: hda - Begin HDA_GEN_* event tag from 1 Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 068/112] ALSA: hda - Clear unsol enable bits on unused pins in " Takashi Iwai
                   ` (47 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

Add a flag to indicate whether the vmaster mute hook enum is exposed
or not.  Conexant codecs may want not to expose the control depending
on the model.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c   | 3 ++-
 sound/pci/hda/hda_generic.h   | 1 +
 sound/pci/hda/patch_realtek.c | 8 ++++++--
 3 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 6fb454e..a5c4bc0 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -2966,7 +2966,8 @@ int snd_hda_gen_build_controls(struct hda_codec *codec)
 		if (err < 0)
 			return err;
 		if (spec->vmaster_mute.hook)
-			snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true);
+			snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute,
+						 spec->vmaster_mute_enum);
 	}
 
 	free_kctls(spec); /* no longer needed */
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index b598899..a406cd4 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -151,6 +151,7 @@ struct hda_gen_spec {
 	unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */
 	unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */
 	unsigned int own_eapd_ctl:1; /* set EAPD by own function */
+	unsigned int vmaster_mute_enum:1; /* add vmaster mute mode enum */
 
 	/* for virtual master */
 	hda_nid_t vmaster_nid;
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 0b32729..1c343f2 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -2672,8 +2672,10 @@ static void alc269_fixup_mic1_mute(struct hda_codec *codec,
 				   const struct alc_fixup *fix, int action)
 {
 	struct alc_spec *spec = codec->spec;
-	if (action == ALC_FIXUP_ACT_PROBE)
+	if (action == ALC_FIXUP_ACT_PROBE) {
 		spec->gen.vmaster_mute.hook = alc269_fixup_mic1_mute_hook;
+		spec->gen.vmaster_mute_enum = 1;
+	}
 }
 
 /* update mute-LED according to the speaker mute state via mic2 VREF pin */
@@ -2688,8 +2690,10 @@ static void alc269_fixup_mic2_mute(struct hda_codec *codec,
 				   const struct alc_fixup *fix, int action)
 {
 	struct alc_spec *spec = codec->spec;
-	if (action == ALC_FIXUP_ACT_PROBE)
+	if (action == ALC_FIXUP_ACT_PROBE) {
 		spec->gen.vmaster_mute.hook = alc269_fixup_mic2_mute_hook;
+		spec->gen.vmaster_mute_enum = 1;
+	}
 }
 
 static void alc271_hp_gate_mic_jack(struct hda_codec *codec,
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 068/112] ALSA: hda - Clear unsol enable bits on unused pins in generic parser
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (66 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 067/112] ALSA: hda - Add spec->vmaster_mute_enum flag to generic parser Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 069/112] ALSA: hda - Refactor init_extra_out() in hda_generic.c Takashi Iwai
                   ` (46 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

For preliminary works to migrate the generic parser for Conexant
codecs: the same function is ported to hda_generic.c.
But now it looks through the jack detect table so that it can cover
better.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index a5c4bc0..b7b8d7e 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -3532,6 +3532,23 @@ static void init_digital(struct hda_codec *codec)
 		snd_hda_set_pin_ctl(codec, pin, PIN_IN);
 }
 
+/* clear unsol-event tags on unused pins; Conexant codecs seem to leave
+ * invalid unsol tags by some reason
+ */
+static void clear_unsol_on_unused_pins(struct hda_codec *codec)
+{
+	int i;
+
+	for (i = 0; i < codec->init_pins.used; i++) {
+		struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
+		hda_nid_t nid = pin->nid;
+		if (is_jack_detectable(codec, nid) &&
+		    !snd_hda_jack_tbl_get(codec, nid))
+			snd_hda_codec_update_cache(codec, nid, 0,
+					AC_VERB_SET_UNSOLICITED_ENABLE, 0);
+	}
+}
+
 int snd_hda_gen_init(struct hda_codec *codec)
 {
 	struct hda_gen_spec *spec = codec->spec;
@@ -3550,6 +3567,8 @@ int snd_hda_gen_init(struct hda_codec *codec)
 	init_input_src(codec);
 	init_digital(codec);
 
+	clear_unsol_on_unused_pins(codec);
+
 	/* call init functions of standard auto-mute helpers */
 	snd_hda_gen_hp_automute(codec, NULL);
 	snd_hda_gen_line_automute(codec, NULL);
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 069/112] ALSA: hda - Refactor init_extra_out() in hda_generic.c
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (67 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 068/112] ALSA: hda - Clear unsol enable bits on unused pins in " Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 070/112] ALSA: hda - Fix initialization of primary outputs " Takashi Iwai
                   ` (45 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

Just a small clean up by splitting a function.
No functional changes at all.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 49 ++++++++++++++++++++++-----------------------
 1 file changed, 24 insertions(+), 25 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index b7b8d7e..96c779b 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -3389,45 +3389,44 @@ static void init_multi_out(struct hda_codec *codec)
 	}
 }
 
-/* initialize hp and speaker paths */
-static void init_extra_out(struct hda_codec *codec)
+
+static void __init_extra_out(struct hda_codec *codec, int num_outs,
+			     hda_nid_t *pins, hda_nid_t *dacs, int type)
 {
 	struct hda_gen_spec *spec = codec->spec;
 	int i;
 	hda_nid_t pin, dac;
 
-	for (i = 0; i < spec->autocfg.hp_outs; i++) {
-		if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
-			break;
-		pin = spec->autocfg.hp_pins[i];
-		if (!pin)
-			break;
-		dac = spec->multiout.hp_out_nid[i];
-		if (!dac) {
-			if (i > 0 && spec->multiout.hp_out_nid[0])
-				dac = spec->multiout.hp_out_nid[0];
-			else
-				dac = spec->multiout.dac_nids[0];
-		}
-		set_output_and_unmute(codec, pin, PIN_HP, dac);
-	}
-	for (i = 0; i < spec->autocfg.speaker_outs; i++) {
-		if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
-			break;
-		pin = spec->autocfg.speaker_pins[i];
+	for (i = 0; i < num_outs; i++) {
+		pin = pins[i];
 		if (!pin)
 			break;
-		dac = spec->multiout.extra_out_nid[i];
+		dac = dacs[i];
 		if (!dac) {
-			if (i > 0 && spec->multiout.extra_out_nid[0])
-				dac = spec->multiout.extra_out_nid[0];
+			if (i > 0 && dacs[0])
+				dac = dacs[0];
 			else
 				dac = spec->multiout.dac_nids[0];
 		}
-		set_output_and_unmute(codec, pin, PIN_OUT, dac);
+		set_output_and_unmute(codec, pin, type, dac);
 	}
 }
 
+/* initialize hp and speaker paths */
+static void init_extra_out(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+
+	if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT)
+		__init_extra_out(codec, spec->autocfg.hp_outs,
+				 spec->autocfg.hp_pins,
+				 spec->multiout.hp_out_nid, PIN_HP);
+	if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT)
+		__init_extra_out(codec, spec->autocfg.speaker_outs,
+				 spec->autocfg.speaker_pins,
+				 spec->multiout.extra_out_nid, PIN_OUT);
+}
+
 /* initialize multi-io paths */
 static void init_multi_io(struct hda_codec *codec)
 {
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 070/112] ALSA: hda - Fix initialization of primary outputs in hda_generic.c
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (68 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 069/112] ALSA: hda - Refactor init_extra_out() in hda_generic.c Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 071/112] ALSA: hda - Dynamically turn on/off EAPD in generic codec driver Takashi Iwai
                   ` (44 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

There were some old codes that look not stable enough, which was
derived from the old Realtek code.  The initialization for primary
output in init_multi_out() needs to consider the case of shared DAC.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 96c779b..a133fcf 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -3372,6 +3372,7 @@ static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin,
 static void init_multi_out(struct hda_codec *codec)
 {
 	struct hda_gen_spec *spec = codec->spec;
+	hda_nid_t nid, dac;
 	int pin_type;
 	int i;
 
@@ -3380,12 +3381,14 @@ static void init_multi_out(struct hda_codec *codec)
 	else
 		pin_type = PIN_OUT;
 
-	for (i = 0; i <= HDA_SIDE; i++) {
-		hda_nid_t nid = spec->autocfg.line_out_pins[i];
-		if (nid)
-			set_output_and_unmute(codec, nid, pin_type,
-					      spec->multiout.dac_nids[i]);
-
+	for (i = 0; i < spec->autocfg.line_outs; i++) {
+		nid = spec->autocfg.line_out_pins[i];
+		if (nid) {
+			dac = spec->multiout.dac_nids[i];
+			if (!dac)
+				dac = spec->multiout.dac_nids[0];
+			set_output_and_unmute(codec, nid, pin_type, dac);
+		}
 	}
 }
 
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 071/112] ALSA: hda - Dynamically turn on/off EAPD in generic codec driver
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (69 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 070/112] ALSA: hda - Fix initialization of primary outputs " Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 072/112] ALSA: hda - Use cached version for changing pins in hda_generic.c Takashi Iwai
                   ` (43 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

When spec->own_eapd_ctl isn't set, try to turn on/off EAPD on demand
for each pin.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 22 ++++++++++++++++------
 1 file changed, 16 insertions(+), 6 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index a133fcf..46b00e0 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -523,6 +523,18 @@ void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path,
 }
 EXPORT_SYMBOL_HDA(snd_hda_activate_path);
 
+/* turn on/off EAPD on the given pin */
+static void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	if (spec->own_eapd_ctl ||
+	    !(snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD))
+		return;
+	snd_hda_codec_update_cache(codec, pin, 0,
+				   AC_VERB_SET_EAPD_BTLENABLE,
+				   enable ? 0x02 : 0x00);
+}
+
 
 /*
  * Helper functions for creating mixer ctl elements
@@ -1485,7 +1497,9 @@ static int set_multi_io(struct hda_codec *codec, int idx, bool output)
 	if (output) {
 		snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT);
 		snd_hda_activate_path(codec, path, true, true);
+		set_pin_eapd(codec, nid, true);
 	} else {
+		set_pin_eapd(codec, nid, false);
 		snd_hda_activate_path(codec, path, false, true);
 		snd_hda_set_pin_ctl_cache(codec, nid,
 					  spec->multi_io[idx].ctl_in);
@@ -2418,6 +2432,7 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
 			val = 0;
 		val |= pin_bits;
 		snd_hda_set_pin_ctl(codec, nid, val);
+		set_pin_eapd(codec, nid, !mute);
 	}
 }
 
@@ -3351,7 +3366,6 @@ EXPORT_SYMBOL_HDA(snd_hda_gen_build_pcms);
 static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin,
 				  int pin_type, hda_nid_t dac)
 {
-	struct hda_gen_spec *spec = codec->spec;
 	struct nid_path *path;
 
 	snd_hda_set_pin_ctl_cache(codec, pin, pin_type);
@@ -3361,11 +3375,7 @@ static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin,
 	if (path->active)
 		return;
 	snd_hda_activate_path(codec, path, true, true);
-
-	if (!spec->own_eapd_ctl &&
-	    (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD))
-		snd_hda_codec_update_cache(codec, pin, 0,
-					   AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+	set_pin_eapd(codec, pin, true);
 }
 
 /* initialize primary output paths */
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 072/112] ALSA: hda - Use cached version for changing pins in hda_generic.c
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (70 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 071/112] ALSA: hda - Dynamically turn on/off EAPD in generic codec driver Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 073/112] ALSA: hda - Fix PCM name string for generic parser Takashi Iwai
                   ` (42 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

There is no reason to avoid snd_hda_set_pin_ctl_cache() there.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 46b00e0..f4b5043 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -1572,11 +1572,12 @@ static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic)
 		const hda_nid_t vref_pin = spec->shared_mic_vref_pin;
 		unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin);
 		if (vref_val != AC_PINCTL_VREF_HIZ)
-			snd_hda_set_pin_ctl(codec, vref_pin, PIN_IN | (set_as_mic ? vref_val : 0));
+			snd_hda_set_pin_ctl_cache(codec, vref_pin,
+					PIN_IN | (set_as_mic ? vref_val : 0));
 	}
 
 	val = set_as_mic ? val | PIN_IN : PIN_HP;
-	snd_hda_set_pin_ctl(codec, pin, val);
+	snd_hda_set_pin_ctl_cache(codec, pin, val);
 
 	spec->automute_speaker = !set_as_mic;
 	call_update_outputs(codec);
@@ -2431,7 +2432,7 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
 		} else
 			val = 0;
 		val |= pin_bits;
-		snd_hda_set_pin_ctl(codec, nid, val);
+		snd_hda_set_pin_ctl_cache(codec, nid, val);
 		set_pin_eapd(codec, nid, !mute);
 	}
 }
@@ -3467,7 +3468,7 @@ static void set_input_pin(struct hda_codec *codec, hda_nid_t nid,
 	unsigned int val = PIN_IN;
 	if (auto_pin_type == AUTO_PIN_MIC)
 		val |= snd_hda_get_default_vref(codec, nid);
-	snd_hda_set_pin_ctl(codec, nid, val);
+	snd_hda_set_pin_ctl_cache(codec, nid, val);
 }
 
 /* set up input pins and loopback paths */
@@ -3541,7 +3542,7 @@ static void init_digital(struct hda_codec *codec)
 	}
 	pin = spec->autocfg.dig_in_pin;
 	if (pin)
-		snd_hda_set_pin_ctl(codec, pin, PIN_IN);
+		snd_hda_set_pin_ctl_cache(codec, pin, PIN_IN);
 }
 
 /* clear unsol-event tags on unused pins; Conexant codecs seem to leave
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 073/112] ALSA: hda - Fix PCM name string for generic parser
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (71 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 072/112] ALSA: hda - Use cached version for changing pins in hda_generic.c Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 074/112] ALSA: hda - Drop spec->channel_mode field from hda_gen_spec Takashi Iwai
                   ` (41 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

When a PCM name string is generated from the chip name, it might
become strange like "CX20549 (Venice) Analog".  In this patch, the
parser tries to drop the invalid words like "(Venice)" in the PCM name
string.  Also, when the name string is given beforehand by the caller,
respect it and use it as is.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 32 +++++++++++++++++++++++++++-----
 1 file changed, 27 insertions(+), 5 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index f4b5043..d4cb9df 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -24,6 +24,8 @@
 #include <linux/slab.h>
 #include <linux/export.h>
 #include <linux/sort.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
 #include <sound/core.h>
 #include <sound/jack.h>
 #include "hda_codec.h"
@@ -3230,6 +3232,25 @@ static const struct hda_pcm_stream dyn_adc_pcm_analog_capture = {
 	},
 };
 
+static void fill_pcm_stream_name(char *str, size_t len, const char *sfx,
+				 const char *chip_name)
+{
+	char *p;
+
+	if (*str)
+		return;
+	strlcpy(str, chip_name, len);
+
+	/* drop non-alnum chars after a space */
+	for (p = strchr(str, ' '); p; p = strchr(p + 1, ' ')) {
+		if (!isalnum(p[1])) {
+			*p = 0;
+			break;
+		}
+	}
+	strlcat(str, sfx, len);
+}
+
 /* build PCM streams based on the parsed results */
 int snd_hda_gen_build_pcms(struct hda_codec *codec)
 {
@@ -3245,8 +3266,9 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec)
 	if (spec->no_analog)
 		goto skip_analog;
 
-	snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
-		 "%s Analog", codec->chip_name);
+	fill_pcm_stream_name(spec->stream_name_analog,
+			     sizeof(spec->stream_name_analog),
+			     " Analog", codec->chip_name);
 	info->name = spec->stream_name_analog;
 
 	if (spec->multiout.num_dacs > 0) {
@@ -3286,9 +3308,9 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec)
  skip_analog:
 	/* SPDIF for stream index #1 */
 	if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
-		snprintf(spec->stream_name_digital,
-			 sizeof(spec->stream_name_digital),
-			 "%s Digital", codec->chip_name);
+		fill_pcm_stream_name(spec->stream_name_digital,
+				     sizeof(spec->stream_name_digital),
+				     " Digital", codec->chip_name);
 		codec->num_pcms = 2;
 		codec->slave_dig_outs = spec->multiout.slave_dig_outs;
 		info = spec->pcm_rec + 1;
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 074/112] ALSA: hda - Drop spec->channel_mode field from hda_gen_spec
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (72 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 073/112] ALSA: hda - Fix PCM name string for generic parser Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 075/112] ALSA: hda - Add more debug prints about new paths Takashi Iwai
                   ` (40 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

It's never used in the generic parser.  It was there from the old
Realtek code, which has been dropped quite ago, too.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 10 ----------
 sound/pci/hda/hda_generic.h |  2 --
 2 files changed, 12 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index d4cb9df..43acf3d 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -3258,7 +3258,6 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec)
 	struct hda_pcm *info = spec->pcm_rec;
 	const struct hda_pcm_stream *p;
 	bool have_multi_adcs;
-	int i;
 
 	codec->num_pcms = 1;
 	codec->pcm_info = info;
@@ -3296,15 +3295,6 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec)
 		info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
 	}
 
-	if (spec->channel_mode) {
-		info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 0;
-		for (i = 0; i < spec->num_channel_mode; i++) {
-			if (spec->channel_mode[i].channels > info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max) {
-				info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->channel_mode[i].channels;
-			}
-		}
-	}
-
  skip_analog:
 	/* SPDIF for stream index #1 */
 	if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index a406cd4..6365140 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -101,8 +101,6 @@ struct hda_gen_spec {
 	unsigned int cur_mux[3];
 
 	/* channel model */
-	const struct hda_channel_mode *channel_mode;
-	int num_channel_mode;
 	int const_channel_count;	/* min. channel count (for speakers) */
 	int ext_channel_count;		/* current channel count for multi-io */
 
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 075/112] ALSA: hda - Add more debug prints about new paths
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (73 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 074/112] ALSA: hda - Drop spec->channel_mode field from hda_gen_spec Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 076/112] ALSA: hda - Fix typos in debug_show_configs() Takashi Iwai
                   ` (39 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

Add a better debug print code to show the new assigned paths in
generic parser.  It appears only with CONFIG_SND_DEBUG_VERBOSE=y.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 46 +++++++++++++++++++++++++++++++++++----------
 1 file changed, 36 insertions(+), 10 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 43acf3d..8af5324 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -186,6 +186,21 @@ static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid,
 		is_ctl_used(codec, val, NID_PATH_MUTE_CTL);
 }
 
+static void print_nid_path(const char *pfx, struct nid_path *path)
+{
+	char buf[40];
+	int i;
+
+
+	buf[0] = 0;
+	for (i = 0; i < path->depth; i++) {
+		char tmp[4];
+		sprintf(tmp, ":%02x", path->path[i]);
+		strlcat(buf, tmp, sizeof(buf));
+	}
+	snd_printdd("%s path: depth=%d %s\n", pfx, path->depth, buf);
+}
+
 /* called recursively */
 static bool __parse_nid_path(struct hda_codec *codec,
 			     hda_nid_t from_nid, hda_nid_t to_nid,
@@ -252,11 +267,6 @@ bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
 	if (__parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path, 1)) {
 		path->path[path->depth] = to_nid;
 		path->depth++;
-#if 0
-		snd_printdd("path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
-			    path->depth, path->path[0], path->path[1],
-			    path->path[2], path->path[3], path->path[4]);
-#endif
 		return true;
 	}
 	return false;
@@ -816,6 +826,7 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs,
 		return 0;
 
 	for (i = 0; i < num_outs; i++) {
+		struct nid_path *path;
 		hda_nid_t pin = pins[i];
 		if (!dacs[i])
 			dacs[i] = look_for_dac(codec, pin, false);
@@ -850,8 +861,11 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs,
 			else
 				badness += bad->no_dac;
 		}
-		if (!snd_hda_add_new_path(codec, dac, pin, 0))
+		path = snd_hda_add_new_path(codec, dac, pin, 0);
+		if (!path)
 			dac = dacs[i] = 0;
+		else
+			print_nid_path("output", path);
 		if (dac)
 			badness += assign_out_path_ctls(codec, pin, dac);
 	}
@@ -935,6 +949,7 @@ static int fill_multi_ios(struct hda_codec *codec,
 	dacs = spec->multiout.num_dacs;
 	for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
 		for (i = 0; i < cfg->num_inputs; i++) {
+			struct nid_path *path;
 			hda_nid_t nid = cfg->inputs[i].pin;
 			hda_nid_t dac = 0;
 
@@ -962,10 +977,12 @@ static int fill_multi_ios(struct hda_codec *codec,
 				badness++;
 				continue;
 			}
-			if (!snd_hda_add_new_path(codec, dac, nid, 0)) {
+			path = snd_hda_add_new_path(codec, dac, nid, 0);
+			if (!path) {
 				badness++;
 				continue;
 			}
+			print_nid_path("multiio", path);
 			spec->multi_io[spec->multi_ios].pin = nid;
 			spec->multi_io[spec->multi_ios].dac = dac;
 			spec->multi_ios++;
@@ -1004,15 +1021,18 @@ static bool map_singles(struct hda_codec *codec, int outs,
 	int i;
 	bool found = false;
 	for (i = 0; i < outs; i++) {
+		struct nid_path *path;
 		hda_nid_t dac;
 		if (dacs[i])
 			continue;
 		dac = get_dac_if_single(codec, pins[i]);
 		if (!dac)
 			continue;
-		if (snd_hda_add_new_path(codec, dac, pins[i], 0)) {
+		path = snd_hda_add_new_path(codec, dac, pins[i], 0);
+		if (path) {
 			dacs[i] = dac;
 			found = true;
+			print_nid_path("output", path);
 		}
 	}
 	return found;
@@ -1268,6 +1288,7 @@ static int parse_output_paths(struct hda_codec *codec)
 	}
 
 	if (badness) {
+		debug_badness("==> restoring best_cfg\n");
 		*cfg = *best_cfg;
 		fill_and_eval_dacs(codec, best_wired, best_mio);
 	}
@@ -1659,6 +1680,7 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin,
 	path = snd_hda_add_new_path(codec, pin, mix_nid, 2);
 	if (!path)
 		return -EINVAL;
+	print_nid_path("loopback", path);
 
 	idx = path->idx[path->depth - 1];
 	if (nid_has_volume(codec, mix_nid, HDA_INPUT)) {
@@ -1836,6 +1858,7 @@ static int create_input_ctls(struct hda_codec *codec)
 				spec->paths.used--;
 				continue;
 			}
+			print_nid_path("input", path);
 
 			if (!imux_added) {
 				spec->imux_pins[imux->num_items] = pin;
@@ -2295,6 +2318,7 @@ static int parse_mic_boost(struct hda_codec *codec)
 static void parse_digital(struct hda_codec *codec)
 {
 	struct hda_gen_spec *spec = codec->spec;
+	struct nid_path *path;
 	int i, nums;
 	hda_nid_t dig_nid;
 
@@ -2305,8 +2329,10 @@ static void parse_digital(struct hda_codec *codec)
 		dig_nid = look_for_dac(codec, pin, true);
 		if (!dig_nid)
 			continue;
-		if (!snd_hda_add_new_path(codec, dig_nid, pin, 2))
+		path = snd_hda_add_new_path(codec, dig_nid, pin, 2);
+		if (!path)
 			continue;
+		print_nid_path("digout", path);
 		if (!nums) {
 			spec->multiout.dig_out_nid = dig_nid;
 			spec->dig_out_type = spec->autocfg.dig_out_type[0];
@@ -2322,7 +2348,6 @@ static void parse_digital(struct hda_codec *codec)
 	if (spec->autocfg.dig_in_pin) {
 		dig_nid = codec->start_nid;
 		for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
-			struct nid_path *path;
 			unsigned int wcaps = get_wcaps(codec, dig_nid);
 			if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
 				continue;
@@ -2332,6 +2357,7 @@ static void parse_digital(struct hda_codec *codec)
 						    spec->autocfg.dig_in_pin,
 						    dig_nid, 2);
 			if (path) {
+				print_nid_path("digin", path);
 				path->active = true;
 				spec->dig_in_nid = dig_nid;
 				break;
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 076/112] ALSA: hda - Fix typos in debug_show_configs()
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (74 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 075/112] ALSA: hda - Add more debug prints about new paths Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 077/112] ALSA: hda - Define HDA_PARSE_* for snd_hda_parse_nid_path() argument Takashi Iwai
                   ` (38 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

It never showed the 4th line out and headphone pins since quite ago.
Oh well.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 8af5324..b341450 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -1164,7 +1164,7 @@ static void debug_show_configs(struct hda_gen_spec *spec, struct auto_pin_cfg *c
 {
 	debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
 		      cfg->line_out_pins[0], cfg->line_out_pins[1],
-		      cfg->line_out_pins[2], cfg->line_out_pins[2],
+		      cfg->line_out_pins[2], cfg->line_out_pins[3],
 		      spec->multiout.dac_nids[0],
 		      spec->multiout.dac_nids[1],
 		      spec->multiout.dac_nids[2],
@@ -1176,7 +1176,7 @@ static void debug_show_configs(struct hda_gen_spec *spec, struct auto_pin_cfg *c
 			      spec->multi_io[0].dac, spec->multi_io[1].dac);
 	debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
 		      cfg->hp_pins[0], cfg->hp_pins[1],
-		      cfg->hp_pins[2], cfg->hp_pins[2],
+		      cfg->hp_pins[2], cfg->hp_pins[3],
 		      spec->multiout.hp_out_nid[0],
 		      spec->multiout.hp_out_nid[1],
 		      spec->multiout.hp_out_nid[2],
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 077/112] ALSA: hda - Define HDA_PARSE_* for snd_hda_parse_nid_path() argument
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (75 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 076/112] ALSA: hda - Fix typos in debug_show_configs() Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 078/112] ALSA: hda - Allow aamix in the primary output path Takashi Iwai
                   ` (37 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

... instead of numbers.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 28 +++++++++++++++-------------
 sound/pci/hda/hda_generic.h |  7 +++++++
 2 files changed, 22 insertions(+), 13 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index b341450..18b5fae 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -211,9 +211,9 @@ static bool __parse_nid_path(struct hda_codec *codec,
 	int i, nums;
 
 	if (to_nid == spec->mixer_nid) {
-		if (!with_aa_mix)
+		if (with_aa_mix == HDA_PARSE_NO_AAMIX)
 			return false;
-		with_aa_mix = 2; /* mark aa-mix is included */
+		with_aa_mix = HDA_PARSE_ALL; /* mark aa-mix is included */
 	}
 
 	nums = snd_hda_get_connections(codec, to_nid, conn, ARRAY_SIZE(conn));
@@ -228,7 +228,7 @@ static bool __parse_nid_path(struct hda_codec *codec,
 				continue;
 		}
 		/* aa-mix is requested but not included? */
-		if (!(spec->mixer_nid && with_aa_mix == 1))
+		if (!(spec->mixer_nid && with_aa_mix == HDA_PARSE_ONLY_AAMIX))
 			goto found;
 	}
 	if (depth >= MAX_NID_PATH_DEPTH)
@@ -256,9 +256,11 @@ static bool __parse_nid_path(struct hda_codec *codec,
 
 /* parse the widget path from the given nid to the target nid;
  * when @from_nid is 0, try to find an empty DAC;
- * when @with_aa_mix is 0, paths with spec->mixer_nid are excluded.
- * when @with_aa_mix is 1, paths without spec->mixer_nid are excluded.
- * when @with_aa_mix is 2, no special handling about spec->mixer_nid.
+ * when @with_aa_mix is HDA_PARSE_NO_AAMIX, paths with spec->mixer_nid are
+ * excluded, only the paths that don't go through the mixer will be chosen.
+ * when @with_aa_mix is HDA_PARSE_ONLY_AAMIX, only the paths going through
+ * spec->mixer_nid will be chosen.
+ * when @with_aa_mix is HDA_PARSE_ALL, no special handling about mixer widget.
  */
 bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
 			    hda_nid_t to_nid, int with_aa_mix,
@@ -861,7 +863,7 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs,
 			else
 				badness += bad->no_dac;
 		}
-		path = snd_hda_add_new_path(codec, dac, pin, 0);
+		path = snd_hda_add_new_path(codec, dac, pin, HDA_PARSE_NO_AAMIX);
 		if (!path)
 			dac = dacs[i] = 0;
 		else
@@ -977,7 +979,7 @@ static int fill_multi_ios(struct hda_codec *codec,
 				badness++;
 				continue;
 			}
-			path = snd_hda_add_new_path(codec, dac, nid, 0);
+			path = snd_hda_add_new_path(codec, dac, nid, HDA_PARSE_NO_AAMIX);
 			if (!path) {
 				badness++;
 				continue;
@@ -1028,7 +1030,7 @@ static bool map_singles(struct hda_codec *codec, int outs,
 		dac = get_dac_if_single(codec, pins[i]);
 		if (!dac)
 			continue;
-		path = snd_hda_add_new_path(codec, dac, pins[i], 0);
+		path = snd_hda_add_new_path(codec, dac, pins[i], HDA_PARSE_NO_AAMIX);
 		if (path) {
 			dacs[i] = dac;
 			found = true;
@@ -1677,7 +1679,7 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin,
 	    !nid_has_mute(codec, mix_nid, HDA_INPUT))
 		return 0; /* no need for analog loopback */
 
-	path = snd_hda_add_new_path(codec, pin, mix_nid, 2);
+	path = snd_hda_add_new_path(codec, pin, mix_nid, HDA_PARSE_ALL);
 	if (!path)
 		return -EINVAL;
 	print_nid_path("loopback", path);
@@ -1851,7 +1853,7 @@ static int create_input_ctls(struct hda_codec *codec)
 			if (!path)
 				return -ENOMEM;
 			memset(path, 0, sizeof(*path));
-			if (!snd_hda_parse_nid_path(codec, pin, adc, 2, path)) {
+			if (!snd_hda_parse_nid_path(codec, pin, adc, HDA_PARSE_ALL, path)) {
 				snd_printd(KERN_ERR
 					   "invalid input path 0x%x -> 0x%x\n",
 					   pin, adc);
@@ -2329,7 +2331,7 @@ static void parse_digital(struct hda_codec *codec)
 		dig_nid = look_for_dac(codec, pin, true);
 		if (!dig_nid)
 			continue;
-		path = snd_hda_add_new_path(codec, dig_nid, pin, 2);
+		path = snd_hda_add_new_path(codec, dig_nid, pin, HDA_PARSE_ALL);
 		if (!path)
 			continue;
 		print_nid_path("digout", path);
@@ -2355,7 +2357,7 @@ static void parse_digital(struct hda_codec *codec)
 				continue;
 			path = snd_hda_add_new_path(codec,
 						    spec->autocfg.dig_in_pin,
-						    dig_nid, 2);
+						    dig_nid, HDA_PARSE_ALL);
 			if (path) {
 				print_nid_path("digin", path);
 				path->active = true;
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index 6365140..85d138f 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -180,6 +180,13 @@ int snd_hda_gen_init(struct hda_codec *codec);
 
 struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec,
 				      hda_nid_t from_nid, hda_nid_t to_nid);
+
+enum {
+	HDA_PARSE_NO_AAMIX,
+	HDA_PARSE_ONLY_AAMIX,
+	HDA_PARSE_ALL,
+};
+
 bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
 			    hda_nid_t to_nid, int with_aa_mix,
 			    struct nid_path *path);
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 078/112] ALSA: hda - Allow aamix in the primary output path
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (76 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 077/112] ALSA: hda - Define HDA_PARSE_* for snd_hda_parse_nid_path() argument Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 079/112] ALSA: hda - Implement independent HP control Takashi Iwai
                   ` (36 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

Allow the path including the loopback mixer widget in the primary
output channel as an alternative in the generic codec parser.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 18b5fae..f3c6ace 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -864,6 +864,10 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs,
 				badness += bad->no_dac;
 		}
 		path = snd_hda_add_new_path(codec, dac, pin, HDA_PARSE_NO_AAMIX);
+		if (!path && i > 0 && spec->mixer_nid) {
+			/* try with aamix */
+			path = snd_hda_add_new_path(codec, dac, pin, HDA_PARSE_ALL);
+		}
 		if (!path)
 			dac = dacs[i] = 0;
 		else
@@ -1020,6 +1024,7 @@ static int fill_multi_ios(struct hda_codec *codec,
 static bool map_singles(struct hda_codec *codec, int outs,
 			const hda_nid_t *pins, hda_nid_t *dacs)
 {
+	struct hda_gen_spec *spec = codec->spec;
 	int i;
 	bool found = false;
 	for (i = 0; i < outs; i++) {
@@ -1031,6 +1036,8 @@ static bool map_singles(struct hda_codec *codec, int outs,
 		if (!dac)
 			continue;
 		path = snd_hda_add_new_path(codec, dac, pins[i], HDA_PARSE_NO_AAMIX);
+		if (!path && i > 0 && spec->mixer_nid)
+			path = snd_hda_add_new_path(codec, dac, pins[i], HDA_PARSE_ALL);
 		if (path) {
 			dacs[i] = dac;
 			found = true;
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 079/112] ALSA: hda - Implement independent HP control
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (77 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 078/112] ALSA: hda - Allow aamix in the primary output path Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 080/112] ALSA: hda - Add inv_eapd flag to struct hda_codec Takashi Iwai
                   ` (35 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

Similar like the implementation in patch_analog.c and patch_via.c,
the generic parser can provide the independent HP PCM stream now.
It's enabled when spec->indep_hp is set by the caller while parsing.

Currently no dynamic PCM switching as in patch_via.c is implemented
yet.  The control returns -EBUSY when the value is changed during PCM
operations.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 128 +++++++++++++++++++++++++++++++++++++++++++-
 sound/pci/hda/hda_generic.h |   9 ++++
 2 files changed, 136 insertions(+), 1 deletion(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index f3c6ace..cc47460 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -41,6 +41,7 @@ int snd_hda_gen_spec_init(struct hda_gen_spec *spec)
 	snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32);
 	snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8);
 	snd_array_init(&spec->paths, sizeof(struct nid_path), 8);
+	mutex_init(&spec->pcm_mutex);
 	return 0;
 }
 EXPORT_SYMBOL_HDA(snd_hda_gen_spec_init);
@@ -1485,6 +1486,77 @@ static int create_speaker_out_ctls(struct hda_codec *codec)
 }
 
 /*
+ * independent HP controls
+ */
+
+static int indep_hp_info(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_info *uinfo)
+{
+	return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
+}
+
+static int indep_hp_get(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct hda_gen_spec *spec = codec->spec;
+	ucontrol->value.enumerated.item[0] = spec->indep_hp_enabled;
+	return 0;
+}
+
+static int indep_hp_put(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct hda_gen_spec *spec = codec->spec;
+	unsigned int select = ucontrol->value.enumerated.item[0];
+
+	mutex_lock(&spec->pcm_mutex);
+	if (spec->active_streams) {
+		mutex_unlock(&spec->pcm_mutex);
+		return -EBUSY;
+	}
+
+	if (spec->indep_hp_enabled != select) {
+		spec->indep_hp_enabled = select;
+		if (spec->indep_hp_enabled)
+			spec->multiout.hp_out_nid[0] = 0;
+		else
+			spec->multiout.hp_out_nid[0] = spec->alt_dac_nid;
+		return 1;
+	}
+	mutex_unlock(&spec->pcm_mutex);
+	return 0;
+}
+
+static const struct snd_kcontrol_new indep_hp_ctl = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Independent HP",
+	.info = indep_hp_info,
+	.get = indep_hp_get,
+	.put = indep_hp_put,
+};
+
+
+static int create_indep_hp_ctls(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+
+	if (!spec->indep_hp)
+		return 0;
+	if (!spec->multiout.hp_out_nid[0]) {
+		spec->indep_hp = 0;
+		return 0;
+	}
+
+	spec->indep_hp_enabled = false;
+	spec->alt_dac_nid = spec->multiout.hp_out_nid[0];
+	if (!snd_hda_gen_add_kctl(spec, NULL, &indep_hp_ctl))
+		return -ENOMEM;
+	return 0;
+}
+
+/*
  * channel mode enum control
  */
 
@@ -2905,6 +2977,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
 	err = create_speaker_out_ctls(codec);
 	if (err < 0)
 		return err;
+	err = create_indep_hp_ctls(codec);
+	if (err < 0)
+		return err;
 	err = create_shared_input(codec);
 	if (err < 0)
 		return err;
@@ -3057,8 +3132,16 @@ static int playback_pcm_open(struct hda_pcm_stream *hinfo,
 			     struct snd_pcm_substream *substream)
 {
 	struct hda_gen_spec *spec = codec->spec;
-	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+	int err;
+
+	mutex_lock(&spec->pcm_mutex);
+	err = snd_hda_multi_out_analog_open(codec,
+					    &spec->multiout, substream,
 					     hinfo);
+	if (!err)
+		spec->active_streams |= 1 << STREAM_MULTI_OUT;
+	mutex_unlock(&spec->pcm_mutex);
+	return err;
 }
 
 static int playback_pcm_prepare(struct hda_pcm_stream *hinfo,
@@ -3080,6 +3163,44 @@ static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
 	return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
 }
 
+static int playback_pcm_close(struct hda_pcm_stream *hinfo,
+			      struct hda_codec *codec,
+			      struct snd_pcm_substream *substream)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	mutex_lock(&spec->pcm_mutex);
+	spec->active_streams &= ~(1 << STREAM_MULTI_OUT);
+	mutex_unlock(&spec->pcm_mutex);
+	return 0;
+}
+
+static int alt_playback_pcm_open(struct hda_pcm_stream *hinfo,
+				 struct hda_codec *codec,
+				 struct snd_pcm_substream *substream)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	int err = 0;
+
+	mutex_lock(&spec->pcm_mutex);
+	if (!spec->indep_hp_enabled)
+		err = -EBUSY;
+	else
+		spec->active_streams |= 1 << STREAM_INDEP_HP;
+	mutex_unlock(&spec->pcm_mutex);
+	return err;
+}
+
+static int alt_playback_pcm_close(struct hda_pcm_stream *hinfo,
+				  struct hda_codec *codec,
+				  struct snd_pcm_substream *substream)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	mutex_lock(&spec->pcm_mutex);
+	spec->active_streams &= ~(1 << STREAM_INDEP_HP);
+	mutex_unlock(&spec->pcm_mutex);
+	return 0;
+}
+
 /*
  * Digital out
  */
@@ -3154,6 +3275,7 @@ static const struct hda_pcm_stream pcm_analog_playback = {
 	/* NID is set in build_pcms */
 	.ops = {
 		.open = playback_pcm_open,
+		.close = playback_pcm_close,
 		.prepare = playback_pcm_prepare,
 		.cleanup = playback_pcm_cleanup
 	},
@@ -3171,6 +3293,10 @@ static const struct hda_pcm_stream pcm_analog_alt_playback = {
 	.channels_min = 2,
 	.channels_max = 2,
 	/* NID is set in build_pcms */
+	.ops = {
+		.open = alt_playback_pcm_open,
+		.close = alt_playback_pcm_close
+	},
 };
 
 static const struct hda_pcm_stream pcm_analog_alt_capture = {
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index 85d138f..5c1569c 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -65,6 +65,9 @@ struct automic_entry {
 	unsigned int attr;	/* pin attribute (INPUT_PIN_ATTR_*) */
 };
 
+/* active stream id */
+enum { STREAM_MULTI_OUT, STREAM_INDEP_HP };
+
 struct hda_gen_spec {
 	char stream_name_analog[32];	/* analog PCM stream */
 	const struct hda_pcm_stream *stream_analog_playback;
@@ -76,6 +79,10 @@ struct hda_gen_spec {
 	const struct hda_pcm_stream *stream_digital_playback;
 	const struct hda_pcm_stream *stream_digital_capture;
 
+	/* PCM */
+	unsigned int active_streams;
+	struct mutex pcm_mutex;
+
 	/* playback */
 	struct hda_multi_out multiout;	/* playback set-up
 					 * max_channels, dacs must be set
@@ -150,6 +157,8 @@ struct hda_gen_spec {
 	unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */
 	unsigned int own_eapd_ctl:1; /* set EAPD by own function */
 	unsigned int vmaster_mute_enum:1; /* add vmaster mute mode enum */
+	unsigned int indep_hp:1; /* independent HP supported */
+	unsigned int indep_hp_enabled:1; /* independent HP enabled */
 
 	/* for virtual master */
 	hda_nid_t vmaster_nid;
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 080/112] ALSA: hda - Add inv_eapd flag to struct hda_codec
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (78 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 079/112] ALSA: hda - Implement independent HP control Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 081/112] ALSA: hda - Add codec->inv_jack_detect flag Takashi Iwai
                   ` (34 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

Add the new flag, codec->inv_eapd, indicating that the EAPD
implementation is inverted.

There are always broken hardware in the world.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_codec.h   | 1 +
 sound/pci/hda/hda_generic.c | 2 ++
 2 files changed, 3 insertions(+)

diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 2d9a51c..369ffaf 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -866,6 +866,7 @@ struct hda_codec {
 	unsigned int pins_shutup:1;	/* pins are shut up */
 	unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */
 	unsigned int no_jack_detect:1;	/* Machine has no jack-detection */
+	unsigned int inv_eapd:1; /* broken h/w: inverted EAPD control */
 	unsigned int pcm_format_first:1; /* PCM format must be set first */
 	unsigned int epss:1;		/* supporting EPSS? */
 	unsigned int cached_write:1;	/* write only to caches */
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index cc47460..18daa8f 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -545,6 +545,8 @@ static void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable)
 	if (spec->own_eapd_ctl ||
 	    !(snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD))
 		return;
+	if (codec->inv_eapd)
+		enable = !enable;
 	snd_hda_codec_update_cache(codec, pin, 0,
 				   AC_VERB_SET_EAPD_BTLENABLE,
 				   enable ? 0x02 : 0x00);
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 081/112] ALSA: hda - Add codec->inv_jack_detect flag
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (79 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 080/112] ALSA: hda - Add inv_eapd flag to struct hda_codec Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 082/112] ALSA: hda - Revive snd_hda_get_conn_list() Takashi Iwai
                   ` (33 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

Yet another broken hardware workaround: there are hardware indicating
the inverted jack detection bit result.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_codec.h | 1 +
 sound/pci/hda/hda_jack.c  | 6 +++++-
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 369ffaf..9f241d1 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -867,6 +867,7 @@ struct hda_codec {
 	unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */
 	unsigned int no_jack_detect:1;	/* Machine has no jack-detection */
 	unsigned int inv_eapd:1; /* broken h/w: inverted EAPD control */
+	unsigned int inv_jack_detect:1;	/* broken h/w: inverted detection bit */
 	unsigned int pcm_format_first:1; /* PCM format must be set first */
 	unsigned int epss:1;		/* supporting EPSS? */
 	unsigned int cached_write:1;	/* write only to caches */
diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c
index 6e9f57b..6479b65 100644
--- a/sound/pci/hda/hda_jack.c
+++ b/sound/pci/hda/hda_jack.c
@@ -39,6 +39,7 @@ EXPORT_SYMBOL_HDA(is_jack_detectable);
 static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid)
 {
 	u32 pincap;
+	u32 val;
 
 	if (!codec->no_trigger_sense) {
 		pincap = snd_hda_query_pin_caps(codec, nid);
@@ -46,8 +47,11 @@ static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid)
 			snd_hda_codec_read(codec, nid, 0,
 					AC_VERB_SET_PIN_SENSE, 0);
 	}
-	return snd_hda_codec_read(codec, nid, 0,
+	val = snd_hda_codec_read(codec, nid, 0,
 				  AC_VERB_GET_PIN_SENSE, 0);
+	if (codec->inv_jack_detect)
+		val ^= AC_PINSENSE_PRESENCE;
+	return val;
 }
 
 /**
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 082/112] ALSA: hda - Revive snd_hda_get_conn_list()
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (80 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 081/112] ALSA: hda - Add codec->inv_jack_detect flag Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 083/112] ALSA: hda - Add hooks for HP/line/mic auto switching Takashi Iwai
                   ` (32 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

Manage the connection list cache using linked lists instead of
snd_array, and revive snd_hda_get_conn_list() again, so that we don't
have to keep the expanded values locally.
This will reduce the stack usage by recursive call of
snd_hda_get_conn_index() or parse_nid_path() of the generic parser.

The list management doesn't include any mutex protection, thus the
caller needs to take care of race appropriately.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_codec.c   | 164 ++++++++++++++++++++++++++------------------
 sound/pci/hda/hda_codec.h   |   4 +-
 sound/pci/hda/hda_generic.c |   8 +--
 3 files changed, 106 insertions(+), 70 deletions(-)

diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 380c9ed..cd318e4 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -334,20 +334,51 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid,
 }
 EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes);
 
+/* connection list element */
+struct hda_conn_list {
+	struct list_head list;
+	int len;
+	hda_nid_t nid;
+	hda_nid_t conns[0];
+};
+
 /* look up the cached results */
-static hda_nid_t *lookup_conn_list(struct snd_array *array, hda_nid_t nid)
+static struct hda_conn_list *
+lookup_conn_list(struct hda_codec *codec, hda_nid_t nid)
 {
-	int i, len;
-	for (i = 0; i < array->used; ) {
-		hda_nid_t *p = snd_array_elem(array, i);
-		if (nid == *p)
+	struct hda_conn_list *p;
+	list_for_each_entry(p, &codec->conn_list, list) {
+		if (p->nid == nid)
 			return p;
-		len = p[1];
-		i += len + 2;
 	}
 	return NULL;
 }
 
+static int add_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
+			 const hda_nid_t *list)
+{
+	struct hda_conn_list *p;
+
+	p = kmalloc(sizeof(*p) + len * sizeof(hda_nid_t), GFP_KERNEL);
+	if (!p)
+		return -ENOMEM;
+	p->len = len;
+	p->nid = nid;
+	memcpy(p->conns, list, len * sizeof(hda_nid_t));
+	list_add(&p->list, &codec->conn_list);
+	return 0;
+}
+
+static void remove_conn_list(struct hda_codec *codec)
+{
+	while (!list_empty(&codec->conn_list)) {
+		struct hda_conn_list *p;
+		p = list_first_entry(&codec->conn_list, typeof(*p), list);
+		list_del(&p->list);
+		kfree(p);
+	}
+}
+
 /* read the connection and add to the cache */
 static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid)
 {
@@ -361,6 +392,49 @@ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid)
 }
 
 /**
+ * snd_hda_get_conn_list - get connection list
+ * @codec: the HDA codec
+ * @nid: NID to parse
+ * @len: number of connection list entries
+ * @listp: the pointer to store NID list
+ *
+ * Parses the connection list of the given widget and stores the pointer
+ * to the list of NIDs.
+ *
+ * Returns the number of connections, or a negative error code.
+ *
+ * Note that the returned pointer isn't protected against the list
+ * modification.  If snd_hda_override_conn_list() might be called
+ * concurrently, protect with a mutex appropriately.
+ */
+int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
+			  const hda_nid_t **listp)
+{
+	bool added = false;
+
+	for (;;) {
+		int err;
+		const struct hda_conn_list *p;
+
+		/* if the connection-list is already cached, read it */
+		p = lookup_conn_list(codec, nid);
+		if (p) {
+			if (listp)
+				*listp = p->conns;
+			return p->len;
+		}
+		if (snd_BUG_ON(added))
+			return -EINVAL;
+
+		err = read_and_add_raw_conns(codec, nid);
+		if (err < 0)
+			return err;
+		added = true;
+	}
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_conn_list);
+
+/**
  * snd_hda_get_connections - copy connection list
  * @codec: the HDA codec
  * @nid: NID to parse
@@ -375,39 +449,20 @@ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid)
 int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
 			    hda_nid_t *conn_list, int max_conns)
 {
-	struct snd_array *array = &codec->conn_lists;
-	int len;
-	hda_nid_t *p;
-	bool added = false;
+	const hda_nid_t *list;
+	int len = snd_hda_get_conn_list(codec, nid, &list);
 
- again:
-	mutex_lock(&codec->hash_mutex);
-	len = -1;
-	/* if the connection-list is already cached, read it */
-	p = lookup_conn_list(array, nid);
-	if (p) {
-		len = p[1];
-		if (conn_list && len > max_conns) {
+	if (len > 0 && conn_list) {
+		if (len > max_conns) {
 			snd_printk(KERN_ERR "hda_codec: "
 				   "Too many connections %d for NID 0x%x\n",
 				   len, nid);
-			mutex_unlock(&codec->hash_mutex);
 			return -EINVAL;
 		}
-		if (conn_list && len)
-			memcpy(conn_list, p + 2, len * sizeof(hda_nid_t));
+		memcpy(conn_list, list, len * sizeof(hda_nid_t));
 	}
-	mutex_unlock(&codec->hash_mutex);
-	if (len >= 0)
-		return len;
-	if (snd_BUG_ON(added))
-		return -EINVAL;
 
-	len = read_and_add_raw_conns(codec, nid);
-	if (len < 0)
-		return len;
-	added = true;
-	goto again;
+	return len;
 }
 EXPORT_SYMBOL_HDA(snd_hda_get_connections);
 
@@ -519,15 +574,6 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
 	return conns;
 }
 
-static bool add_conn_list(struct snd_array *array, hda_nid_t nid)
-{
-	hda_nid_t *p = snd_array_new(array);
-	if (!p)
-		return false;
-	*p = nid;
-	return true;
-}
-
 /**
  * snd_hda_override_conn_list - add/modify the connection-list to cache
  * @codec: the HDA codec
@@ -543,28 +589,15 @@ static bool add_conn_list(struct snd_array *array, hda_nid_t nid)
 int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
 			       const hda_nid_t *list)
 {
-	struct snd_array *array = &codec->conn_lists;
-	hda_nid_t *p;
-	int i, old_used;
+	struct hda_conn_list *p;
 
-	mutex_lock(&codec->hash_mutex);
-	p = lookup_conn_list(array, nid);
-	if (p)
-		*p = -1; /* invalidate the old entry */
-
-	old_used = array->used;
-	if (!add_conn_list(array, nid) || !add_conn_list(array, len))
-		goto error_add;
-	for (i = 0; i < len; i++)
-		if (!add_conn_list(array, list[i]))
-			goto error_add;
-	mutex_unlock(&codec->hash_mutex);
-	return 0;
+	p = lookup_conn_list(codec, nid);
+	if (p) {
+		list_del(&p->list);
+		kfree(p);
+	}
 
- error_add:
-	array->used = old_used;
-	mutex_unlock(&codec->hash_mutex);
-	return -ENOMEM;
+	return add_conn_list(codec, nid, len, list);
 }
 EXPORT_SYMBOL_HDA(snd_hda_override_conn_list);
 
@@ -582,10 +615,10 @@ EXPORT_SYMBOL_HDA(snd_hda_override_conn_list);
 int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
 			   hda_nid_t nid, int recursive)
 {
-	hda_nid_t conn[HDA_MAX_NUM_INPUTS];
+	const hda_nid_t *conn;
 	int i, nums;
 
-	nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
+	nums = snd_hda_get_conn_list(codec, mux, &conn);
 	for (i = 0; i < nums; i++)
 		if (conn[i] == nid)
 			return i;
@@ -1186,8 +1219,8 @@ static void snd_hda_codec_free(struct hda_codec *codec)
 	snd_array_free(&codec->mixers);
 	snd_array_free(&codec->nids);
 	snd_array_free(&codec->cvt_setups);
-	snd_array_free(&codec->conn_lists);
 	snd_array_free(&codec->spdif_out);
+	remove_conn_list(codec);
 	codec->bus->caddr_tbl[codec->addr] = NULL;
 	if (codec->patch_ops.free)
 		codec->patch_ops.free(codec);
@@ -1257,10 +1290,11 @@ int snd_hda_codec_new(struct hda_bus *bus,
 	snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
 	snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
 	snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8);
-	snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64);
 	snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16);
 	snd_array_init(&codec->jacktbl, sizeof(struct hda_jack_tbl), 16);
 	snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8);
+	INIT_LIST_HEAD(&codec->conn_list);
+
 	INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work);
 
 #ifdef CONFIG_PM
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 9f241d1..93ec747 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -831,7 +831,7 @@ struct hda_codec {
 	struct hda_cache_rec amp_cache;	/* cache for amp access */
 	struct hda_cache_rec cmd_cache;	/* cache for other commands */
 
-	struct snd_array conn_lists;	/* connection-list array */
+	struct list_head conn_list;	/* linked-list of connection-list */
 
 	struct mutex spdif_mutex;
 	struct mutex control_mutex;
@@ -944,6 +944,8 @@ snd_hda_get_num_conns(struct hda_codec *codec, hda_nid_t nid)
 }
 int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
 			    hda_nid_t *conn_list, int max_conns);
+int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
+			  const hda_nid_t **listp);
 int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums,
 			  const hda_nid_t *list);
 int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 18daa8f..f69f2ab 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -208,7 +208,7 @@ static bool __parse_nid_path(struct hda_codec *codec,
 			     int with_aa_mix, struct nid_path *path, int depth)
 {
 	struct hda_gen_spec *spec = codec->spec;
-	hda_nid_t conn[16];
+	const hda_nid_t *conn;
 	int i, nums;
 
 	if (to_nid == spec->mixer_nid) {
@@ -217,7 +217,7 @@ static bool __parse_nid_path(struct hda_codec *codec,
 		with_aa_mix = HDA_PARSE_ALL; /* mark aa-mix is included */
 	}
 
-	nums = snd_hda_get_connections(codec, to_nid, conn, ARRAY_SIZE(conn));
+	nums = snd_hda_get_conn_list(codec, to_nid, &conn);
 	for (i = 0; i < nums; i++) {
 		if (conn[i] != from_nid) {
 			/* special case: when from_nid is 0,
@@ -481,12 +481,12 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
 			    int i, bool enable, bool add_aamix)
 {
 	struct hda_gen_spec *spec = codec->spec;
-	hda_nid_t conn[16];
+	const hda_nid_t *conn;
 	int n, nums, idx;
 	int type;
 	hda_nid_t nid = path->path[i];
 
-	nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
+	nums = snd_hda_get_conn_list(codec, nid, &conn);
 	type = get_wcaps_type(get_wcaps(codec, nid));
 	if (type == AC_WID_PIN ||
 	    (type == AC_WID_AUD_IN && codec->single_adc_amp)) {
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 083/112] ALSA: hda - Add hooks for HP/line/mic auto switching
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (81 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 082/112] ALSA: hda - Revive snd_hda_get_conn_list() Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 084/112] ALSA: hda - Don't skip amp init for activated paths Takashi Iwai
                   ` (31 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

... as a preliminary work for migrating patch_sigmatel.c.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 6 ++++++
 sound/pci/hda/hda_generic.h | 8 ++++++++
 2 files changed, 14 insertions(+)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index f69f2ab..c5a9b84 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -2778,6 +2778,8 @@ static int check_auto_mute_availability(struct hda_codec *codec)
 		snd_printdd("hda-codec: Enable HP auto-muting on NID 0x%x\n",
 			    nid);
 		snd_hda_jack_detect_enable_callback(codec, nid, HDA_GEN_HP_EVENT,
+						    spec->hp_automute_hook ?
+						    spec->hp_automute_hook :
 						    snd_hda_gen_hp_automute);
 		spec->detect_hp = 1;
 	}
@@ -2791,6 +2793,8 @@ static int check_auto_mute_availability(struct hda_codec *codec)
 				snd_printdd("hda-codec: Enable Line-Out auto-muting on NID 0x%x\n", nid);
 				snd_hda_jack_detect_enable_callback(codec, nid,
 								    HDA_GEN_FRONT_EVENT,
+								    spec->line_automute_hook ?
+								    spec->line_automute_hook :
 								    snd_hda_gen_line_automute);
 				spec->detect_lo = 1;
 			}
@@ -2843,6 +2847,8 @@ static bool auto_mic_check_imux(struct hda_codec *codec)
 		snd_hda_jack_detect_enable_callback(codec,
 						    spec->am_entry[i].pin,
 						    HDA_GEN_MIC_EVENT,
+						    spec->mic_autoswitch_hook ?
+						    spec->mic_autoswitch_hook :
 						    snd_hda_gen_mic_autoswitch);
 	return true;
 }
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index 5c1569c..1090a52 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -180,6 +180,14 @@ struct hda_gen_spec {
 	void (*init_hook)(struct hda_codec *codec);
 	void (*automute_hook)(struct hda_codec *codec);
 	void (*cap_sync_hook)(struct hda_codec *codec);
+
+	/* automute / autoswitch hooks */
+	void (*hp_automute_hook)(struct hda_codec *codec,
+				 struct hda_jack_tbl *tbl);
+	void (*line_automute_hook)(struct hda_codec *codec,
+				   struct hda_jack_tbl *tbl);
+	void (*mic_autoswitch_hook)(struct hda_codec *codec,
+				    struct hda_jack_tbl *tbl);
 };
 
 int snd_hda_gen_spec_init(struct hda_gen_spec *spec);
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 084/112] ALSA: hda - Don't skip amp init for activated paths
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (82 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 083/112] ALSA: hda - Add hooks for HP/line/mic auto switching Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 085/112] ALSA: hda - Initialize output paths with current active states Takashi Iwai
                   ` (30 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

activate_amp() in the generic parser checks whether the given NID is
included in any active paths and skips it if found.  This was a
workaround for avoiding disabling the widgets in the active paths when
one path is disabled, thus it shouldn't be applied to the case for
path activation.  Due to this wrong check, some analog loopback paths
haven't been initialized correctly.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index c5a9b84..08eced0 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -463,7 +463,7 @@ static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir,
 {
 	int val;
 	if (is_ctl_associated(codec, nid, dir, idx) ||
-	    is_active_nid(codec, nid, dir, idx))
+	    (!enable && is_active_nid(codec, nid, dir, idx)))
 		return;
 	val = get_amp_val_to_activate(codec, nid, dir, enable);
 	snd_hda_codec_amp_stereo(codec, nid, dir, idx, 0xff, val);
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 085/112] ALSA: hda - Initialize output paths with current active states
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (83 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 084/112] ALSA: hda - Don't skip amp init for activated paths Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 086/112] ALSA: hda - Avoid duplicated path creations Takashi Iwai
                   ` (29 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

Set path->active flag at the path creation time and let the paths
initialized according to the current path->active state in
set_output_and_unmute().  This allows to modify the active flag of
some output paths dynamically, e.g. switching the front output route
with or without aamix like patch_via.c.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 08eced0..5051350 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -873,8 +873,10 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs,
 		}
 		if (!path)
 			dac = dacs[i] = 0;
-		else
+		else {
 			print_nid_path("output", path);
+			path->active = true;
+		}
 		if (dac)
 			badness += assign_out_path_ctls(codec, pin, dac);
 	}
@@ -1045,6 +1047,7 @@ static bool map_singles(struct hda_codec *codec, int outs,
 			dacs[i] = dac;
 			found = true;
 			print_nid_path("output", path);
+			path->active = true;
 		}
 	}
 	return found;
@@ -2416,6 +2419,7 @@ static void parse_digital(struct hda_codec *codec)
 		if (!path)
 			continue;
 		print_nid_path("digout", path);
+		path->active = true;
 		if (!nums) {
 			spec->multiout.dig_out_nid = dig_nid;
 			spec->dig_out_type = spec->autocfg.dig_out_type[0];
@@ -3554,10 +3558,8 @@ static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin,
 	path = snd_hda_get_nid_path(codec, dac, pin);
 	if (!path)
 		return;
-	if (path->active)
-		return;
-	snd_hda_activate_path(codec, path, true, true);
-	set_pin_eapd(codec, pin, true);
+	snd_hda_activate_path(codec, path, path->active, true);
+	set_pin_eapd(codec, pin, path->active);
 }
 
 /* initialize primary output paths */
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 086/112] ALSA: hda - Avoid duplicated path creations
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (84 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 085/112] ALSA: hda - Initialize output paths with current active states Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 087/112] ALSA: hda - Check the existing path in snd_hda_add_new_path() Takashi Iwai
                   ` (28 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

When the paths are created in map_singles(), we don't have to
re-create new paths in try_assign_dacs().  Just evaluate the badness
and skip to the next item.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 5051350..978a0ed 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -833,8 +833,13 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs,
 	for (i = 0; i < num_outs; i++) {
 		struct nid_path *path;
 		hda_nid_t pin = pins[i];
-		if (!dacs[i])
-			dacs[i] = look_for_dac(codec, pin, false);
+
+		if (dacs[i]) {
+			badness += assign_out_path_ctls(codec, pin, dacs[i]);
+			continue;
+		}
+
+		dacs[i] = look_for_dac(codec, pin, false);
 		if (!dacs[i] && !i) {
 			for (j = 1; j < num_outs; j++) {
 				if (is_reachable_path(codec, dacs[j], pin)) {
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 087/112] ALSA: hda - Check the existing path in snd_hda_add_new_path()
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (85 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 086/112] ALSA: hda - Avoid duplicated path creations Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 088/112] ALSA: hda - Simplify the multi-io assignment with multi speakers Takashi Iwai
                   ` (27 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

If the requested path has been already added, return the existing path
instance instead of adding a duplicated instance.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 31 ++++++++++++++++++++++++-------
 sound/pci/hda/hda_generic.h |  1 +
 2 files changed, 25 insertions(+), 7 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 978a0ed..5516905 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -116,11 +116,9 @@ EXPORT_SYMBOL_HDA(snd_hda_gen_spec_free);
  * parsing paths
  */
 
-/* get the path between the given NIDs;
- * passing 0 to either @pin or @dac behaves as a wildcard
- */
-struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec,
-				      hda_nid_t from_nid, hda_nid_t to_nid)
+static struct nid_path *get_nid_path(struct hda_codec *codec,
+				     hda_nid_t from_nid, hda_nid_t to_nid,
+				     int with_aa_mix)
 {
 	struct hda_gen_spec *spec = codec->spec;
 	int i;
@@ -130,11 +128,23 @@ struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec,
 		if (path->depth <= 0)
 			continue;
 		if ((!from_nid || path->path[0] == from_nid) &&
-		    (!to_nid || path->path[path->depth - 1] == to_nid))
-			return path;
+		    (!to_nid || path->path[path->depth - 1] == to_nid)) {
+			if (with_aa_mix == HDA_PARSE_ALL ||
+			    path->with_aa_mix == with_aa_mix)
+				return path;
+		}
 	}
 	return NULL;
 }
+
+/* get the path between the given NIDs;
+ * passing 0 to either @pin or @dac behaves as a wildcard
+ */
+struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec,
+				      hda_nid_t from_nid, hda_nid_t to_nid)
+{
+	return get_nid_path(codec, from_nid, to_nid, HDA_PARSE_ALL);
+}
 EXPORT_SYMBOL_HDA(snd_hda_get_nid_path);
 
 /* check whether the given DAC is already found in any existing paths */
@@ -248,6 +258,8 @@ static bool __parse_nid_path(struct hda_codec *codec,
 
  found:
 	path->path[path->depth] = conn[i];
+	if (conn[i] == spec->mixer_nid)
+		path->with_aa_mix = true;
 	path->idx[path->depth + 1] = i;
 	if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX)
 		path->multi[path->depth + 1] = 1;
@@ -290,6 +302,11 @@ snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid,
 	if (from_nid && to_nid && !is_reachable_path(codec, from_nid, to_nid))
 		return NULL;
 
+	/* check whether the path has been already added */
+	path = get_nid_path(codec, from_nid, to_nid, with_aa_mix);
+	if (path)
+		return path;
+
 	path = snd_array_new(&spec->paths);
 	if (!path)
 		return NULL;
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index 1090a52..f1cae2e 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -53,6 +53,7 @@ struct nid_path {
 	unsigned char multi[MAX_NID_PATH_DEPTH];
 	unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */
 	bool active;
+	bool with_aa_mix;
 };
 
 /* mic/line-in auto switching entry */
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 088/112] ALSA: hda - Simplify the multi-io assignment with multi speakers
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (86 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 087/112] ALSA: hda - Check the existing path in snd_hda_add_new_path() Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 089/112] ALSA: hda - Fix multi-io pin assignment in create_multi_out_ctls() Takashi Iwai
                   ` (26 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

When speakers are chosen as the the primary output during evaluation,
we did some tricks to assign the possible multi-io jacks with a
certain offset value to multi_out dacs.  This was a workaround for the
case with multiple speakers like Acer Aspire.  But this is quite ugly
at the same time and the resultant code is hard to understand.  More
badly, it works wrongly for 2.1 speakers like Apple iMac91.

In this patch, instead of fiddling with the offset to multi_out dacs,
simply add a certain badness number if headphone(s) + multi-ios are
possible.  This simplify the code a bit, and it's more robust.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 63 +++++++++++++++++++++++----------------------
 1 file changed, 32 insertions(+), 31 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 5516905..c928b38 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -943,6 +943,28 @@ static bool can_be_multiio_pin(struct hda_codec *codec,
 	return true;
 }
 
+/* count the number of input pins that are capable to be multi-io */
+static int count_multiio_pins(struct hda_codec *codec, hda_nid_t reference_pin)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin);
+	unsigned int location = get_defcfg_location(defcfg);
+	int type, i;
+	int num_pins = 0;
+
+	for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
+		for (i = 0; i < cfg->num_inputs; i++) {
+			if (cfg->inputs[i].type != type)
+				continue;
+			if (can_be_multiio_pin(codec, location,
+					       cfg->inputs[i].pin))
+				num_pins++;
+		}
+	}
+	return num_pins;
+}
+
 /*
  * multi-io helper
  *
@@ -953,11 +975,11 @@ static bool can_be_multiio_pin(struct hda_codec *codec,
  */
 static int fill_multi_ios(struct hda_codec *codec,
 			  hda_nid_t reference_pin,
-			  bool hardwired, int offset)
+			  bool hardwired)
 {
 	struct hda_gen_spec *spec = codec->spec;
 	struct auto_pin_cfg *cfg = &spec->autocfg;
-	int type, i, j, dacs, num_pins, old_pins;
+	int type, i, j, num_pins, old_pins;
 	unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin);
 	unsigned int location = get_defcfg_location(defcfg);
 	int badness = 0;
@@ -966,20 +988,10 @@ static int fill_multi_ios(struct hda_codec *codec,
 	if (old_pins >= 2)
 		goto end_fill;
 
-	num_pins = 0;
-	for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
-		for (i = 0; i < cfg->num_inputs; i++) {
-			if (cfg->inputs[i].type != type)
-				continue;
-			if (can_be_multiio_pin(codec, location,
-					       cfg->inputs[i].pin))
-				num_pins++;
-		}
-	}
+	num_pins = count_multiio_pins(codec, reference_pin);
 	if (num_pins < 2)
 		goto end_fill;
 
-	dacs = spec->multiout.num_dacs;
 	for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
 		for (i = 0; i < cfg->num_inputs; i++) {
 			struct nid_path *path;
@@ -997,11 +1009,6 @@ static int fill_multi_ios(struct hda_codec *codec,
 			if (j < spec->multi_ios)
 				continue;
 
-			if (offset && offset + spec->multi_ios < dacs) {
-				dac = spec->private_dac_nids[offset + spec->multi_ios];
-				if (!is_reachable_path(codec, dac, nid))
-					dac = 0;
-			}
 			if (hardwired)
 				dac = get_dac_if_single(codec, nid);
 			else if (!dac)
@@ -1109,7 +1116,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
 					      spec->multiout.extra_out_nid);
 			if (fill_mio_first && cfg->line_outs == 1 &&
 			    cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
-				err = fill_multi_ios(codec, cfg->line_out_pins[0], true, 0);
+				err = fill_multi_ios(codec, cfg->line_out_pins[0], true);
 				if (!err)
 					mapped = true;
 			}
@@ -1136,7 +1143,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
 	if (fill_mio_first &&
 	    cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
 		/* try to fill multi-io first */
-		err = fill_multi_ios(codec, cfg->line_out_pins[0], false, 0);
+		err = fill_multi_ios(codec, cfg->line_out_pins[0], false);
 		if (err < 0)
 			return err;
 		/* we don't count badness at this stage yet */
@@ -1160,22 +1167,16 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
 		badness += err;
 	}
 	if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
-		err = fill_multi_ios(codec, cfg->line_out_pins[0], false, 0);
-		if (err < 0)
-			return err;
-		badness += err;
-	}
-	if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
-		/* try multi-ios with HP + inputs */
-		int offset = 0;
-		if (cfg->line_outs >= 3)
-			offset = 1;
-		err = fill_multi_ios(codec, cfg->hp_pins[0], false, offset);
+		err = fill_multi_ios(codec, cfg->line_out_pins[0], false);
 		if (err < 0)
 			return err;
 		badness += err;
 	}
 
+	if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+		if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2)
+			spec->multi_ios = 1; /* give badness */
+
 	if (spec->multi_ios == 2) {
 		for (i = 0; i < 2; i++)
 			spec->private_dac_nids[spec->multiout.num_dacs++] =
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 089/112] ALSA: hda - Fix multi-io pin assignment in create_multi_out_ctls()
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (87 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 088/112] ALSA: hda - Simplify the multi-io assignment with multi speakers Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 090/112] ALSA: hda - Manage using output/loopback path indices Takashi Iwai
                   ` (25 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

The multi-io pins are calculated with a blind assumption of
cfg->line_outs = 1.  This isn't always true.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index c928b38..75a519c 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -1368,7 +1368,7 @@ static int create_multi_out_ctls(struct hda_codec *codec,
 		if (!dac)
 			continue;
 		if (i >= cfg->line_outs) {
-			pin = spec->multi_io[i - 1].pin;
+			pin = spec->multi_io[i - cfg->line_outs].pin;
 			index = 0;
 			name = channel_name[i];
 		} else {
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 090/112] ALSA: hda - Manage using output/loopback path indices
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (88 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 089/112] ALSA: hda - Fix multi-io pin assignment in create_multi_out_ctls() Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 091/112] ALSA: hda - Initialize digital-input path properly Takashi Iwai
                   ` (24 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

Instead of search for the path with the certain route at each time,
keep the path index for each output and loopback, and just use it when
referred.

In this implementation, the path index number begins with one, not
zero (although I've been writing in C over decades).  It's just to
make the check for uninitialized values easier.

So far, the input paths aren't handled with indices yet, but still
picked up via snd_hda_get_nid_path() at each time.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 139 ++++++++++++++++++++++++++++----------------
 sound/pci/hda/hda_generic.h |   9 +++
 2 files changed, 98 insertions(+), 50 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 75a519c..2296839 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -147,6 +147,33 @@ struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_get_nid_path);
 
+/* get the index number corresponding to the path instance;
+ * the index starts from 1, for easier checking the invalid value
+ */
+int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	struct nid_path *array = spec->paths.list;
+	ssize_t idx;
+
+	if (!spec->paths.used)
+		return 0;
+	idx = path - array;
+	if (idx < 0 || idx >= spec->paths.used)
+		return 0;
+	return idx + 1;
+}
+
+/* get the path instance corresponding to the given index number */
+struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx)
+{
+	struct hda_gen_spec *spec = codec->spec;
+
+	if (idx <= 0 || idx > spec->paths.used)
+		return NULL;
+	return snd_array_elem(&spec->paths, idx - 1);
+}
+
 /* check whether the given DAC is already found in any existing paths */
 static bool is_dac_already_used(struct hda_codec *codec, hda_nid_t nid)
 {
@@ -836,6 +863,7 @@ static struct badness_table extra_out_badness = {
 /* try to assign DACs to pins and return the resultant badness */
 static int try_assign_dacs(struct hda_codec *codec, int num_outs,
 			   const hda_nid_t *pins, hda_nid_t *dacs,
+			   int *path_idx,
 			   const struct badness_table *bad)
 {
 	struct hda_gen_spec *spec = codec->spec;
@@ -862,6 +890,7 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs,
 				if (is_reachable_path(codec, dacs[j], pin)) {
 					dacs[0] = dacs[j];
 					dacs[j] = 0;
+					path_idx[j] = 0;
 					break;
 				}
 			}
@@ -898,6 +927,7 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs,
 		else {
 			print_nid_path("output", path);
 			path->active = true;
+			path_idx[i] = snd_hda_get_path_idx(codec, path);
 		}
 		if (dac)
 			badness += assign_out_path_ctls(codec, pin, dac);
@@ -1025,6 +1055,8 @@ static int fill_multi_ios(struct hda_codec *codec,
 			print_nid_path("multiio", path);
 			spec->multi_io[spec->multi_ios].pin = nid;
 			spec->multi_io[spec->multi_ios].dac = dac;
+			spec->out_paths[cfg->line_outs + spec->multi_ios] =
+				snd_hda_get_path_idx(codec, path);
 			spec->multi_ios++;
 			if (spec->multi_ios >= 2)
 				break;
@@ -1056,7 +1088,7 @@ static int fill_multi_ios(struct hda_codec *codec,
 
 /* map DACs for all pins in the list if they are single connections */
 static bool map_singles(struct hda_codec *codec, int outs,
-			const hda_nid_t *pins, hda_nid_t *dacs)
+			const hda_nid_t *pins, hda_nid_t *dacs, int *path_idx)
 {
 	struct hda_gen_spec *spec = codec->spec;
 	int i;
@@ -1077,6 +1109,7 @@ static bool map_singles(struct hda_codec *codec, int outs,
 			found = true;
 			print_nid_path("output", path);
 			path->active = true;
+			path_idx[i] = snd_hda_get_path_idx(codec, path);
 		}
 	}
 	return found;
@@ -1107,13 +1140,16 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
 		do {
 			mapped = map_singles(codec, cfg->line_outs,
 					     cfg->line_out_pins,
-					     spec->private_dac_nids);
+					     spec->private_dac_nids,
+					     spec->out_paths);
 			mapped |= map_singles(codec, cfg->hp_outs,
 					      cfg->hp_pins,
-					      spec->multiout.hp_out_nid);
+					      spec->multiout.hp_out_nid,
+					      spec->hp_paths);
 			mapped |= map_singles(codec, cfg->speaker_outs,
 					      cfg->speaker_pins,
-					      spec->multiout.extra_out_nid);
+					      spec->multiout.extra_out_nid,
+					      spec->speaker_paths);
 			if (fill_mio_first && cfg->line_outs == 1 &&
 			    cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
 				err = fill_multi_ios(codec, cfg->line_out_pins[0], true);
@@ -1124,7 +1160,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
 	}
 
 	badness += try_assign_dacs(codec, cfg->line_outs, cfg->line_out_pins,
-				   spec->private_dac_nids,
+				   spec->private_dac_nids, spec->out_paths,
 				   &main_out_badness);
 
 	/* re-count num_dacs and squash invalid entries */
@@ -1152,6 +1188,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
 	if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
 		err = try_assign_dacs(codec, cfg->hp_outs, cfg->hp_pins,
 				      spec->multiout.hp_out_nid,
+				      spec->hp_paths,
 				      &extra_out_badness);
 		if (err < 0)
 			return err;
@@ -1161,7 +1198,8 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
 		err = try_assign_dacs(codec, cfg->speaker_outs,
 				      cfg->speaker_pins,
 				      spec->multiout.extra_out_nid,
-					 &extra_out_badness);
+				      spec->speaker_paths,
+				      &extra_out_badness);
 		if (err < 0)
 			return err;
 		badness += err;
@@ -1336,9 +1374,7 @@ static int parse_output_paths(struct hda_codec *codec)
 
 	if (cfg->line_out_pins[0]) {
 		struct nid_path *path;
-		path = snd_hda_get_nid_path(codec,
-					    spec->multiout.dac_nids[0],
-					    cfg->line_out_pins[0]);
+		path = snd_hda_get_path_from_idx(codec, spec->out_paths[0]);
 		if (path)
 			spec->vmaster_nid = look_for_out_vol_nid(codec, path);
 	}
@@ -1361,22 +1397,20 @@ static int create_multi_out_ctls(struct hda_codec *codec,
 	for (i = 0; i < noutputs; i++) {
 		const char *name;
 		int index;
-		hda_nid_t dac, pin;
+		hda_nid_t dac;
 		struct nid_path *path;
 
 		dac = spec->multiout.dac_nids[i];
 		if (!dac)
 			continue;
 		if (i >= cfg->line_outs) {
-			pin = spec->multi_io[i - cfg->line_outs].pin;
 			index = 0;
 			name = channel_name[i];
 		} else {
-			pin = cfg->line_out_pins[i];
 			name = get_line_out_pfx(spec, i, true, &index);
 		}
 
-		path = snd_hda_get_nid_path(codec, dac, pin);
+		path = snd_hda_get_path_from_idx(codec, spec->out_paths[i]);
 		if (!path)
 			continue;
 		if (!name || !strcmp(name, "CLFE")) {
@@ -1406,12 +1440,13 @@ static int create_multi_out_ctls(struct hda_codec *codec,
 }
 
 static int create_extra_out(struct hda_codec *codec, hda_nid_t pin,
-			    hda_nid_t dac, const char *pfx, int cidx)
+			    hda_nid_t dac, int path_idx,
+			    const char *pfx, int cidx)
 {
 	struct nid_path *path;
 	int err;
 
-	path = snd_hda_get_nid_path(codec, dac, pin);
+	path = snd_hda_get_path_from_idx(codec, path_idx);
 	if (!path)
 		return 0;
 	/* bind volume control will be created in the case of dac = 0 */
@@ -1429,7 +1464,7 @@ static int create_extra_out(struct hda_codec *codec, hda_nid_t pin,
 /* add playback controls for speaker and HP outputs */
 static int create_extra_outs(struct hda_codec *codec, int num_pins,
 			     const hda_nid_t *pins, const hda_nid_t *dacs,
-			     const char *pfx)
+			     const int *paths, const char *pfx)
 {
 	struct hda_gen_spec *spec = codec->spec;
 	struct hda_bind_ctls *ctl;
@@ -1443,7 +1478,7 @@ static int create_extra_outs(struct hda_codec *codec, int num_pins,
 		hda_nid_t dac = *dacs;
 		if (!dac)
 			dac = spec->multiout.dac_nids[0];
-		return create_extra_out(codec, *pins, dac, pfx, 0);
+		return create_extra_out(codec, *pins, dac, paths[0], pfx, 0);
 	}
 
 	for (i = 0; i < num_pins; i++) {
@@ -1453,14 +1488,16 @@ static int create_extra_outs(struct hda_codec *codec, int num_pins,
 		else
 			dac = 0;
 		if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) {
-			err = create_extra_out(codec, pins[i], dac,
+			err = create_extra_out(codec, pins[i], dac, paths[i],
 					       "Bass Speaker", 0);
 		} else if (num_pins >= 3) {
 			snprintf(name, sizeof(name), "%s %s",
 				 pfx, channel_name[i]);
-			err = create_extra_out(codec, pins[i], dac, name, 0);
+			err = create_extra_out(codec, pins[i], dac, paths[i],
+					       name, 0);
 		} else {
-			err = create_extra_out(codec, pins[i], dac, pfx, i);
+			err = create_extra_out(codec, pins[i], dac, paths[i],
+					       pfx, i);
 		}
 		if (err < 0)
 			return err;
@@ -1478,7 +1515,7 @@ static int create_extra_outs(struct hda_codec *codec, int num_pins,
 		struct nid_path *path;
 		if (!pins[i] || !dacs[i])
 			continue;
-		path = snd_hda_get_nid_path(codec, dacs[i], pins[i]);
+		path = snd_hda_get_path_from_idx(codec, paths[i]);
 		if (!path)
 			continue;
 		vol = look_for_out_vol_nid(codec, path);
@@ -1501,6 +1538,7 @@ static int create_hp_out_ctls(struct hda_codec *codec)
 	return create_extra_outs(codec, spec->autocfg.hp_outs,
 				 spec->autocfg.hp_pins,
 				 spec->multiout.hp_out_nid,
+				 spec->hp_paths,
 				 "Headphone");
 }
 
@@ -1510,6 +1548,7 @@ static int create_speaker_out_ctls(struct hda_codec *codec)
 	return create_extra_outs(codec, spec->autocfg.speaker_outs,
 				 spec->autocfg.speaker_pins,
 				 spec->multiout.extra_out_nid,
+				 spec->speaker_paths,
 				 "Speaker");
 }
 
@@ -1613,13 +1652,21 @@ static int ch_mode_get(struct snd_kcontrol *kcontrol,
 	return 0;
 }
 
+static inline struct nid_path *
+get_multiio_path(struct hda_codec *codec, int idx)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	return snd_hda_get_path_from_idx(codec,
+		spec->out_paths[spec->autocfg.line_outs + idx]);
+}
+
 static int set_multi_io(struct hda_codec *codec, int idx, bool output)
 {
 	struct hda_gen_spec *spec = codec->spec;
 	hda_nid_t nid = spec->multi_io[idx].pin;
 	struct nid_path *path;
 
-	path = snd_hda_get_nid_path(codec, spec->multi_io[idx].dac, nid);
+	path = get_multiio_path(codec, idx);
 	if (!path)
 		return -EINVAL;
 
@@ -1773,8 +1820,8 @@ static void add_loopback_list(struct hda_gen_spec *spec, hda_nid_t mix, int idx)
 #endif
 
 /* create input playback/capture controls for the given pin */
-static int new_analog_input(struct hda_codec *codec, hda_nid_t pin,
-			    const char *ctlname, int ctlidx,
+static int new_analog_input(struct hda_codec *codec, int input_idx,
+			    hda_nid_t pin, const char *ctlname, int ctlidx,
 			    hda_nid_t mix_nid)
 {
 	struct hda_gen_spec *spec = codec->spec;
@@ -1790,6 +1837,7 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin,
 	if (!path)
 		return -EINVAL;
 	print_nid_path("loopback", path);
+	spec->loopback_paths[input_idx] = snd_hda_get_path_idx(codec, path);
 
 	idx = path->idx[path->depth - 1];
 	if (nid_has_volume(codec, mix_nid, HDA_INPUT)) {
@@ -1942,7 +1990,7 @@ static int create_input_ctls(struct hda_codec *codec)
 
 		if (mixer) {
 			if (is_reachable_path(codec, pin, mixer)) {
-				err = new_analog_input(codec, pin,
+				err = new_analog_input(codec, i, pin,
 						       label, type_idx, mixer);
 				if (err < 0)
 					return err;
@@ -2443,6 +2491,7 @@ static void parse_digital(struct hda_codec *codec)
 			continue;
 		print_nid_path("digout", path);
 		path->active = true;
+		spec->digout_paths[i] = snd_hda_get_path_idx(codec, path);
 		if (!nums) {
 			spec->multiout.dig_out_nid = dig_nid;
 			spec->dig_out_type = spec->autocfg.dig_out_type[0];
@@ -3573,12 +3622,12 @@ EXPORT_SYMBOL_HDA(snd_hda_gen_build_pcms);
 
 /* configure the path from the given dac to the pin as the proper output */
 static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin,
-				  int pin_type, hda_nid_t dac)
+				  int pin_type, int path_idx)
 {
 	struct nid_path *path;
 
 	snd_hda_set_pin_ctl_cache(codec, pin, pin_type);
-	path = snd_hda_get_nid_path(codec, dac, pin);
+	path = snd_hda_get_path_from_idx(codec, path_idx);
 	if (!path)
 		return;
 	snd_hda_activate_path(codec, path, path->active, true);
@@ -3589,7 +3638,7 @@ static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin,
 static void init_multi_out(struct hda_codec *codec)
 {
 	struct hda_gen_spec *spec = codec->spec;
-	hda_nid_t nid, dac;
+	hda_nid_t nid;
 	int pin_type;
 	int i;
 
@@ -3600,35 +3649,24 @@ static void init_multi_out(struct hda_codec *codec)
 
 	for (i = 0; i < spec->autocfg.line_outs; i++) {
 		nid = spec->autocfg.line_out_pins[i];
-		if (nid) {
-			dac = spec->multiout.dac_nids[i];
-			if (!dac)
-				dac = spec->multiout.dac_nids[0];
-			set_output_and_unmute(codec, nid, pin_type, dac);
-		}
+		if (nid)
+			set_output_and_unmute(codec, nid, pin_type,
+					      spec->out_paths[i]);
 	}
 }
 
 
 static void __init_extra_out(struct hda_codec *codec, int num_outs,
-			     hda_nid_t *pins, hda_nid_t *dacs, int type)
+			     hda_nid_t *pins, int *paths, int type)
 {
-	struct hda_gen_spec *spec = codec->spec;
 	int i;
-	hda_nid_t pin, dac;
+	hda_nid_t pin;
 
 	for (i = 0; i < num_outs; i++) {
 		pin = pins[i];
 		if (!pin)
 			break;
-		dac = dacs[i];
-		if (!dac) {
-			if (i > 0 && dacs[0])
-				dac = dacs[0];
-			else
-				dac = spec->multiout.dac_nids[0];
-		}
-		set_output_and_unmute(codec, pin, type, dac);
+		set_output_and_unmute(codec, pin, type, paths[i]);
 	}
 }
 
@@ -3640,11 +3678,11 @@ static void init_extra_out(struct hda_codec *codec)
 	if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT)
 		__init_extra_out(codec, spec->autocfg.hp_outs,
 				 spec->autocfg.hp_pins,
-				 spec->multiout.hp_out_nid, PIN_HP);
+				 spec->hp_paths, PIN_HP);
 	if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT)
 		__init_extra_out(codec, spec->autocfg.speaker_outs,
 				 spec->autocfg.speaker_pins,
-				 spec->multiout.extra_out_nid, PIN_OUT);
+				 spec->speaker_paths, PIN_OUT);
 }
 
 /* initialize multi-io paths */
@@ -3656,7 +3694,7 @@ static void init_multi_io(struct hda_codec *codec)
 	for (i = 0; i < spec->multi_ios; i++) {
 		hda_nid_t pin = spec->multi_io[i].pin;
 		struct nid_path *path;
-		path = snd_hda_get_nid_path(codec, spec->multi_io[i].dac, pin);
+		path = get_multiio_path(codec, i);
 		if (!path)
 			continue;
 		if (!spec->multi_io[i].ctl_in)
@@ -3692,7 +3730,7 @@ static void init_analog_input(struct hda_codec *codec)
 		/* init loopback inputs */
 		if (spec->mixer_nid) {
 			struct nid_path *path;
-			path = snd_hda_get_nid_path(codec, nid, spec->mixer_nid);
+			path = snd_hda_get_path_from_idx(codec, spec->loopback_paths[i]);
 			if (path)
 				snd_hda_activate_path(codec, path,
 						      path->active, false);
@@ -3744,7 +3782,8 @@ static void init_digital(struct hda_codec *codec)
 		pin = spec->autocfg.dig_out_pins[i];
 		if (!pin)
 			continue;
-		set_output_and_unmute(codec, pin, PIN_OUT, 0);
+		set_output_and_unmute(codec, pin, PIN_OUT,
+				      spec->digout_paths[i]);
 	}
 	pin = spec->autocfg.dig_in_pin;
 	if (pin)
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index f1cae2e..71d409f 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -130,6 +130,13 @@ struct hda_gen_spec {
 	/* path list */
 	struct snd_array paths;
 
+	/* path indices */
+	int out_paths[AUTO_CFG_MAX_OUTS];
+	int hp_paths[AUTO_CFG_MAX_OUTS];
+	int speaker_paths[AUTO_CFG_MAX_OUTS];
+	int digout_paths[AUTO_CFG_MAX_OUTS];
+	int loopback_paths[HDA_MAX_NUM_INPUTS];
+
 	/* auto-mic stuff */
 	int am_num_entries;
 	struct automic_entry am_entry[MAX_AUTO_MIC_PINS];
@@ -198,6 +205,8 @@ int snd_hda_gen_init(struct hda_codec *codec);
 
 struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec,
 				      hda_nid_t from_nid, hda_nid_t to_nid);
+int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path);
+struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx);
 
 enum {
 	HDA_PARSE_NO_AAMIX,
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 091/112] ALSA: hda - Initialize digital-input path properly
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (89 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 090/112] ALSA: hda - Manage using output/loopback path indices Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 092/112] ALSA: hda - Correct aamix output paths Takashi Iwai
                   ` (23 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

Call the path activation for the digital input pin properly, not only
setting the pin control.  Also add spec->digin_path for keeping the
path index.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 8 +++++++-
 sound/pci/hda/hda_generic.h | 1 +
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 2296839..65883c7 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -2519,6 +2519,7 @@ static void parse_digital(struct hda_codec *codec)
 				print_nid_path("digin", path);
 				path->active = true;
 				spec->dig_in_nid = dig_nid;
+				spec->digin_path = snd_hda_get_path_idx(codec, path);
 				break;
 			}
 		}
@@ -3786,8 +3787,13 @@ static void init_digital(struct hda_codec *codec)
 				      spec->digout_paths[i]);
 	}
 	pin = spec->autocfg.dig_in_pin;
-	if (pin)
+	if (pin) {
+		struct nid_path *path;
 		snd_hda_set_pin_ctl_cache(codec, pin, PIN_IN);
+		path = snd_hda_get_path_from_idx(codec, spec->digin_path);
+		if (path)
+			snd_hda_activate_path(codec, path, path->active, false);
+	}
 }
 
 /* clear unsol-event tags on unused pins; Conexant codecs seem to leave
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index 71d409f..ba8de12 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -136,6 +136,7 @@ struct hda_gen_spec {
 	int speaker_paths[AUTO_CFG_MAX_OUTS];
 	int digout_paths[AUTO_CFG_MAX_OUTS];
 	int loopback_paths[HDA_MAX_NUM_INPUTS];
+	int digin_path;
 
 	/* auto-mic stuff */
 	int am_num_entries;
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 092/112] ALSA: hda - Correct aamix output paths
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (90 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 091/112] ALSA: hda - Initialize digital-input path properly Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 093/112] ALSA: hda - Add Loopback Mixing control Takashi Iwai
                   ` (22 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

The output paths including aamix should be parsed only for the first
output.  The surround paths including aamix must be wrong, since it
would mix all streams, i.e. all channels would be mixed into a single
and multiplexed again.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 65883c7..774c092 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -918,7 +918,7 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs,
 				badness += bad->no_dac;
 		}
 		path = snd_hda_add_new_path(codec, dac, pin, HDA_PARSE_NO_AAMIX);
-		if (!path && i > 0 && spec->mixer_nid) {
+		if (!path && !i && spec->mixer_nid) {
 			/* try with aamix */
 			path = snd_hda_add_new_path(codec, dac, pin, HDA_PARSE_ALL);
 		}
@@ -1102,7 +1102,7 @@ static bool map_singles(struct hda_codec *codec, int outs,
 		if (!dac)
 			continue;
 		path = snd_hda_add_new_path(codec, dac, pins[i], HDA_PARSE_NO_AAMIX);
-		if (!path && i > 0 && spec->mixer_nid)
+		if (!path && !i && spec->mixer_nid)
 			path = snd_hda_add_new_path(codec, dac, pins[i], HDA_PARSE_ALL);
 		if (path) {
 			dacs[i] = dac;
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 093/112] ALSA: hda - Add Loopback Mixing control
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (91 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 092/112] ALSA: hda - Correct aamix output paths Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 094/112] ALSA: hda - Fix truncated control names Takashi Iwai
                   ` (21 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

For codecs that have individual routes going through a loopback mixer
(like VIA codecs), we need to provide an explicit switch to choose
whether the output goes through mixer or directly from DAC.

This patch adds the check for such paths and creates "Loopback Mixing"
enum control when available.

It won't influence on codecs like Realtek or others where the loopback
mixer is connected independently from the primary output routes.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 106 ++++++++++++++++++++++++++++++++++++++++++++
 sound/pci/hda/hda_generic.h |   4 ++
 2 files changed, 110 insertions(+)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 774c092..5b30f9f 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -1115,6 +1115,24 @@ static bool map_singles(struct hda_codec *codec, int outs,
 	return found;
 }
 
+/* create a new path including aamix if available, and return its index */
+static int check_aamix_out_path(struct hda_codec *codec, int path_idx)
+{
+	struct nid_path *path;
+
+	path = snd_hda_get_path_from_idx(codec, path_idx);
+	if (!path || !path->depth || path->with_aa_mix)
+		return 0;
+	path = snd_hda_add_new_path(codec, path->path[0],
+				    path->path[path->depth - 1],
+				    HDA_PARSE_ONLY_AAMIX);
+	if (!path)
+		return 0;
+	print_nid_path("output-aamix", path);
+	path->active = false; /* unused as default */
+	return snd_hda_get_path_idx(codec, path);
+}
+
 /* fill in the dac_nids table from the parsed pin configuration */
 static int fill_and_eval_dacs(struct hda_codec *codec,
 			      bool fill_hardwired,
@@ -1211,6 +1229,17 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
 		badness += err;
 	}
 
+	if (spec->mixer_nid) {
+		spec->aamix_out_paths[0] =
+			check_aamix_out_path(codec, spec->out_paths[0]);
+		if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+			spec->aamix_out_paths[1] =
+				check_aamix_out_path(codec, spec->hp_paths[0]);
+		if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+			spec->aamix_out_paths[2] =
+				check_aamix_out_path(codec, spec->speaker_paths[0]);
+	}
+
 	if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
 		if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2)
 			spec->multi_ios = 1; /* give badness */
@@ -1728,6 +1757,80 @@ static int create_multi_channel_mode(struct hda_codec *codec)
 }
 
 /*
+ * aamix loopback enable/disable switch
+ */
+
+#define loopback_mixing_info	indep_hp_info
+
+static int loopback_mixing_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct hda_gen_spec *spec = codec->spec;
+	ucontrol->value.enumerated.item[0] = spec->aamix_mode;
+	return 0;
+}
+
+static void update_aamix_paths(struct hda_codec *codec, bool do_mix,
+			       int nomix_path_idx, int mix_path_idx)
+{
+	struct nid_path *nomix_path, *mix_path;
+
+	nomix_path = snd_hda_get_path_from_idx(codec, nomix_path_idx);
+	mix_path = snd_hda_get_path_from_idx(codec, mix_path_idx);
+	if (!nomix_path || !mix_path)
+		return;
+	if (do_mix) {
+		snd_hda_activate_path(codec, nomix_path, false, true);
+		snd_hda_activate_path(codec, mix_path, true, true);
+	} else {
+		snd_hda_activate_path(codec, mix_path, false, true);
+		snd_hda_activate_path(codec, nomix_path, true, true);
+	}
+}
+
+static int loopback_mixing_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct hda_gen_spec *spec = codec->spec;
+	unsigned int val = ucontrol->value.enumerated.item[0];
+
+	if (val == spec->aamix_mode)
+		return 0;
+	spec->aamix_mode = val;
+	update_aamix_paths(codec, val, spec->out_paths[0],
+			   spec->aamix_out_paths[0]);
+	update_aamix_paths(codec, val, spec->hp_paths[0],
+			   spec->aamix_out_paths[1]);
+	update_aamix_paths(codec, val, spec->speaker_paths[0],
+			   spec->aamix_out_paths[2]);
+	return 1;
+}
+
+static const struct snd_kcontrol_new loopback_mixing_enum = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Loopback Mixing",
+	.info = loopback_mixing_info,
+	.get = loopback_mixing_get,
+	.put = loopback_mixing_put,
+};
+
+static int create_loopback_mixing_ctl(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+
+	if (!spec->mixer_nid)
+		return 0;
+	if (!(spec->aamix_out_paths[0] || spec->aamix_out_paths[1] ||
+	      spec->aamix_out_paths[2]))
+		return 0;
+	if (!snd_hda_gen_add_kctl(spec, NULL, &loopback_mixing_enum))
+		return -ENOMEM;
+	return 0;
+}
+
+/*
  * shared headphone/mic handling
  */
 
@@ -3065,6 +3168,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
 	err = create_indep_hp_ctls(codec);
 	if (err < 0)
 		return err;
+	err = create_loopback_mixing_ctl(codec);
+	if (err < 0)
+		return err;
 	err = create_shared_input(codec);
 	if (err < 0)
 		return err;
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index ba8de12..d4a8f6c 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -134,6 +134,7 @@ struct hda_gen_spec {
 	int out_paths[AUTO_CFG_MAX_OUTS];
 	int hp_paths[AUTO_CFG_MAX_OUTS];
 	int speaker_paths[AUTO_CFG_MAX_OUTS];
+	int aamix_out_paths[3];
 	int digout_paths[AUTO_CFG_MAX_OUTS];
 	int loopback_paths[HDA_MAX_NUM_INPUTS];
 	int digin_path;
@@ -169,6 +170,9 @@ struct hda_gen_spec {
 	unsigned int indep_hp:1; /* independent HP supported */
 	unsigned int indep_hp_enabled:1; /* independent HP enabled */
 
+	/* loopback mixing mode */
+	bool aamix_mode;
+
 	/* for virtual master */
 	hda_nid_t vmaster_nid;
 	struct hda_vmaster_mute_hook vmaster_mute;
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 094/112] ALSA: hda - Fix truncated control names
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (92 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 093/112] ALSA: hda - Add Loopback Mixing control Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 095/112] ALSA: hda - Prefer binding the primary CLFE output Takashi Iwai
                   ` (20 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

... like "Speaker Surround Playback Switch".
This fix had been already applied to patch_conexant.c but was
forgotten in other places, then migrated to hda_generic.c.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 5b30f9f..2b33d50 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -1497,7 +1497,7 @@ static int create_extra_outs(struct hda_codec *codec, int num_pins,
 {
 	struct hda_gen_spec *spec = codec->spec;
 	struct hda_bind_ctls *ctl;
-	char name[32];
+	char name[44];
 	int i, n, err;
 
 	if (!num_pins || !pins[0])
@@ -2543,7 +2543,7 @@ static int parse_mic_boost(struct hda_codec *codec)
 		nid = cfg->inputs[i].pin;
 		if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) {
 			const char *label;
-			char boost_label[32];
+			char boost_label[44];
 			struct nid_path *path;
 			unsigned int val;
 
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 095/112] ALSA: hda - Prefer binding the primary CLFE output
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (93 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 094/112] ALSA: hda - Fix truncated control names Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 096/112] ALSA: hda - Add missing slave names for Speaker Surround, etc Takashi Iwai
                   ` (19 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

When 5.1 or more multiple speakers with found but not enough DACs are
available, it's better to bind such pins to the DACs of the primary
outputs with the same channels rather than binding to the first DAC
(i.e. the front channel).  For the cases with two speaker pins, it's
rather regarded as front + bass combination, thus it's more practical
to still bind to the front, though.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 33 +++++++++++++++++++++++++++------
 1 file changed, 27 insertions(+), 6 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 2b33d50..fa8302f 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -860,6 +860,27 @@ static struct badness_table extra_out_badness = {
 	.shared_surr_main = BAD_NO_EXTRA_SURR_DAC,
 };
 
+/* get the DAC of the primary output corresponding to the given array index */
+static hda_nid_t get_primary_out(struct hda_codec *codec, int idx)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+
+	if (cfg->line_outs > idx)
+		return spec->private_dac_nids[idx];
+	idx -= cfg->line_outs;
+	if (spec->multi_ios > idx)
+		return spec->multi_io[idx].dac;
+	return 0;
+}
+
+/* return the DAC if it's reachable, otherwise zero */
+static inline hda_nid_t try_dac(struct hda_codec *codec,
+				hda_nid_t dac, hda_nid_t pin)
+{
+	return is_reachable_path(codec, dac, pin) ? dac : 0;
+}
+
 /* try to assign DACs to pins and return the resultant badness */
 static int try_assign_dacs(struct hda_codec *codec, int num_outs,
 			   const hda_nid_t *pins, hda_nid_t *dacs,
@@ -867,7 +888,6 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs,
 			   const struct badness_table *bad)
 {
 	struct hda_gen_spec *spec = codec->spec;
-	struct auto_pin_cfg *cfg = &spec->autocfg;
 	int i, j;
 	int badness = 0;
 	hda_nid_t dac;
@@ -897,11 +917,12 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs,
 		}
 		dac = dacs[i];
 		if (!dac) {
-			if (is_reachable_path(codec, dacs[0], pin))
-				dac = dacs[0];
-			else if (cfg->line_outs > i &&
-				 is_reachable_path(codec, spec->private_dac_nids[i], pin))
-				dac = spec->private_dac_nids[i];
+			if (num_outs > 2)
+				dac = try_dac(codec, get_primary_out(codec, i), pin);
+			if (!dac)
+				dac = try_dac(codec, dacs[0], pin);
+			if (!dac)
+				dac = try_dac(codec, get_primary_out(codec, i), pin);
 			if (dac) {
 				if (!i)
 					badness += bad->shared_primary;
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 096/112] ALSA: hda - Add missing slave names for Speaker Surround, etc
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (94 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 095/112] ALSA: hda - Prefer binding the primary CLFE output Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 097/112] ALSA: hda - Drop unneeded pin argument from set_output_and_unmute() Takashi Iwai
                   ` (18 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index fa8302f..823fac5 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -3250,6 +3250,9 @@ static const char * const slave_pfxs[] = {
 	"Front", "Surround", "Center", "LFE", "Side",
 	"Headphone", "Speaker", "Mono", "Line Out",
 	"CLFE", "Bass Speaker", "PCM",
+	"Speaker Front", "Speaker Surround", "Speaker CLFE", "Speaker Side",
+	"Headphone Front", "Headphone Surround", "Headphone CLFE",
+	"Headphone Side",
 	NULL,
 };
 
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 097/112] ALSA: hda - Drop unneeded pin argument from set_output_and_unmute()
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (95 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 096/112] ALSA: hda - Add missing slave names for Speaker Surround, etc Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 098/112] ALSA: hda - Drop bind-volume workaround Takashi Iwai
                   ` (17 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

Just a minor refactoring.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 41 +++++++++++++----------------------------
 1 file changed, 13 insertions(+), 28 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 823fac5..dd65d2a 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -3751,16 +3751,18 @@ EXPORT_SYMBOL_HDA(snd_hda_gen_build_pcms);
  * Standard auto-parser initializations
  */
 
-/* configure the path from the given dac to the pin as the proper output */
-static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin,
+/* configure the given path as a proper output */
+static void set_output_and_unmute(struct hda_codec *codec,
 				  int pin_type, int path_idx)
 {
 	struct nid_path *path;
+	hda_nid_t pin;
 
-	snd_hda_set_pin_ctl_cache(codec, pin, pin_type);
 	path = snd_hda_get_path_from_idx(codec, path_idx);
-	if (!path)
+	if (!path || !path->depth)
 		return;
+	pin = path->path[path->depth - 1];
+	snd_hda_set_pin_ctl_cache(codec, pin, pin_type);
 	snd_hda_activate_path(codec, path, path->active, true);
 	set_pin_eapd(codec, pin, path->active);
 }
@@ -3769,7 +3771,6 @@ static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin,
 static void init_multi_out(struct hda_codec *codec)
 {
 	struct hda_gen_spec *spec = codec->spec;
-	hda_nid_t nid;
 	int pin_type;
 	int i;
 
@@ -3778,27 +3779,18 @@ static void init_multi_out(struct hda_codec *codec)
 	else
 		pin_type = PIN_OUT;
 
-	for (i = 0; i < spec->autocfg.line_outs; i++) {
-		nid = spec->autocfg.line_out_pins[i];
-		if (nid)
-			set_output_and_unmute(codec, nid, pin_type,
-					      spec->out_paths[i]);
-	}
+	for (i = 0; i < spec->autocfg.line_outs; i++)
+		set_output_and_unmute(codec, pin_type, spec->out_paths[i]);
 }
 
 
 static void __init_extra_out(struct hda_codec *codec, int num_outs,
-			     hda_nid_t *pins, int *paths, int type)
+			     int *paths, int type)
 {
 	int i;
-	hda_nid_t pin;
 
-	for (i = 0; i < num_outs; i++) {
-		pin = pins[i];
-		if (!pin)
-			break;
-		set_output_and_unmute(codec, pin, type, paths[i]);
-	}
+	for (i = 0; i < num_outs; i++)
+		set_output_and_unmute(codec, type, paths[i]);
 }
 
 /* initialize hp and speaker paths */
@@ -3808,11 +3800,9 @@ static void init_extra_out(struct hda_codec *codec)
 
 	if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT)
 		__init_extra_out(codec, spec->autocfg.hp_outs,
-				 spec->autocfg.hp_pins,
 				 spec->hp_paths, PIN_HP);
 	if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT)
 		__init_extra_out(codec, spec->autocfg.speaker_outs,
-				 spec->autocfg.speaker_pins,
 				 spec->speaker_paths, PIN_OUT);
 }
 
@@ -3909,13 +3899,8 @@ static void init_digital(struct hda_codec *codec)
 	int i;
 	hda_nid_t pin;
 
-	for (i = 0; i < spec->autocfg.dig_outs; i++) {
-		pin = spec->autocfg.dig_out_pins[i];
-		if (!pin)
-			continue;
-		set_output_and_unmute(codec, pin, PIN_OUT,
-				      spec->digout_paths[i]);
-	}
+	for (i = 0; i < spec->autocfg.dig_outs; i++)
+		set_output_and_unmute(codec, PIN_OUT, spec->digout_paths[i]);
 	pin = spec->autocfg.dig_in_pin;
 	if (pin) {
 		struct nid_path *path;
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 098/112] ALSA: hda - Drop bind-volume workaround
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (96 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 097/112] ALSA: hda - Drop unneeded pin argument from set_output_and_unmute() Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 099/112] ALSA: hda - Add pcm_playback_hook to hda_gen_spec Takashi Iwai
                   ` (16 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

The bind-volume workaround was introduced for simplifying the mixer
abstraction in the case where one or more pins of multiple outputs
lack of individual volume controls.  This was essentially the case
like Acer Aspire 5935, which has 5.1 speakers and 5.1 (multi-io)
jacks although there are 5 DACs, so some of them must share a DAC.

However, the recent code rewrite changed the DAC assignment policy to
share with the same channel instead of binding to the front, thus
binding the volumes for all channels makes little sense now, rather
it's confusing.  So in this patch, the ugly workaround is finally
dropped and simply create the volume control corresponding to the
parsed path position.

For dual headphones or 2.1 speakers with a shared volume control, it's
anyway bound to the same DAC if needed, so this change shouldn't bring
any practical difference.

And, as a good bonus, we can cut off the whole code handling the bind
volume elements.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 114 +++++++-------------------------------------
 sound/pci/hda/hda_generic.h |   3 --
 2 files changed, 17 insertions(+), 100 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index dd65d2a..15b742a 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -39,7 +39,6 @@
 int snd_hda_gen_spec_init(struct hda_gen_spec *spec)
 {
 	snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32);
-	snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8);
 	snd_array_init(&spec->paths, sizeof(struct nid_path), 8);
 	mutex_init(&spec->pcm_mutex);
 	return 0;
@@ -75,39 +74,11 @@ static void free_kctls(struct hda_gen_spec *spec)
 	snd_array_free(&spec->kctls);
 }
 
-static struct hda_bind_ctls *new_bind_ctl(struct hda_codec *codec,
-					  unsigned int nums,
-					  struct hda_ctl_ops *ops)
-{
-	struct hda_gen_spec *spec = codec->spec;
-	struct hda_bind_ctls **ctlp, *ctl;
-	ctlp = snd_array_new(&spec->bind_ctls);
-	if (!ctlp)
-		return NULL;
-	ctl = kzalloc(sizeof(*ctl) + sizeof(long) * (nums + 1), GFP_KERNEL);
-	*ctlp = ctl;
-	if (ctl)
-		ctl->ops = ops;
-	return ctl;
-}
-
-static void free_bind_ctls(struct hda_gen_spec *spec)
-{
-	if (spec->bind_ctls.list) {
-		struct hda_bind_ctls **ctl = spec->bind_ctls.list;
-		int i;
-		for (i = 0; i < spec->bind_ctls.used; i++)
-			kfree(ctl[i]);
-	}
-	snd_array_free(&spec->bind_ctls);
-}
-
 void snd_hda_gen_spec_free(struct hda_gen_spec *spec)
 {
 	if (!spec)
 		return;
 	free_kctls(spec);
-	free_bind_ctls(spec);
 	snd_array_free(&spec->paths);
 }
 EXPORT_SYMBOL_HDA(snd_hda_gen_spec_free);
@@ -1489,8 +1460,7 @@ static int create_multi_out_ctls(struct hda_codec *codec,
 	return 0;
 }
 
-static int create_extra_out(struct hda_codec *codec, hda_nid_t pin,
-			    hda_nid_t dac, int path_idx,
+static int create_extra_out(struct hda_codec *codec, int path_idx,
 			    const char *pfx, int cidx)
 {
 	struct nid_path *path;
@@ -1499,12 +1469,9 @@ static int create_extra_out(struct hda_codec *codec, hda_nid_t pin,
 	path = snd_hda_get_path_from_idx(codec, path_idx);
 	if (!path)
 		return 0;
-	/* bind volume control will be created in the case of dac = 0 */
-	if (dac) {
-		err = add_stereo_vol(codec, pfx, cidx, path);
-		if (err < 0)
-			return err;
-	}
+	err = add_stereo_vol(codec, pfx, cidx, path);
+	if (err < 0)
+		return err;
 	err = add_stereo_sw(codec, pfx, cidx, path);
 	if (err < 0)
 		return err;
@@ -1513,69 +1480,26 @@ static int create_extra_out(struct hda_codec *codec, hda_nid_t pin,
 
 /* add playback controls for speaker and HP outputs */
 static int create_extra_outs(struct hda_codec *codec, int num_pins,
-			     const hda_nid_t *pins, const hda_nid_t *dacs,
 			     const int *paths, const char *pfx)
 {
-	struct hda_gen_spec *spec = codec->spec;
-	struct hda_bind_ctls *ctl;
-	char name[44];
-	int i, n, err;
-
-	if (!num_pins || !pins[0])
-		return 0;
-
-	if (num_pins == 1) {
-		hda_nid_t dac = *dacs;
-		if (!dac)
-			dac = spec->multiout.dac_nids[0];
-		return create_extra_out(codec, *pins, dac, paths[0], pfx, 0);
-	}
+	int i;
 
 	for (i = 0; i < num_pins; i++) {
-		hda_nid_t dac;
-		if (dacs[num_pins - 1])
-			dac = dacs[i]; /* with individual volumes */
-		else
-			dac = 0;
-		if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) {
-			err = create_extra_out(codec, pins[i], dac, paths[i],
-					       "Bass Speaker", 0);
-		} else if (num_pins >= 3) {
-			snprintf(name, sizeof(name), "%s %s",
+		const char *name;
+		char tmp[44];
+		int err, idx = 0;
+
+		if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker"))
+			name = "Bass Speaker";
+		else if (num_pins >= 3) {
+			snprintf(tmp, sizeof(tmp), "%s %s",
 				 pfx, channel_name[i]);
-			err = create_extra_out(codec, pins[i], dac, paths[i],
-					       name, 0);
+			name = tmp;
 		} else {
-			err = create_extra_out(codec, pins[i], dac, paths[i],
-					       pfx, i);
+			name = pfx;
+			idx = i;
 		}
-		if (err < 0)
-			return err;
-	}
-	if (dacs[num_pins - 1])
-		return 0;
-
-	/* Let's create a bind-controls for volumes */
-	ctl = new_bind_ctl(codec, num_pins, &snd_hda_bind_vol);
-	if (!ctl)
-		return -ENOMEM;
-	n = 0;
-	for (i = 0; i < num_pins; i++) {
-		hda_nid_t vol;
-		struct nid_path *path;
-		if (!pins[i] || !dacs[i])
-			continue;
-		path = snd_hda_get_path_from_idx(codec, paths[i]);
-		if (!path)
-			continue;
-		vol = look_for_out_vol_nid(codec, path);
-		if (vol)
-			ctl->values[n++] =
-				HDA_COMPOSE_AMP_VAL(vol, 3, 0, HDA_OUTPUT);
-	}
-	if (n) {
-		snprintf(name, sizeof(name), "%s Playback Volume", pfx);
-		err = add_control(spec, HDA_CTL_BIND_VOL, name, 0, (long)ctl);
+		err = create_extra_out(codec, paths[i], name, idx);
 		if (err < 0)
 			return err;
 	}
@@ -1586,8 +1510,6 @@ static int create_hp_out_ctls(struct hda_codec *codec)
 {
 	struct hda_gen_spec *spec = codec->spec;
 	return create_extra_outs(codec, spec->autocfg.hp_outs,
-				 spec->autocfg.hp_pins,
-				 spec->multiout.hp_out_nid,
 				 spec->hp_paths,
 				 "Headphone");
 }
@@ -1596,8 +1518,6 @@ static int create_speaker_out_ctls(struct hda_codec *codec)
 {
 	struct hda_gen_spec *spec = codec->spec;
 	return create_extra_outs(codec, spec->autocfg.speaker_outs,
-				 spec->autocfg.speaker_pins,
-				 spec->multiout.extra_out_nid,
 				 spec->speaker_paths,
 				 "Speaker");
 }
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index d4a8f6c..4c0d9ad 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -186,9 +186,6 @@ struct hda_gen_spec {
 	int multi_ios;
 	struct hda_multi_io multi_io[4];
 
-	/* bind volumes */
-	struct snd_array bind_ctls;
-
 	/* hooks */
 	void (*init_hook)(struct hda_codec *codec);
 	void (*automute_hook)(struct hda_codec *codec);
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 099/112] ALSA: hda - Add pcm_playback_hook to hda_gen_spec
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (97 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 098/112] ALSA: hda - Drop bind-volume workaround Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 100/112] ALSA: hda - Allow jack detection when polling is enabled Takashi Iwai
                   ` (15 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

The new hook which is called at each PCM playback ops.
It can be used to control the codec-specific power-saving feature in
each codec driver.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 65 +++++++++++++++++++++++++++++++++++++++++----
 sound/pci/hda/hda_generic.h | 14 ++++++++++
 2 files changed, 74 insertions(+), 5 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 15b742a..a041032 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -3259,6 +3259,16 @@ EXPORT_SYMBOL_HDA(snd_hda_gen_build_controls);
  * PCM definitions
  */
 
+static void call_pcm_playback_hook(struct hda_pcm_stream *hinfo,
+				   struct hda_codec *codec,
+				   struct snd_pcm_substream *substream,
+				   int action)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	if (spec->pcm_playback_hook)
+		spec->pcm_playback_hook(hinfo, codec, substream, action);
+}
+
 /*
  * Analog playback callbacks
  */
@@ -3273,8 +3283,11 @@ static int playback_pcm_open(struct hda_pcm_stream *hinfo,
 	err = snd_hda_multi_out_analog_open(codec,
 					    &spec->multiout, substream,
 					     hinfo);
-	if (!err)
+	if (!err) {
 		spec->active_streams |= 1 << STREAM_MULTI_OUT;
+		call_pcm_playback_hook(hinfo, codec, substream,
+				       HDA_GEN_PCM_ACT_OPEN);
+	}
 	mutex_unlock(&spec->pcm_mutex);
 	return err;
 }
@@ -3286,8 +3299,14 @@ static int playback_pcm_prepare(struct hda_pcm_stream *hinfo,
 				struct snd_pcm_substream *substream)
 {
 	struct hda_gen_spec *spec = codec->spec;
-	return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
-						stream_tag, format, substream);
+	int err;
+
+	err = snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
+					       stream_tag, format, substream);
+	if (!err)
+		call_pcm_playback_hook(hinfo, codec, substream,
+				       HDA_GEN_PCM_ACT_PREPARE);
+	return err;
 }
 
 static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
@@ -3295,7 +3314,13 @@ static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
 				struct snd_pcm_substream *substream)
 {
 	struct hda_gen_spec *spec = codec->spec;
-	return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+	int err;
+
+	err = snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+	if (!err)
+		call_pcm_playback_hook(hinfo, codec, substream,
+				       HDA_GEN_PCM_ACT_CLEANUP);
+	return err;
 }
 
 static int playback_pcm_close(struct hda_pcm_stream *hinfo,
@@ -3305,6 +3330,8 @@ static int playback_pcm_close(struct hda_pcm_stream *hinfo,
 	struct hda_gen_spec *spec = codec->spec;
 	mutex_lock(&spec->pcm_mutex);
 	spec->active_streams &= ~(1 << STREAM_MULTI_OUT);
+	call_pcm_playback_hook(hinfo, codec, substream,
+			       HDA_GEN_PCM_ACT_CLOSE);
 	mutex_unlock(&spec->pcm_mutex);
 	return 0;
 }
@@ -3321,6 +3348,8 @@ static int alt_playback_pcm_open(struct hda_pcm_stream *hinfo,
 		err = -EBUSY;
 	else
 		spec->active_streams |= 1 << STREAM_INDEP_HP;
+	call_pcm_playback_hook(hinfo, codec, substream,
+			       HDA_GEN_PCM_ACT_OPEN);
 	mutex_unlock(&spec->pcm_mutex);
 	return err;
 }
@@ -3332,10 +3361,34 @@ static int alt_playback_pcm_close(struct hda_pcm_stream *hinfo,
 	struct hda_gen_spec *spec = codec->spec;
 	mutex_lock(&spec->pcm_mutex);
 	spec->active_streams &= ~(1 << STREAM_INDEP_HP);
+	call_pcm_playback_hook(hinfo, codec, substream,
+			       HDA_GEN_PCM_ACT_CLOSE);
 	mutex_unlock(&spec->pcm_mutex);
 	return 0;
 }
 
+static int alt_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+				    struct hda_codec *codec,
+				    unsigned int stream_tag,
+				    unsigned int format,
+				    struct snd_pcm_substream *substream)
+{
+	snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
+	call_pcm_playback_hook(hinfo, codec, substream,
+			       HDA_GEN_PCM_ACT_PREPARE);
+	return 0;
+}
+
+static int alt_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+				    struct hda_codec *codec,
+				    struct snd_pcm_substream *substream)
+{
+	snd_hda_codec_cleanup_stream(codec, hinfo->nid);
+	call_pcm_playback_hook(hinfo, codec, substream,
+			       HDA_GEN_PCM_ACT_CLEANUP);
+	return 0;
+}
+
 /*
  * Digital out
  */
@@ -3430,7 +3483,9 @@ static const struct hda_pcm_stream pcm_analog_alt_playback = {
 	/* NID is set in build_pcms */
 	.ops = {
 		.open = alt_playback_pcm_open,
-		.close = alt_playback_pcm_close
+		.close = alt_playback_pcm_close,
+		.prepare = alt_playback_pcm_prepare,
+		.cleanup = alt_playback_pcm_cleanup
 	},
 };
 
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index 4c0d9ad..7e84c22 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -69,6 +69,14 @@ struct automic_entry {
 /* active stream id */
 enum { STREAM_MULTI_OUT, STREAM_INDEP_HP };
 
+/* PCM hook action */
+enum {
+	HDA_GEN_PCM_ACT_OPEN,
+	HDA_GEN_PCM_ACT_PREPARE,
+	HDA_GEN_PCM_ACT_CLEANUP,
+	HDA_GEN_PCM_ACT_CLOSE,
+};
+
 struct hda_gen_spec {
 	char stream_name_analog[32];	/* analog PCM stream */
 	const struct hda_pcm_stream *stream_analog_playback;
@@ -191,6 +199,12 @@ struct hda_gen_spec {
 	void (*automute_hook)(struct hda_codec *codec);
 	void (*cap_sync_hook)(struct hda_codec *codec);
 
+	/* PCM playback hook */
+	void (*pcm_playback_hook)(struct hda_pcm_stream *hinfo,
+				  struct hda_codec *codec,
+				  struct snd_pcm_substream *substream,
+				  int action);
+
 	/* automute / autoswitch hooks */
 	void (*hp_automute_hook)(struct hda_codec *codec,
 				 struct hda_jack_tbl *tbl);
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 100/112] ALSA: hda - Allow jack detection when polling is enabled
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (98 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 099/112] ALSA: hda - Add pcm_playback_hook to hda_gen_spec Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 101/112] ALSA: hda - Add snd_hda_gen_free() and snd_hda_gen_check_power_status() Takashi Iwai
                   ` (14 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

Let is_jack_detectable() return true when the jack polling is enabled
for the codec.  VT1708 uses the polling explicitly so we need to allow
it.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_jack.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c
index 6479b65..1d035ef 100644
--- a/sound/pci/hda/hda_jack.c
+++ b/sound/pci/hda/hda_jack.c
@@ -29,7 +29,8 @@ bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid)
 	if (get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) &
 	     AC_DEFCFG_MISC_NO_PRESENCE)
 		return false;
-	if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP))
+	if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) &&
+	    !codec->jackpoll_interval)
 		return false;
 	return true;
 }
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 101/112] ALSA: hda - Add snd_hda_gen_free() and snd_hda_gen_check_power_status()
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (99 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 100/112] ALSA: hda - Allow jack detection when polling is enabled Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 102/112] ALSA: hda - Remove dead HDA_CTL_BIND_VOL and HDA_CTL_BIND_SW codes Takashi Iwai
                   ` (13 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

Just to remove duplicated codes.
Also fixed EXPORT_SYMBOL() in hda_generic.c.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c   | 33 ++++++++++++++++++---------------
 sound/pci/hda/hda_generic.h   |  5 +++++
 sound/pci/hda/patch_realtek.c | 12 +-----------
 3 files changed, 24 insertions(+), 26 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index a041032..78f210d 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -3937,36 +3937,39 @@ int snd_hda_gen_init(struct hda_codec *codec)
 	hda_call_check_power_status(codec, 0x01);
 	return 0;
 }
-EXPORT_SYMBOL(snd_hda_gen_init);
+EXPORT_SYMBOL_HDA(snd_hda_gen_init);
 
 
-/*
- * the generic codec support
- */
+void snd_hda_gen_free(struct hda_codec *codec)
+{
+	snd_hda_gen_spec_free(codec->spec);
+	kfree(codec->spec);
+	codec->spec = NULL;
+}
+EXPORT_SYMBOL_HDA(snd_hda_gen_free);
 
 #ifdef CONFIG_PM
-static int generic_check_power_status(struct hda_codec *codec, hda_nid_t nid)
+int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid)
 {
 	struct hda_gen_spec *spec = codec->spec;
 	return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
 }
+EXPORT_SYMBOL_HDA(snd_hda_gen_check_power_status);
 #endif
 
-static void generic_free(struct hda_codec *codec)
-{
-	snd_hda_gen_spec_free(codec->spec);
-	kfree(codec->spec);
-	codec->spec = NULL;
-}
+
+/*
+ * the generic codec support
+ */
 
 static const struct hda_codec_ops generic_patch_ops = {
 	.build_controls = snd_hda_gen_build_controls,
 	.build_pcms = snd_hda_gen_build_pcms,
 	.init = snd_hda_gen_init,
-	.free = generic_free,
+	.free = snd_hda_gen_free,
 	.unsol_event = snd_hda_jack_unsol_event,
 #ifdef CONFIG_PM
-	.check_power_status = generic_check_power_status,
+	.check_power_status = snd_hda_gen_check_power_status,
 #endif
 };
 
@@ -3993,7 +3996,7 @@ int snd_hda_parse_generic_codec(struct hda_codec *codec)
 	return 0;
 
 error:
-	generic_free(codec);
+	snd_hda_gen_free(codec);
 	return err;
 }
-EXPORT_SYMBOL(snd_hda_parse_generic_codec);
+EXPORT_SYMBOL_HDA(snd_hda_parse_generic_codec);
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index 7e84c22..00a1eab 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -218,6 +218,7 @@ int snd_hda_gen_spec_init(struct hda_gen_spec *spec);
 void snd_hda_gen_spec_free(struct hda_gen_spec *spec);
 
 int snd_hda_gen_init(struct hda_codec *codec);
+void snd_hda_gen_free(struct hda_codec *codec);
 
 struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec,
 				      hda_nid_t from_nid, hda_nid_t to_nid);
@@ -257,4 +258,8 @@ void snd_hda_gen_mic_autoswitch(struct hda_codec *codec,
 				struct hda_jack_tbl *jack);
 void snd_hda_gen_update_outputs(struct hda_codec *codec);
 
+#ifdef CONFIG_PM
+int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid);
+#endif
+
 #endif /* __SOUND_HDA_GENERIC_H */
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 1c343f2..ac0937f 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -808,14 +808,6 @@ static int alc_init(struct hda_codec *codec)
 	return 0;
 }
 
-#ifdef CONFIG_PM
-static int alc_check_power_status(struct hda_codec *codec, hda_nid_t nid)
-{
-	struct alc_spec *spec = codec->spec;
-	return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid);
-}
-#endif
-
 static inline void alc_shutup(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
@@ -876,10 +868,8 @@ static const struct hda_codec_ops alc_patch_ops = {
 	.unsol_event = snd_hda_jack_unsol_event,
 #ifdef CONFIG_PM
 	.resume = alc_resume,
-#endif
-#ifdef CONFIG_PM
 	.suspend = alc_suspend,
-	.check_power_status = alc_check_power_status,
+	.check_power_status = snd_hda_gen_check_power_status,
 #endif
 	.reboot_notify = alc_shutup,
 };
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 102/112] ALSA: hda - Remove dead HDA_CTL_BIND_VOL and HDA_CTL_BIND_SW codes
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (100 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 101/112] ALSA: hda - Add snd_hda_gen_free() and snd_hda_gen_check_power_status() Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 103/112] ALSA: hda - Add brief comments to exported snd_hda_gen_*_() functions Takashi Iwai
                   ` (12 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 78f210d..68024ad 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -576,15 +576,11 @@ enum {
 	HDA_CTL_WIDGET_VOL,
 	HDA_CTL_WIDGET_MUTE,
 	HDA_CTL_BIND_MUTE,
-	HDA_CTL_BIND_VOL,
-	HDA_CTL_BIND_SW,
 };
 static const struct snd_kcontrol_new control_templates[] = {
 	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
 	HDA_CODEC_MUTE(NULL, 0, 0, 0),
 	HDA_BIND_MUTE(NULL, 0, 0, 0),
-	HDA_BIND_VOL(NULL, 0),
-	HDA_BIND_SW(NULL, 0),
 };
 
 /* add dynamic controls from template */
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 103/112] ALSA: hda - Add brief comments to exported snd_hda_gen_*_() functions
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (101 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 102/112] ALSA: hda - Remove dead HDA_CTL_BIND_VOL and HDA_CTL_BIND_SW codes Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 104/112] ALSA: hda - Clear path indices properly at each re-evaluation Takashi Iwai
                   ` (11 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 68024ad..7c76a00 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -3899,6 +3899,10 @@ static void clear_unsol_on_unused_pins(struct hda_codec *codec)
 	}
 }
 
+/*
+ * initialize the generic spec;
+ * this can be put as patch_ops.init function
+ */
 int snd_hda_gen_init(struct hda_codec *codec)
 {
 	struct hda_gen_spec *spec = codec->spec;
@@ -3935,7 +3939,10 @@ int snd_hda_gen_init(struct hda_codec *codec)
 }
 EXPORT_SYMBOL_HDA(snd_hda_gen_init);
 
-
+/*
+ * free the generic spec;
+ * this can be put as patch_ops.free function
+ */
 void snd_hda_gen_free(struct hda_codec *codec)
 {
 	snd_hda_gen_spec_free(codec->spec);
@@ -3945,6 +3952,10 @@ void snd_hda_gen_free(struct hda_codec *codec)
 EXPORT_SYMBOL_HDA(snd_hda_gen_free);
 
 #ifdef CONFIG_PM
+/*
+ * check the loopback power save state;
+ * this can be put as patch_ops.check_power_status function
+ */
 int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid)
 {
 	struct hda_gen_spec *spec = codec->spec;
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 104/112] ALSA: hda - Clear path indices properly at each re-evaluation
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (102 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 103/112] ALSA: hda - Add brief comments to exported snd_hda_gen_*_() functions Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 105/112] ALSA: hda - Use direct path reference in assign_out_path_ctls() Takashi Iwai
                   ` (10 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

The path indices must be reset at each evaluation of DAC assignment.
Otherwise the badness value will be wrongly calculated and mixers may
be inconsistently assigned.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 7c76a00..990a79e 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -1138,6 +1138,16 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
 	memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid));
 	spec->multi_ios = 0;
 	snd_array_free(&spec->paths);
+
+	/* clear path indices */
+	memset(spec->out_paths, 0, sizeof(spec->out_paths));
+	memset(spec->hp_paths, 0, sizeof(spec->hp_paths));
+	memset(spec->speaker_paths, 0, sizeof(spec->speaker_paths));
+	memset(spec->aamix_out_paths, 0, sizeof(spec->aamix_out_paths));
+	memset(spec->digout_paths, 0, sizeof(spec->digout_paths));
+	memset(spec->loopback_paths, 0, sizeof(spec->loopback_paths));
+	memset(&spec->digin_path, 0, sizeof(spec->digin_path));
+
 	badness = 0;
 
 	/* fill hard-wired DACs first */
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 105/112] ALSA: hda - Use direct path reference in assign_out_path_ctls()
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (103 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 104/112] ALSA: hda - Clear path indices properly at each re-evaluation Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 106/112] ALSA: hda - Remove unused dac reference in create_multi_out_ctls() Takashi Iwai
                   ` (9 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

Instead of looking through paths with the dac -> pin connection at
each time, just pass the already parsed path index to
assign_out_path_ctls().  This simplifies the code a bit.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 28 ++++++++++++++++------------
 1 file changed, 16 insertions(+), 12 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 990a79e..27b5e02 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -757,23 +757,26 @@ enum {
 	BAD_SHARED_VOL = 0x10,
 };
 
-/* look for widgets in the path between the given NIDs appropriate for
+/* look for widgets in the given path which are appropriate for
  * volume and mute controls, and assign the values to ctls[].
  *
  * When no appropriate widget is found in the path, the badness value
  * is incremented depending on the situation.  The function returns the
  * total badness for both volume and mute controls.
  */
-static int assign_out_path_ctls(struct hda_codec *codec, hda_nid_t pin,
-				hda_nid_t dac)
+static int assign_out_path_ctls(struct hda_codec *codec, struct nid_path *path)
 {
-	struct nid_path *path = snd_hda_get_nid_path(codec, dac, pin);
 	hda_nid_t nid;
 	unsigned int val;
 	int badness = 0;
 
 	if (!path)
 		return BAD_SHARED_VOL * 2;
+
+	if (path->ctls[NID_PATH_VOL_CTL] ||
+	    path->ctls[NID_PATH_MUTE_CTL])
+		return 0; /* already evaluated */
+
 	nid = look_for_out_vol_nid(codec, path);
 	if (nid) {
 		val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
@@ -866,8 +869,9 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs,
 		struct nid_path *path;
 		hda_nid_t pin = pins[i];
 
-		if (dacs[i]) {
-			badness += assign_out_path_ctls(codec, pin, dacs[i]);
+		path = snd_hda_get_path_from_idx(codec, path_idx[i]);
+		if (path) {
+			badness += assign_out_path_ctls(codec, path);
 			continue;
 		}
 
@@ -916,9 +920,8 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs,
 			print_nid_path("output", path);
 			path->active = true;
 			path_idx[i] = snd_hda_get_path_idx(codec, path);
+			badness += assign_out_path_ctls(codec, path);
 		}
-		if (dac)
-			badness += assign_out_path_ctls(codec, pin, dac);
 	}
 
 	return badness;
@@ -1001,6 +1004,7 @@ static int fill_multi_ios(struct hda_codec *codec,
 	unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin);
 	unsigned int location = get_defcfg_location(defcfg);
 	int badness = 0;
+	struct nid_path *path;
 
 	old_pins = spec->multi_ios;
 	if (old_pins >= 2)
@@ -1012,7 +1016,6 @@ static int fill_multi_ios(struct hda_codec *codec,
 
 	for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
 		for (i = 0; i < cfg->num_inputs; i++) {
-			struct nid_path *path;
 			hda_nid_t nid = cfg->inputs[i].pin;
 			hda_nid_t dac = 0;
 
@@ -1067,9 +1070,10 @@ static int fill_multi_ios(struct hda_codec *codec,
 	}
 
 	/* assign volume and mute controls */
-	for (i = old_pins; i < spec->multi_ios; i++)
-		badness += assign_out_path_ctls(codec, spec->multi_io[i].pin,
-						spec->multi_io[i].dac);
+	for (i = old_pins; i < spec->multi_ios; i++) {
+		path = snd_hda_get_path_from_idx(codec, spec->out_paths[cfg->line_outs + i]);
+		badness += assign_out_path_ctls(codec, path);
+	}
 
 	return badness;
 }
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 106/112] ALSA: hda - Remove unused dac reference in create_multi_out_ctls()
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (104 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 105/112] ALSA: hda - Use direct path reference in assign_out_path_ctls() Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 107/112] ALSA: hda - Don't set up active streams twice Takashi Iwai
                   ` (8 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

Remove useless code.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 27b5e02..d13e404 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -1428,12 +1428,8 @@ static int create_multi_out_ctls(struct hda_codec *codec,
 	for (i = 0; i < noutputs; i++) {
 		const char *name;
 		int index;
-		hda_nid_t dac;
 		struct nid_path *path;
 
-		dac = spec->multiout.dac_nids[i];
-		if (!dac)
-			continue;
 		if (i >= cfg->line_outs) {
 			index = 0;
 			name = channel_name[i];
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 107/112] ALSA: hda - Don't set up active streams twice
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (105 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 106/112] ALSA: hda - Remove unused dac reference in create_multi_out_ctls() Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 108/112] ALSA: hda - Fix multi-io channel mode management Takashi Iwai
                   ` (7 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

We don't have to set up a stream that has been already set up
previously.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_codec.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index cd318e4..c8cc7ce 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1493,7 +1493,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
 		    "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n",
 		    nid, stream_tag, channel_id, format);
 	p = get_hda_cvt_setup(codec, nid);
-	if (!p)
+	if (!p || p->active)
 		return;
 
 	if (codec->pcm_format_first)
@@ -1540,7 +1540,7 @@ void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid,
 
 	snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid);
 	p = get_hda_cvt_setup(codec, nid);
-	if (p) {
+	if (p && p->active) {
 		/* here we just clear the active flag when do_now isn't set;
 		 * actual clean-ups will be done later in
 		 * purify_inactive_streams() called from snd_hda_codec_prpapre()
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 108/112] ALSA: hda - Fix multi-io channel mode management
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (106 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 107/112] ALSA: hda - Don't set up active streams twice Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-02-21  8:03   ` Raymond Yau
  2013-01-08 11:39 ` [PATCH 109/112] ALSA: hda - Manage input paths via path indices Takashi Iwai
                   ` (6 subsequent siblings)
  114 siblings, 1 reply; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

The multi-io channels can vary not only from 1 to 6 but also may vary
from 6 to 8 or such.  At the same time, there are more speaker pins
available than the primary output pins.  So, we need three variables
to check: the minimum channel counts for primary outputs, the current
channel counts for primary outputs, and the minimum channel counts for
all outputs.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 92 ++++++++++++++++++++++++++++++---------------
 sound/pci/hda/hda_generic.h | 16 +++++++-
 2 files changed, 76 insertions(+), 32 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index d13e404..193dd55 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -1125,6 +1125,25 @@ static int check_aamix_out_path(struct hda_codec *codec, int path_idx)
 	return snd_hda_get_path_idx(codec, path);
 }
 
+/* fill the empty entries in the dac array for speaker/hp with the
+ * shared dac pointed by the paths
+ */
+static void refill_shared_dacs(struct hda_codec *codec, int num_outs,
+			       hda_nid_t *dacs, int *path_idx)
+{
+	struct nid_path *path;
+	int i;
+
+	for (i = 0; i < num_outs; i++) {
+		if (dacs[i])
+			continue;
+		path = snd_hda_get_path_from_idx(codec, path_idx[i]);
+		if (!path)
+			continue;
+		dacs[i] = path->path[0];
+	}
+}
+
 /* fill in the dac_nids table from the parsed pin configuration */
 static int fill_and_eval_dacs(struct hda_codec *codec,
 			      bool fill_hardwired,
@@ -1183,19 +1202,6 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
 				   spec->private_dac_nids, spec->out_paths,
 				   &main_out_badness);
 
-	/* re-count num_dacs and squash invalid entries */
-	spec->multiout.num_dacs = 0;
-	for (i = 0; i < cfg->line_outs; i++) {
-		if (spec->private_dac_nids[i])
-			spec->multiout.num_dacs++;
-		else {
-			memmove(spec->private_dac_nids + i,
-				spec->private_dac_nids + i + 1,
-				sizeof(hda_nid_t) * (cfg->line_outs - i - 1));
-			spec->private_dac_nids[cfg->line_outs - 1] = 0;
-		}
-	}
-
 	if (fill_mio_first &&
 	    cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
 		/* try to fill multi-io first */
@@ -1246,16 +1252,41 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
 		if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2)
 			spec->multi_ios = 1; /* give badness */
 
+	/* re-count num_dacs and squash invalid entries */
+	spec->multiout.num_dacs = 0;
+	for (i = 0; i < cfg->line_outs; i++) {
+		if (spec->private_dac_nids[i])
+			spec->multiout.num_dacs++;
+		else {
+			memmove(spec->private_dac_nids + i,
+				spec->private_dac_nids + i + 1,
+				sizeof(hda_nid_t) * (cfg->line_outs - i - 1));
+			spec->private_dac_nids[cfg->line_outs - 1] = 0;
+		}
+	}
+
+	spec->ext_channel_count = spec->min_channel_count =
+		spec->multiout.num_dacs;
+
 	if (spec->multi_ios == 2) {
 		for (i = 0; i < 2; i++)
 			spec->private_dac_nids[spec->multiout.num_dacs++] =
 				spec->multi_io[i].dac;
-		spec->ext_channel_count = 2;
 	} else if (spec->multi_ios) {
 		spec->multi_ios = 0;
 		badness += BAD_MULTI_IO;
 	}
 
+	/* re-fill the shared DAC for speaker / headphone */
+	if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+		refill_shared_dacs(codec, cfg->hp_outs,
+				   spec->multiout.hp_out_nid,
+				   spec->hp_paths);
+	if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+		refill_shared_dacs(codec, cfg->speaker_outs,
+				   spec->multiout.extra_out_nid,
+				   spec->speaker_paths);
+
 	return badness;
 }
 
@@ -1608,14 +1639,15 @@ static int ch_mode_info(struct snd_kcontrol *kcontrol,
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct hda_gen_spec *spec = codec->spec;
+	int chs;
 
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
 	uinfo->count = 1;
 	uinfo->value.enumerated.items = spec->multi_ios + 1;
 	if (uinfo->value.enumerated.item > spec->multi_ios)
 		uinfo->value.enumerated.item = spec->multi_ios;
-	sprintf(uinfo->value.enumerated.name, "%dch",
-		(uinfo->value.enumerated.item + 1) * 2);
+	chs = uinfo->value.enumerated.item * 2 + spec->min_channel_count;
+	sprintf(uinfo->value.enumerated.name, "%dch", chs);
 	return 0;
 }
 
@@ -1624,7 +1656,8 @@ static int ch_mode_get(struct snd_kcontrol *kcontrol,
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct hda_gen_spec *spec = codec->spec;
-	ucontrol->value.enumerated.item[0] = (spec->ext_channel_count - 1) / 2;
+	ucontrol->value.enumerated.item[0] =
+		(spec->ext_channel_count - spec->min_channel_count) / 2;
 	return 0;
 }
 
@@ -1672,9 +1705,9 @@ static int ch_mode_put(struct snd_kcontrol *kcontrol,
 	ch = ucontrol->value.enumerated.item[0];
 	if (ch < 0 || ch > spec->multi_ios)
 		return -EINVAL;
-	if (ch == (spec->ext_channel_count - 1) / 2)
+	if (ch == (spec->ext_channel_count - spec->min_channel_count) / 2)
 		return 0;
-	spec->ext_channel_count = (ch + 1) * 2;
+	spec->ext_channel_count = ch * 2 + spec->min_channel_count;
 	for (i = 0; i < spec->multi_ios; i++)
 		set_multi_io(codec, i, i < ch);
 	spec->multiout.max_channels = max(spec->ext_channel_count,
@@ -3125,17 +3158,16 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
 	if (err < 0)
 		return err;
 
-	/* check the multiple speaker pins */
-	if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
-		spec->const_channel_count = cfg->line_outs * 2;
-	else
-		spec->const_channel_count = cfg->speaker_outs * 2;
-
-	if (spec->multi_ios > 0)
-		spec->multiout.max_channels = max(spec->ext_channel_count,
-						  spec->const_channel_count);
-	else
-		spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+	spec->const_channel_count = spec->ext_channel_count;
+	/* check the multiple speaker and headphone pins */
+	if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+		spec->const_channel_count = max(spec->const_channel_count,
+						cfg->speaker_outs * 2);
+	if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+		spec->const_channel_count = max(spec->const_channel_count,
+						cfg->hp_outs * 2);
+	spec->multiout.max_channels = max(spec->ext_channel_count,
+					  spec->const_channel_count);
 
 	err = check_auto_mute_availability(codec);
 	if (err < 0)
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index 00a1eab..b65769c 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -117,8 +117,20 @@ struct hda_gen_spec {
 	unsigned int cur_mux[3];
 
 	/* channel model */
-	int const_channel_count;	/* min. channel count (for speakers) */
-	int ext_channel_count;		/* current channel count for multi-io */
+	/* min_channel_count contains the minimum channel count for primary
+	 * outputs.  When multi_ios is set, the channels can be configured
+	 * between min_channel_count and (min_channel_count + multi_ios * 2).
+	 *
+	 * ext_channel_count contains the current channel count of the primary
+	 * out.  This varies in the range above.
+	 *
+	 * Meanwhile, const_channel_count is the channel count for all outputs
+	 * including headphone and speakers.  It's a constant value, and the
+	 * PCM is set up as max(ext_channel_count, const_channel_count).
+	 */
+	int min_channel_count;		/* min. channel count for primary out */
+	int ext_channel_count;		/* current channel count for primary */
+	int const_channel_count;	/* channel count for all */
 
 	/* PCM information */
 	struct hda_pcm pcm_rec[3];	/* used in build_pcms() */
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 109/112] ALSA: hda - Manage input paths via path indices
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (107 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 108/112] ALSA: hda - Fix multi-io channel mode management Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 110/112] ALSA: hda - Re-define snd_hda_parse_nid_path() Takashi Iwai
                   ` (5 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

... like we did for output and loopback paths.
It makes the code slightly easier.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 44 ++++++++++++++++++--------------------------
 sound/pci/hda/hda_generic.h |  1 +
 2 files changed, 19 insertions(+), 26 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 193dd55..f04a568 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -1168,6 +1168,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
 	memset(spec->speaker_paths, 0, sizeof(spec->speaker_paths));
 	memset(spec->aamix_out_paths, 0, sizeof(spec->aamix_out_paths));
 	memset(spec->digout_paths, 0, sizeof(spec->digout_paths));
+	memset(spec->input_paths, 0, sizeof(spec->input_paths));
 	memset(spec->loopback_paths, 0, sizeof(spec->loopback_paths));
 	memset(&spec->digin_path, 0, sizeof(spec->digin_path));
 
@@ -2056,6 +2057,7 @@ static int create_input_ctls(struct hda_codec *codec)
 	for (i = 0; i < cfg->num_inputs; i++) {
 		hda_nid_t pin;
 		const char *label;
+		int imux_idx;
 		bool imux_added;
 
 		pin = cfg->inputs[i].pin;
@@ -2081,24 +2083,23 @@ static int create_input_ctls(struct hda_codec *codec)
 		}
 
 		imux_added = false;
+		imux_idx = imux->num_items;
 		for (c = 0; c < num_adcs; c++) {
 			struct nid_path *path;
 			hda_nid_t adc = spec->adc_nids[c];
 
 			if (!is_reachable_path(codec, pin, adc))
 				continue;
-			path = snd_array_new(&spec->paths);
-			if (!path)
-				return -ENOMEM;
-			memset(path, 0, sizeof(*path));
-			if (!snd_hda_parse_nid_path(codec, pin, adc, HDA_PARSE_ALL, path)) {
+			path = snd_hda_add_new_path(codec, pin, adc, HDA_PARSE_ALL);
+			if (!path) {
 				snd_printd(KERN_ERR
 					   "invalid input path 0x%x -> 0x%x\n",
 					   pin, adc);
-				spec->paths.used--;
 				continue;
 			}
 			print_nid_path("input", path);
+			spec->input_paths[imux_idx][c] =
+				snd_hda_get_path_idx(codec, path);
 
 			if (!imux_added) {
 				spec->imux_pins[imux->num_items] = pin;
@@ -2117,13 +2118,13 @@ static int create_input_ctls(struct hda_codec *codec)
  * input source mux
  */
 
-/* get the ADC NID corresponding to the given index */
-static hda_nid_t get_adc_nid(struct hda_codec *codec, int adc_idx, int imux_idx)
+/* get the input path specified by the given adc and imux indices */
+static struct nid_path *get_input_path(struct hda_codec *codec, int adc_idx, int imux_idx)
 {
 	struct hda_gen_spec *spec = codec->spec;
 	if (spec->dyn_adc_switch)
 		adc_idx = spec->dyn_adc_idx[imux_idx];
-	return spec->adc_nids[adc_idx];
+	return snd_hda_get_path_from_idx(codec, spec->input_paths[imux_idx][adc_idx]);
 }
 
 static int mux_select(struct hda_codec *codec, unsigned int adc_idx,
@@ -2192,9 +2193,8 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol,
 	 */
 	codec->cached_write = 1;
 	for (i = 0; i < imux->num_items; i++) {
-		path = snd_hda_get_nid_path(codec, spec->imux_pins[i],
-					    get_adc_nid(codec, adc_idx, i));
-		if (!path->ctls[type])
+		path = get_input_path(codec, adc_idx, i);
+		if (!path || !path->ctls[type])
 			continue;
 		kcontrol->private_value = path->ctls[type];
 		err = func(kcontrol, ucontrol);
@@ -2394,21 +2394,18 @@ static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx,
 /* return the vol ctl when used first in the imux list */
 static unsigned int get_first_cap_ctl(struct hda_codec *codec, int idx, int type)
 {
-	struct hda_gen_spec *spec = codec->spec;
 	struct nid_path *path;
 	unsigned int ctl;
 	int i;
 
-	path = snd_hda_get_nid_path(codec, spec->imux_pins[idx],
-				    get_adc_nid(codec, 0, idx));
+	path = get_input_path(codec, 0, idx);
 	if (!path)
 		return 0;
 	ctl = path->ctls[type];
 	if (!ctl)
 		return 0;
 	for (i = 0; i < idx - 1; i++) {
-		path = snd_hda_get_nid_path(codec, spec->imux_pins[i],
-					    get_adc_nid(codec, 0, i));
+		path = get_input_path(codec, 0, i);
 		if (path && path->ctls[type] == ctl)
 			return 0;
 	}
@@ -2474,8 +2471,7 @@ static int create_capture_mixers(struct hda_codec *codec)
 		vol = sw = 0;
 		for (i = 0; i < imux->num_items; i++) {
 			struct nid_path *path;
-			path = snd_hda_get_nid_path(codec, spec->imux_pins[i],
-						    get_adc_nid(codec, n, i));
+			path = get_input_path(codec, n, i);
 			if (!path)
 				continue;
 			parse_capvol_in_path(codec, path);
@@ -2633,9 +2629,7 @@ static int mux_select(struct hda_codec *codec, unsigned int adc_idx,
 	if (spec->cur_mux[adc_idx] == idx)
 		return 0;
 
-	path = snd_hda_get_nid_path(codec,
-				    spec->imux_pins[spec->cur_mux[adc_idx]],
-				    spec->adc_nids[adc_idx]);
+	path = get_input_path(codec, adc_idx, spec->cur_mux[adc_idx]);
 	if (!path)
 		return 0;
 	if (path->active)
@@ -2649,8 +2643,7 @@ static int mux_select(struct hda_codec *codec, unsigned int adc_idx,
 	if (spec->dyn_adc_switch)
 		dyn_adc_pcm_resetup(codec, idx);
 
-	path = snd_hda_get_nid_path(codec, spec->imux_pins[idx],
-				    get_adc_nid(codec, adc_idx, idx));
+	path = get_input_path(codec, adc_idx, idx);
 	if (!path)
 		return 0;
 	if (path->active)
@@ -3887,8 +3880,7 @@ static void init_input_src(struct hda_codec *codec)
 
 	for (c = 0; c < nums; c++) {
 		for (i = 0; i < imux->num_items; i++) {
-			path = snd_hda_get_nid_path(codec, spec->imux_pins[i],
-						    get_adc_nid(codec, c, i));
+			path = get_input_path(codec, c, i);
 			if (path) {
 				bool active = path->active;
 				if (i == spec->cur_mux[c])
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index b65769c..1ad9127 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -156,6 +156,7 @@ struct hda_gen_spec {
 	int speaker_paths[AUTO_CFG_MAX_OUTS];
 	int aamix_out_paths[3];
 	int digout_paths[AUTO_CFG_MAX_OUTS];
+	int input_paths[HDA_MAX_NUM_INPUTS][AUTO_CFG_MAX_OUTS];
 	int loopback_paths[HDA_MAX_NUM_INPUTS];
 	int digin_path;
 
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 110/112] ALSA: hda - Re-define snd_hda_parse_nid_path()
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (108 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 109/112] ALSA: hda - Manage input paths via path indices Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 111/112] ALSA: hda - Handle BOTH jack port as a fixed output Takashi Iwai
                   ` (4 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

This commit modifies the definition of snd_hda_parse_nid_path()
slightly, now with_aa_mix argument is changed to anchor_nid, so that
it can handle any NID generically as an anchor point to include or
exclude.

The with_aa_mix field in struct nid_path is removed again by this
change.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 102 ++++++++++++++++++++++++--------------------
 sound/pci/hda/hda_generic.h |  12 +-----
 2 files changed, 57 insertions(+), 57 deletions(-)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index f04a568..ba88be2 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -87,9 +87,25 @@ EXPORT_SYMBOL_HDA(snd_hda_gen_spec_free);
  * parsing paths
  */
 
+/* return the position of NID in the list, or -1 if not found */
+static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
+{
+	int i;
+	for (i = 0; i < nums; i++)
+		if (list[i] == nid)
+			return i;
+	return -1;
+}
+
+/* return true if the given NID is contained in the path */
+static bool is_nid_contained(struct nid_path *path, hda_nid_t nid)
+{
+	return find_idx_in_nid_list(nid, path->path, path->depth) >= 0;
+}
+
 static struct nid_path *get_nid_path(struct hda_codec *codec,
 				     hda_nid_t from_nid, hda_nid_t to_nid,
-				     int with_aa_mix)
+				     int anchor_nid)
 {
 	struct hda_gen_spec *spec = codec->spec;
 	int i;
@@ -100,8 +116,9 @@ static struct nid_path *get_nid_path(struct hda_codec *codec,
 			continue;
 		if ((!from_nid || path->path[0] == from_nid) &&
 		    (!to_nid || path->path[path->depth - 1] == to_nid)) {
-			if (with_aa_mix == HDA_PARSE_ALL ||
-			    path->with_aa_mix == with_aa_mix)
+			if (!anchor_nid ||
+			    (anchor_nid > 0 && is_nid_contained(path, anchor_nid)) ||
+			    (anchor_nid < 0 && !is_nid_contained(path, anchor_nid)))
 				return path;
 		}
 	}
@@ -114,7 +131,7 @@ static struct nid_path *get_nid_path(struct hda_codec *codec,
 struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec,
 				      hda_nid_t from_nid, hda_nid_t to_nid)
 {
-	return get_nid_path(codec, from_nid, to_nid, HDA_PARSE_ALL);
+	return get_nid_path(codec, from_nid, to_nid, 0);
 }
 EXPORT_SYMBOL_HDA(snd_hda_get_nid_path);
 
@@ -213,17 +230,16 @@ static void print_nid_path(const char *pfx, struct nid_path *path)
 /* called recursively */
 static bool __parse_nid_path(struct hda_codec *codec,
 			     hda_nid_t from_nid, hda_nid_t to_nid,
-			     int with_aa_mix, struct nid_path *path, int depth)
+			     int anchor_nid, struct nid_path *path,
+			     int depth)
 {
-	struct hda_gen_spec *spec = codec->spec;
 	const hda_nid_t *conn;
 	int i, nums;
 
-	if (to_nid == spec->mixer_nid) {
-		if (with_aa_mix == HDA_PARSE_NO_AAMIX)
-			return false;
-		with_aa_mix = HDA_PARSE_ALL; /* mark aa-mix is included */
-	}
+	if (to_nid == anchor_nid)
+		anchor_nid = 0; /* anchor passed */
+	else if (to_nid == (hda_nid_t)(-anchor_nid))
+		return false; /* hit the exclusive nid */
 
 	nums = snd_hda_get_conn_list(codec, to_nid, &conn);
 	for (i = 0; i < nums; i++) {
@@ -236,8 +252,8 @@ static bool __parse_nid_path(struct hda_codec *codec,
 			    is_dac_already_used(codec, conn[i]))
 				continue;
 		}
-		/* aa-mix is requested but not included? */
-		if (!(spec->mixer_nid && with_aa_mix == HDA_PARSE_ONLY_AAMIX))
+		/* anchor is not requested or already passed? */
+		if (anchor_nid <= 0)
 			goto found;
 	}
 	if (depth >= MAX_NID_PATH_DEPTH)
@@ -249,15 +265,13 @@ static bool __parse_nid_path(struct hda_codec *codec,
 		    type == AC_WID_PIN)
 			continue;
 		if (__parse_nid_path(codec, from_nid, conn[i],
-				     with_aa_mix, path, depth + 1))
+				     anchor_nid, path, depth + 1))
 			goto found;
 	}
 	return false;
 
  found:
 	path->path[path->depth] = conn[i];
-	if (conn[i] == spec->mixer_nid)
-		path->with_aa_mix = true;
 	path->idx[path->depth + 1] = i;
 	if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX)
 		path->multi[path->depth + 1] = 1;
@@ -267,17 +281,17 @@ static bool __parse_nid_path(struct hda_codec *codec,
 
 /* parse the widget path from the given nid to the target nid;
  * when @from_nid is 0, try to find an empty DAC;
- * when @with_aa_mix is HDA_PARSE_NO_AAMIX, paths with spec->mixer_nid are
- * excluded, only the paths that don't go through the mixer will be chosen.
- * when @with_aa_mix is HDA_PARSE_ONLY_AAMIX, only the paths going through
- * spec->mixer_nid will be chosen.
- * when @with_aa_mix is HDA_PARSE_ALL, no special handling about mixer widget.
+ * when @anchor_nid is set to a positive value, only paths through the widget
+ * with the given value are evaluated.
+ * when @anchor_nid is set to a negative value, paths through the widget
+ * with the negative of given value are excluded, only other paths are chosen.
+ * when @anchor_nid is zero, no special handling about path selection.
  */
 bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
-			    hda_nid_t to_nid, int with_aa_mix,
+			    hda_nid_t to_nid, int anchor_nid,
 			    struct nid_path *path)
 {
-	if (__parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path, 1)) {
+	if (__parse_nid_path(codec, from_nid, to_nid, anchor_nid, path, 1)) {
 		path->path[path->depth] = to_nid;
 		path->depth++;
 		return true;
@@ -292,7 +306,7 @@ EXPORT_SYMBOL_HDA(snd_hda_parse_nid_path);
  */
 struct nid_path *
 snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid,
-		     hda_nid_t to_nid, int with_aa_mix)
+		     hda_nid_t to_nid, int anchor_nid)
 {
 	struct hda_gen_spec *spec = codec->spec;
 	struct nid_path *path;
@@ -301,7 +315,7 @@ snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid,
 		return NULL;
 
 	/* check whether the path has been already added */
-	path = get_nid_path(codec, from_nid, to_nid, with_aa_mix);
+	path = get_nid_path(codec, from_nid, to_nid, anchor_nid);
 	if (path)
 		return path;
 
@@ -309,7 +323,7 @@ snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid,
 	if (!path)
 		return NULL;
 	memset(path, 0, sizeof(*path));
-	if (snd_hda_parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path))
+	if (snd_hda_parse_nid_path(codec, from_nid, to_nid, anchor_nid, path))
 		return path;
 	/* push back */
 	spec->paths.used--;
@@ -909,10 +923,10 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs,
 			else
 				badness += bad->no_dac;
 		}
-		path = snd_hda_add_new_path(codec, dac, pin, HDA_PARSE_NO_AAMIX);
+		path = snd_hda_add_new_path(codec, dac, pin, -spec->mixer_nid);
 		if (!path && !i && spec->mixer_nid) {
 			/* try with aamix */
-			path = snd_hda_add_new_path(codec, dac, pin, HDA_PARSE_ALL);
+			path = snd_hda_add_new_path(codec, dac, pin, 0);
 		}
 		if (!path)
 			dac = dacs[i] = 0;
@@ -1038,7 +1052,8 @@ static int fill_multi_ios(struct hda_codec *codec,
 				badness++;
 				continue;
 			}
-			path = snd_hda_add_new_path(codec, dac, nid, HDA_PARSE_NO_AAMIX);
+			path = snd_hda_add_new_path(codec, dac, nid,
+						    -spec->mixer_nid);
 			if (!path) {
 				badness++;
 				continue;
@@ -1093,9 +1108,10 @@ static bool map_singles(struct hda_codec *codec, int outs,
 		dac = get_dac_if_single(codec, pins[i]);
 		if (!dac)
 			continue;
-		path = snd_hda_add_new_path(codec, dac, pins[i], HDA_PARSE_NO_AAMIX);
+		path = snd_hda_add_new_path(codec, dac, pins[i],
+					    -spec->mixer_nid);
 		if (!path && !i && spec->mixer_nid)
-			path = snd_hda_add_new_path(codec, dac, pins[i], HDA_PARSE_ALL);
+			path = snd_hda_add_new_path(codec, dac, pins[i], 0);
 		if (path) {
 			dacs[i] = dac;
 			found = true;
@@ -1110,14 +1126,16 @@ static bool map_singles(struct hda_codec *codec, int outs,
 /* create a new path including aamix if available, and return its index */
 static int check_aamix_out_path(struct hda_codec *codec, int path_idx)
 {
+	struct hda_gen_spec *spec = codec->spec;
 	struct nid_path *path;
 
 	path = snd_hda_get_path_from_idx(codec, path_idx);
-	if (!path || !path->depth || path->with_aa_mix)
+	if (!path || !path->depth ||
+	    is_nid_contained(path, spec->mixer_nid))
 		return 0;
 	path = snd_hda_add_new_path(codec, path->path[0],
 				    path->path[path->depth - 1],
-				    HDA_PARSE_ONLY_AAMIX);
+				    spec->mixer_nid);
 	if (!path)
 		return 0;
 	print_nid_path("output-aamix", path);
@@ -1917,7 +1935,7 @@ static int new_analog_input(struct hda_codec *codec, int input_idx,
 	    !nid_has_mute(codec, mix_nid, HDA_INPUT))
 		return 0; /* no need for analog loopback */
 
-	path = snd_hda_add_new_path(codec, pin, mix_nid, HDA_PARSE_ALL);
+	path = snd_hda_add_new_path(codec, pin, mix_nid, 0);
 	if (!path)
 		return -EINVAL;
 	print_nid_path("loopback", path);
@@ -2090,7 +2108,7 @@ static int create_input_ctls(struct hda_codec *codec)
 
 			if (!is_reachable_path(codec, pin, adc))
 				continue;
-			path = snd_hda_add_new_path(codec, pin, adc, HDA_PARSE_ALL);
+			path = snd_hda_add_new_path(codec, pin, adc, 0);
 			if (!path) {
 				snd_printd(KERN_ERR
 					   "invalid input path 0x%x -> 0x%x\n",
@@ -2565,7 +2583,7 @@ static void parse_digital(struct hda_codec *codec)
 		dig_nid = look_for_dac(codec, pin, true);
 		if (!dig_nid)
 			continue;
-		path = snd_hda_add_new_path(codec, dig_nid, pin, HDA_PARSE_ALL);
+		path = snd_hda_add_new_path(codec, dig_nid, pin, 0);
 		if (!path)
 			continue;
 		print_nid_path("digout", path);
@@ -2593,7 +2611,7 @@ static void parse_digital(struct hda_codec *codec)
 				continue;
 			path = snd_hda_add_new_path(codec,
 						    spec->autocfg.dig_in_pin,
-						    dig_nid, HDA_PARSE_ALL);
+						    dig_nid, 0);
 			if (path) {
 				print_nid_path("digin", path);
 				path->active = true;
@@ -2969,16 +2987,6 @@ static int check_auto_mute_availability(struct hda_codec *codec)
 	return 0;
 }
 
-/* return the position of NID in the list, or -1 if not found */
-static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
-{
-	int i;
-	for (i = 0; i < nums; i++)
-		if (list[i] == nid)
-			return i;
-	return -1;
-}
-
 /* check whether all auto-mic pins are valid; setup indices if OK */
 static bool auto_mic_check_imux(struct hda_codec *codec)
 {
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index 1ad9127..343195c 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -53,7 +53,6 @@ struct nid_path {
 	unsigned char multi[MAX_NID_PATH_DEPTH];
 	unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */
 	bool active;
-	bool with_aa_mix;
 };
 
 /* mic/line-in auto switching entry */
@@ -237,19 +236,12 @@ struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec,
 				      hda_nid_t from_nid, hda_nid_t to_nid);
 int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path);
 struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx);
-
-enum {
-	HDA_PARSE_NO_AAMIX,
-	HDA_PARSE_ONLY_AAMIX,
-	HDA_PARSE_ALL,
-};
-
 bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
-			    hda_nid_t to_nid, int with_aa_mix,
+			    hda_nid_t to_nid, int anchor_nid,
 			    struct nid_path *path);
 struct nid_path *
 snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid,
-		     hda_nid_t to_nid, int with_aa_mix);
+		     hda_nid_t to_nid, int anchor_nid);
 void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path,
 			   bool enable, bool add_aamix);
 
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 111/112] ALSA: hda - Handle BOTH jack port as a fixed output
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (109 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 110/112] ALSA: hda - Re-define snd_hda_parse_nid_path() Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:39 ` [PATCH 112/112] ALSA: hda - Add a flag to suppress mic auto-switch Takashi Iwai
                   ` (3 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

When the default config value shows the connection AC_JACK_PORT_BOTH,
it's better to handle it as a speaker pin.  This makes the behavior
consistent in snd_hda_get_pin_label() and snd_hda_parse_pin_defcfg().

There are only few old machines showing this attribute, and all of
them are actually fixed speaker pins, as far as I know.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_auto_parser.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c
index 44c81d3..6a01c01 100644
--- a/sound/pci/hda/hda_auto_parser.c
+++ b/sound/pci/hda/hda_auto_parser.c
@@ -156,7 +156,8 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
 
 		/* workaround for buggy BIOS setups */
 		if (dev == AC_JACK_LINE_OUT) {
-			if (conn == AC_JACK_PORT_FIXED)
+			if (conn == AC_JACK_PORT_FIXED ||
+			    conn == AC_JACK_PORT_BOTH)
 				dev = AC_JACK_SPEAKER;
 		}
 
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* [PATCH 112/112] ALSA: hda - Add a flag to suppress mic auto-switch
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (110 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 111/112] ALSA: hda - Handle BOTH jack port as a fixed output Takashi Iwai
@ 2013-01-08 11:39 ` Takashi Iwai
  2013-01-08 11:47 ` [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (2 subsequent siblings)
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:39 UTC (permalink / raw)
  To: alsa-devel

Add a new flag spec->suppress_mic_auto_switch for codecs that don't
support unsol events properly like VT1708.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/hda/hda_generic.c | 3 +++
 sound/pci/hda/hda_generic.h | 1 +
 2 files changed, 4 insertions(+)

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index ba88be2..2e02094 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -3032,6 +3032,9 @@ static int check_auto_mic_availability(struct hda_codec *codec)
 	unsigned int types;
 	int i, num_pins;
 
+	if (spec->suppress_auto_mic)
+		return 0;
+
 	types = 0;
 	num_pins = 0;
 	for (i = 0; i < cfg->num_inputs; i++) {
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index 343195c..1763e33 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -175,6 +175,7 @@ struct hda_gen_spec {
 	unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */
 	unsigned int automute_lo_possible:1;	  /* there are line outs and HP */
 	unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */
+	unsigned int suppress_auto_mic:1; /* suppress input jack auto switch */
 	unsigned int line_in_auto_switch:1; /* allow line-in auto switch */
 
 	/* other flags */
-- 
1.8.0.1

^ permalink raw reply related	[flat|nested] 123+ messages in thread

* Re: [PATCH RFC 000/112] HD-audio generic parser improvements
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (111 preceding siblings ...)
  2013-01-08 11:39 ` [PATCH 112/112] ALSA: hda - Add a flag to suppress mic auto-switch Takashi Iwai
@ 2013-01-08 11:47 ` Takashi Iwai
  2013-01-08 12:20 ` David Henningsson
  2013-01-08 13:03 ` Jaroslav Kysela
  114 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 11:47 UTC (permalink / raw)
  To: alsa-devel

At Tue,  8 Jan 2013 12:37:53 +0100,
Takashi Iwai wrote:
> 
> Hi,
> 
> this is a large series of patches to implement the really generic
> HD-audio codec parser usable for various codecs.  It's based on the
> Realtek codec parser, so I started hacking patch_realtek.c for more
> generalization and improvements.  Then it's merged to the existing
> generic parser code, hda_generic.c, and let Realtek parser just uses
> this new parser code.  In the end, patch_realtek.c contains only
> the device specific fixups and the call of generic parser functions.
> 
> The series contains a few improvements in the HD-audio core code,
> some of which aren't really dependent on the generic parser code.
> They are just there as I've developed this for a couple of weeks.
> 
> After this patchset, most of other codecs can be migrated as well.
> Currently analog, ca0110, cirrus, cmedia, conexant and via codecs
> have been already rewritten (i.e. the own parser codes dropped).
> patch_sigmatel.c is still not converted yet, but can be done soon
> later.  I'm going to send the conversion patches for these codecs
> right after this series.
> 
> I don't expect to merge all conversions of codec parsers in 3.9
> kernel, but at least, the conversion of Realtek and this generic
> parser code addition can be merged to 3.9.  Maybe minor and easy ones
> like ca0110 and cmedia can be merged as well.

One thing I forgot to mention:
the whole patches are found in sound-unstable git tree
test/hda-gen-parser branch.
  git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-unstable.git

The test/hda-migrate branch contains the patches for converting codec
drivers to use generic parser.


Takashi

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH RFC 000/112] HD-audio generic parser improvements
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (112 preceding siblings ...)
  2013-01-08 11:47 ` [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
@ 2013-01-08 12:20 ` David Henningsson
  2013-01-08 12:44   ` Takashi Iwai
  2013-01-08 13:03 ` Jaroslav Kysela
  114 siblings, 1 reply; 123+ messages in thread
From: David Henningsson @ 2013-01-08 12:20 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel

On 01/08/2013 12:37 PM, Takashi Iwai wrote:
> Hi,
>
> this is a large series of patches to implement the really generic
> HD-audio codec parser usable for various codecs.

So it's really happening, wow :-)

Do you have a tree somewhere to make it easier for me to run it through 
the automated hda-emu tester, to check for regressions and such?



-- 
David Henningsson, Canonical Ltd.
https://launchpad.net/~diwic

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH RFC 000/112] HD-audio generic parser improvements
  2013-01-08 12:20 ` David Henningsson
@ 2013-01-08 12:44   ` Takashi Iwai
  0 siblings, 0 replies; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 12:44 UTC (permalink / raw)
  To: David Henningsson; +Cc: alsa-devel

At Tue, 08 Jan 2013 13:20:04 +0100,
David Henningsson wrote:
> 
> On 01/08/2013 12:37 PM, Takashi Iwai wrote:
> > Hi,
> >
> > this is a large series of patches to implement the really generic
> > HD-audio codec parser usable for various codecs.
> 
> So it's really happening, wow :-)
> 
> Do you have a tree somewhere to make it easier for me to run it through 
> the automated hda-emu tester, to check for regressions and such?

It's in sound-unstable git tree test/hda-gen-parser branch.

  git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-unstable.git

This branch doesn't contain the patches to convert codec drivers,
though.  The branch including all patches is test/hda-migrate branch.

I posted the same info after git-send-email, but it seems that ML
server is still loaded and my post doesn't come up yet...


Takashi

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH RFC 000/112] HD-audio generic parser improvements
  2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
                   ` (113 preceding siblings ...)
  2013-01-08 12:20 ` David Henningsson
@ 2013-01-08 13:03 ` Jaroslav Kysela
  2013-01-08 13:20   ` Takashi Iwai
  114 siblings, 1 reply; 123+ messages in thread
From: Jaroslav Kysela @ 2013-01-08 13:03 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel

Date 8.1.2013 12:37, Takashi Iwai wrote:
> Hi,
> 
> this is a large series of patches to implement the really generic
> HD-audio codec parser usable for various codecs.  It's based on the
> Realtek codec parser, so I started hacking patch_realtek.c for more
> generalization and improvements.  Then it's merged to the existing
> generic parser code, hda_generic.c, and let Realtek parser just uses
> this new parser code.  In the end, patch_realtek.c contains only
> the device specific fixups and the call of generic parser functions.

Great step forward. Thanks.

In my opinion, it would be great to handle all fixups in some "user
space" compatible mode. I mean, do not use the C code for fixups, but
use an universal description code which can be loaded from the user
space probably as ASCII text (firmware like behaviour), too. This code
can be buildin to the kernel modules in a default version. I don't mean
the current implemented patch processing, but full codec initialization.
I think that it may be last change to bring the HDA code to more
maintenable state. Just an idea to avoid further HDA code rewrites which
happen periodically. And now (with your rewrite) it's really good time
to propose also this change.

Please, provide the GIT URL with these changes.

					Thanks,
						Jaroslav

-- 
Jaroslav Kysela <perex@perex.cz>
Linux Kernel Sound Maintainer
ALSA Project; Red Hat, Inc.

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH RFC 000/112] HD-audio generic parser improvements
  2013-01-08 13:03 ` Jaroslav Kysela
@ 2013-01-08 13:20   ` Takashi Iwai
  2013-01-08 14:10     ` Jaroslav Kysela
  0 siblings, 1 reply; 123+ messages in thread
From: Takashi Iwai @ 2013-01-08 13:20 UTC (permalink / raw)
  To: Jaroslav Kysela; +Cc: alsa-devel

At Tue, 08 Jan 2013 14:03:23 +0100,
Jaroslav Kysela wrote:
> 
> Date 8.1.2013 12:37, Takashi Iwai wrote:
> > Hi,
> > 
> > this is a large series of patches to implement the really generic
> > HD-audio codec parser usable for various codecs.  It's based on the
> > Realtek codec parser, so I started hacking patch_realtek.c for more
> > generalization and improvements.  Then it's merged to the existing
> > generic parser code, hda_generic.c, and let Realtek parser just uses
> > this new parser code.  In the end, patch_realtek.c contains only
> > the device specific fixups and the call of generic parser functions.
> 
> Great step forward. Thanks.
> 
> In my opinion, it would be great to handle all fixups in some "user
> space" compatible mode. I mean, do not use the C code for fixups, but
> use an universal description code which can be loaded from the user
> space probably as ASCII text (firmware like behaviour), too. This code
> can be buildin to the kernel modules in a default version. I don't mean
> the current implemented patch processing, but full codec initialization.
> I think that it may be last change to bring the HDA code to more
> maintenable state. Just an idea to avoid further HDA code rewrites which
> happen periodically. And now (with your rewrite) it's really good time
> to propose also this change.

Well, most of required fixups are corrections of the wrong BIOS
information and/or the missing information like some extra flags.
They can be well covered by the current "patch" firmware.  And we
can build this text into the kernel driver as a default database
instead of hard-coded C array (although I'm not sure whether this will
give so much benefit).

The rest is some exceptions where some real codes are required.  This
can't be expressed in a static form.  And any byte-code interpreter in
the driver is a bad idea, and it doesn't make the whole maintenance
scheme easier...


> Please, provide the GIT URL with these changes.

Yep, already posted.


thanks,

Takashi

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH RFC 000/112] HD-audio generic parser improvements
  2013-01-08 13:20   ` Takashi Iwai
@ 2013-01-08 14:10     ` Jaroslav Kysela
  0 siblings, 0 replies; 123+ messages in thread
From: Jaroslav Kysela @ 2013-01-08 14:10 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel

Date 8.1.2013 14:20, Takashi Iwai wrote:
> At Tue, 08 Jan 2013 14:03:23 +0100,
> Jaroslav Kysela wrote:
>>
>> Date 8.1.2013 12:37, Takashi Iwai wrote:
>>> Hi,
>>>
>>> this is a large series of patches to implement the really generic
>>> HD-audio codec parser usable for various codecs.  It's based on the
>>> Realtek codec parser, so I started hacking patch_realtek.c for more
>>> generalization and improvements.  Then it's merged to the existing
>>> generic parser code, hda_generic.c, and let Realtek parser just uses
>>> this new parser code.  In the end, patch_realtek.c contains only
>>> the device specific fixups and the call of generic parser functions.
>>
>> Great step forward. Thanks.
>>
>> In my opinion, it would be great to handle all fixups in some "user
>> space" compatible mode. I mean, do not use the C code for fixups, but
>> use an universal description code which can be loaded from the user
>> space probably as ASCII text (firmware like behaviour), too. This code
>> can be buildin to the kernel modules in a default version. I don't mean
>> the current implemented patch processing, but full codec initialization.
>> I think that it may be last change to bring the HDA code to more
>> maintenable state. Just an idea to avoid further HDA code rewrites which
>> happen periodically. And now (with your rewrite) it's really good time
>> to propose also this change.
> 
> Well, most of required fixups are corrections of the wrong BIOS
> information and/or the missing information like some extra flags.
> They can be well covered by the current "patch" firmware.  And we
> can build this text into the kernel driver as a default database
> instead of hard-coded C array (although I'm not sure whether this will
> give so much benefit).
> 
> The rest is some exceptions where some real codes are required.  This
> can't be expressed in a static form.  And any byte-code interpreter in
> the driver is a bad idea, and it doesn't make the whole maintenance
> scheme easier...

I believe that a good compromise might be implemented. The special cases
can be also part of the parser having configurable values if possible,
so the whole codec description can be static.

The discussion seems to reach another point: One parser handling all
possible information or multiple parsers overriding the generic one
(actual state)?

>> Please, provide the GIT URL with these changes.
> 
> Yep, already posted.

Got it. Thanks.

					Jaroslav


-- 
Jaroslav Kysela <perex@perex.cz>
Linux Kernel Sound Maintainer
ALSA Project; Red Hat, Inc.

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 044/112] ALSA: hda - More generic auto-mic switching for Realtek codecs
  2013-01-08 11:38 ` [PATCH 044/112] ALSA: hda - More generic auto-mic switching for Realtek codecs Takashi Iwai
@ 2013-01-10  0:41   ` Raymond Yau
  2013-01-10 15:08     ` Takashi Iwai
  0 siblings, 1 reply; 123+ messages in thread
From: Raymond Yau @ 2013-01-10  0:41 UTC (permalink / raw)
  To: Takashi Iwai, lgperson; +Cc: alsa-devel

>
> This patch extends the capability of the auto-mic feature.
> Instead of limiting the automatic input-source selection only to the
> mics (internal, external and dock mics), allow it for generic inputs,
> e.g. switching between the rear line-in and the front mic.
>
> The logic is to check the attribute and location of input pins, and
> enable the automatic selection feature only if all such pins are in
> different locations (e.g. internal, front, rear, etc) and line-in or
> mic pins.  That is, if multiple input pins are assigned to a single
> location, the feature isn't enabled because we don't know the
> priority.
>
> (You may wonder why this restriction doesn't exist for the headphone
>  automute.  The reason is that the output case is different from the
>  input: the input source is an exclusive selection while the output
>  can be multiplexed.)
>
> Note that, for avoiding regressions, the line-in auto switching
> feature isn't activated as default.  It has to be set explicitly via
> spec->line_in_auto_switch flag in a fixup code.

Is this feature automatically disabled when rear Mic or line in Jack is
retasked as output ?

Does it mean that multi channel capture will not be implemented since it is
conflict with both dynamic adc  switching and auto Mic selection ?

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 044/112] ALSA: hda - More generic auto-mic switching for Realtek codecs
  2013-01-10  0:41   ` Raymond Yau
@ 2013-01-10 15:08     ` Takashi Iwai
  2013-01-17  2:47       ` FF
  0 siblings, 1 reply; 123+ messages in thread
From: Takashi Iwai @ 2013-01-10 15:08 UTC (permalink / raw)
  To: Raymond Yau; +Cc: alsa-devel, lgperson

At Thu, 10 Jan 2013 08:41:13 +0800,
Raymond Yau wrote:
> 
> >
> > This patch extends the capability of the auto-mic feature.
> > Instead of limiting the automatic input-source selection only to the
> > mics (internal, external and dock mics), allow it for generic inputs,
> > e.g. switching between the rear line-in and the front mic.
> >
> > The logic is to check the attribute and location of input pins, and
> > enable the automatic selection feature only if all such pins are in
> > different locations (e.g. internal, front, rear, etc) and line-in or
> > mic pins.  That is, if multiple input pins are assigned to a single
> > location, the feature isn't enabled because we don't know the
> > priority.
> >
> > (You may wonder why this restriction doesn't exist for the headphone
> >  automute.  The reason is that the output case is different from the
> >  input: the input source is an exclusive selection while the output
> >  can be multiplexed.)
> >
> > Note that, for avoiding regressions, the line-in auto switching
> > feature isn't activated as default.  It has to be set explicitly via
> > spec->line_in_auto_switch flag in a fixup code.
> 
> Is this feature automatically disabled when rear Mic or line in Jack is
> retasked as output ?
> 
> Does it mean that multi channel capture will not be implemented since it is
> conflict with both dynamic adc  switching and auto Mic selection ?

The recent jack detection codes (at least the code in
test/hda-gen-parser branch) checks the direction of the pinctl.
If a multi-io pin is configured as output, the mic autoswitch will
ignore this pin.  Vice versa for the auto-mute and headphone-as-mic
case.


Takashi

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 044/112] ALSA: hda - More generic auto-mic switching for Realtek codecs
  2013-01-10 15:08     ` Takashi Iwai
@ 2013-01-17  2:47       ` FF
  0 siblings, 0 replies; 123+ messages in thread
From: FF @ 2013-01-17  2:47 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: Raymond Yau, alsa-devel

Hello!
   where is the trace_hda_send_cmd function source code?I can not find it in the path of    alsa-kernel\pci\hda\hda_code.c and linux kenerl.
Thank you!
 

/*
 * Compose a 32bit command word to be sent to the HD-audio controller
 */
static inline unsigned int
make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
        unsigned int verb, unsigned int parm)
{
 u32 val;
 if ((codec->addr & ~0xf) || (direct & ~1) || (nid & ~0x7f) ||
     (verb & ~0xfff) || (parm & ~0xffff)) {
  printk(KERN_ERR "hda-codec: out of range cmd %x:%x:%x:%x:%x\n",
         codec->addr, direct, nid, verb, parm);
  return ~0;
 }
 val = (u32)codec->addr << 28;
 val |= (u32)direct << 27;
 val |= (u32)nid << 20;
 val |= verb << 8;
 val |= parm;
 return val;
}
/*
 * Send and receive a verb
 */
static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
      unsigned int *res)
{
 struct hda_bus *bus = codec->bus;
 int err;
 if (cmd == ~0)
  return -1;
 if (res)
  *res = -1;
 again:
 snd_hda_power_up(codec);
 mutex_lock(&bus->cmd_mutex);
 trace_hda_send_cmd(codec, cmd);
 err = bus->ops.command(bus, cmd);
 if (!err && res) {
  *res = bus->ops.get_response(bus, codec->addr);
  trace_hda_get_response(codec, *res);
 }
 mutex_unlock(&bus->cmd_mutex);
 snd_hda_power_down(codec);
 if (res && *res == -1 && bus->rirb_error) {
  if (bus->response_reset) {
   snd_printd("hda_codec: resetting BUS due to "
       "fatal communication error\n");
   trace_hda_bus_reset(bus);
   bus->ops.bus_reset(bus);
  }
  goto again;
 }
 /* clear reset-flag when the communication gets recovered */
 if (!err)
  bus->response_reset = 0;
 return err;
}









At 2013-01-10 23:08:52,"Takashi Iwai" <tiwai@suse.de> wrote:
>At Thu, 10 Jan 2013 08:41:13 +0800,
>Raymond Yau wrote:
>> 
>> >
>> > This patch extends the capability of the auto-mic feature.
>> > Instead of limiting the automatic input-source selection only to the
>> > mics (internal, external and dock mics), allow it for generic inputs,
>> > e.g. switching between the rear line-in and the front mic.
>> >
>> > The logic is to check the attribute and location of input pins, and
>> > enable the automatic selection feature only if all such pins are in
>> > different locations (e.g. internal, front, rear, etc) and line-in or
>> > mic pins.  That is, if multiple input pins are assigned to a single
>> > location, the feature isn't enabled because we don't know the
>> > priority.
>> >
>> > (You may wonder why this restriction doesn't exist for the headphone
>> >  automute.  The reason is that the output case is different from the
>> >  input: the input source is an exclusive selection while the output
>> >  can be multiplexed.)
>> >
>> > Note that, for avoiding regressions, the line-in auto switching
>> > feature isn't activated as default.  It has to be set explicitly via
>> > spec->line_in_auto_switch flag in a fixup code.
>> 
>> Is this feature automatically disabled when rear Mic or line in Jack is
>> retasked as output ?
>> 
>> Does it mean that multi channel capture will not be implemented since it is
>> conflict with both dynamic adc  switching and auto Mic selection ?
>
>The recent jack detection codes (at least the code in
>test/hda-gen-parser branch) checks the direction of the pinctl.
>If a multi-io pin is configured as output, the mic autoswitch will
>ignore this pin.  Vice versa for the auto-mute and headphone-as-mic
>case.
>
>
>Takashi

^ permalink raw reply	[flat|nested] 123+ messages in thread

* Re: [PATCH 108/112] ALSA: hda - Fix multi-io channel mode management
  2013-01-08 11:39 ` [PATCH 108/112] ALSA: hda - Fix multi-io channel mode management Takashi Iwai
@ 2013-02-21  8:03   ` Raymond Yau
  0 siblings, 0 replies; 123+ messages in thread
From: Raymond Yau @ 2013-02-21  8:03 UTC (permalink / raw)
  To: Takashi Iwai, alex.baldacchino.alsasub; +Cc: alsa-devel

>
> The multi-io channels can vary not only from 1 to 6 but also may vary
> from 6 to 8 or such.  At the same time, there are more speaker pins
> available than the primary output pins.  So, we need three variables
> to check: the minimum channel counts for primary outputs, the current
> channel counts for primary outputs, and the minimum channel counts for
> all outputs.
>

Under which condition will the driver create the 6 / 8 channel mode control
?

An unused DAC

desktop with 5 jacks at rear panel and retask Blue Line In jack as output
Jack for side channel (AsRock
890GX Extreme3)

^ permalink raw reply	[flat|nested] 123+ messages in thread

end of thread, other threads:[~2013-02-21  8:03 UTC | newest]

Thread overview: 123+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-01-08 11:37 [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
2013-01-08 11:37 ` [PATCH 001/112] ALSA: hda/realtek - Simplify alc_auto_is_dac_reachable() Takashi Iwai
2013-01-08 11:37 ` [PATCH 002/112] ALSA: hda/realtek - List up all available DACs Takashi Iwai
2013-01-08 11:37 ` [PATCH 003/112] ALSA: hda/realtek - Add output path parser Takashi Iwai
2013-01-08 11:37 ` [PATCH 004/112] ALSA: hda/realtek - Manage mixer controls in out_path list Takashi Iwai
2013-01-08 11:37 ` [PATCH 005/112] ALSA: hda - Fix mono amp values in proc output Takashi Iwai
2013-01-08 11:37 ` [PATCH 006/112] ALSA: hda/realtek - Reduce vol/mute ctl lookups at parsing codec Takashi Iwai
2013-01-08 11:38 ` [PATCH 007/112] ALSA: hda/realtek - Simplify the output volume initialization Takashi Iwai
2013-01-08 11:38 ` [PATCH 008/112] ALSA: hda/realtek - Make path->idx[] and path->multi[] consistent Takashi Iwai
2013-01-08 11:38 ` [PATCH 009/112] ALSA: hda/realtek - Parse input paths Takashi Iwai
2013-01-08 11:38 ` [PATCH 010/112] ALSA: hda/realtek - Parse analog loopback paths more generically Takashi Iwai
2013-01-08 11:38 ` [PATCH 011/112] ALSA: hda/realtek - Check amp capabilities of aa-mixer widget Takashi Iwai
2013-01-08 11:38 ` [PATCH 012/112] ALSA: hda/realtek - Fix initialization of input amps in output paths Takashi Iwai
2013-01-08 11:38 ` [PATCH 013/112] ALSA: hda - Remove snd_hda_codec_amp_update() call from patch_*.c Takashi Iwai
2013-01-08 11:38 ` [PATCH 014/112] ALSA: hda - Introduce cache & flush cmd / amp writes Takashi Iwai
2013-01-08 11:38 ` [PATCH 015/112] ALSA: hda - Introduce snd_hda_codec_amp_init*() Takashi Iwai
2013-01-08 11:38 ` [PATCH 016/112] ALSA: hda/realtek - Remove non-standard automute mode Takashi Iwai
2013-01-08 11:38 ` [PATCH 017/112] ALSA: hda/realtek - Add path active flag Takashi Iwai
2013-01-08 11:38 ` [PATCH 018/112] ALSA: hda/realtek - Consolidate is_reachable_path() Takashi Iwai
2013-01-08 11:38 ` [PATCH 019/112] ALSA: hda/realtek - Consolidate to a single path list Takashi Iwai
2013-01-08 11:38 ` [PATCH 020/112] ALSA: hda/realtek - Use path-based parser for digital outputs Takashi Iwai
2013-01-08 11:38 ` [PATCH 021/112] ALSA: hda/realtek - Rename get_out_path() to get_nid_path() Takashi Iwai
2013-01-08 11:38 ` [PATCH 022/112] ALSA: hda/realtek - Fix the initialization of pin amp-in Takashi Iwai
2013-01-08 11:38 ` [PATCH 023/112] ALSA: hda/realtek - Add missing initialization of multi-io routes Takashi Iwai
2013-01-08 11:38 ` [PATCH 024/112] ALSA: hda/realtek - Add boost volumes to path list Takashi Iwai
2013-01-08 11:38 ` [PATCH 025/112] ALSA: hda/realtek - Initialize loopback paths properly Takashi Iwai
2013-01-08 11:38 ` [PATCH 026/112] ALSA: hda/realtek - Don't change connection at path deactivation Takashi Iwai
2013-01-08 11:38 ` [PATCH 027/112] ALSA: hda/realtek - Make input path parser more generic Takashi Iwai
2013-01-08 11:38 ` [PATCH 028/112] ALSA: hda/realtek - Clean up some spec fields Takashi Iwai
2013-01-08 11:38 ` [PATCH 029/112] ALSA: hda/realtek - Remove superfluous input amp init Takashi Iwai
2013-01-08 11:38 ` [PATCH 030/112] ALSA: hda/realtek - Rename add_new_out_path() with add_new_nid_path() Takashi Iwai
2013-01-08 11:38 ` [PATCH 031/112] ALSA: hda/realtek - Parse digital input path Takashi Iwai
2013-01-08 11:38 ` [PATCH 032/112] ALSA: hda/realtek - Allow different pins for shared hp/mic vref check Takashi Iwai
2013-01-08 11:38 ` [PATCH 033/112] ALSA: hda/realtek - Drop auto_mic_valid_imux flag Takashi Iwai
2013-01-08 11:38 ` [PATCH 034/112] ALSA: hda/realtek - Remove unused fields and macro definitions Takashi Iwai
2013-01-08 11:38 ` [PATCH 035/112] ALSA: hda/realtek - Handle vmaster hook in the parser side Takashi Iwai
2013-01-08 11:38 ` [PATCH 036/112] ALSA: hda/realtek - Assign Master mixer when possible Takashi Iwai
2013-01-08 11:38 ` [PATCH 037/112] ALSA: hda/realtek - Merge a few split functions Takashi Iwai
2013-01-08 11:38 ` [PATCH 038/112] ALSA: hda/realtek - Allow passing name=NULL to alc_kcontrol_new() Takashi Iwai
2013-01-08 11:38 ` [PATCH 039/112] ALSA: hda/realtek - Allow multiple individual capture volume/switch controls Takashi Iwai
2013-01-08 11:38 ` [PATCH 040/112] ALSA: hda/realtek - Add conexant-style inverted dmic handling Takashi Iwai
2013-01-08 11:38 ` [PATCH 041/112] ALSA: hda - Move fixup code into struct hda_codec Takashi Iwai
2013-01-08 11:38 ` [PATCH 042/112] ALSA: hda/realtek - Fix split stereo dmic code Takashi Iwai
2013-01-08 11:38 ` [PATCH 043/112] ALSA: hda - Rearrange INPUT_PIN_ATTR_* Takashi Iwai
2013-01-08 11:38 ` [PATCH 044/112] ALSA: hda - More generic auto-mic switching for Realtek codecs Takashi Iwai
2013-01-10  0:41   ` Raymond Yau
2013-01-10 15:08     ` Takashi Iwai
2013-01-17  2:47       ` FF
2013-01-08 11:38 ` [PATCH 045/112] ALSA: hda/realtek - Remove redundant argument from alc_mux_select() Takashi Iwai
2013-01-08 11:38 ` [PATCH 046/112] ALSA: hda - Merge Realtek parser code to generic parser Takashi Iwai
2013-01-08 11:38 ` [PATCH 047/112] ALSA: hda - Add EAPD control " Takashi Iwai
2013-01-08 11:38 ` [PATCH 048/112] ALSA: hda - Export snd_hda_gen_add_kctl() Takashi Iwai
2013-01-08 11:38 ` [PATCH 049/112] ALSA: hda - Move the call of snd_hda_parse_pin_defcfg() from snd_hda_gen_parse_auto_config() Takashi Iwai
2013-01-08 11:38 ` [PATCH 050/112] ALSA: hda - Fix NULL dereference in snd_hda_gen_build_controls() Takashi Iwai
2013-01-08 11:38 ` [PATCH 051/112] ALSA: hda - Export standard jack event handlers for generic parser Takashi Iwai
2013-01-08 11:38 ` [PATCH 052/112] ALSA: hda - Use generic parser codes for Realtek driver Takashi Iwai
2013-01-08 11:38 ` [PATCH 053/112] ALSA: hda - Use "Capture Source" for single sources Takashi Iwai
2013-01-08 11:38 ` [PATCH 054/112] ALSA: hda - Allow one chance for zero NID in connection list Takashi Iwai
2013-01-08 11:38 ` [PATCH 055/112] ALSA: hda - Clear dirty flag upon cache write Takashi Iwai
2013-01-08 11:38 ` [PATCH 056/112] ALSA: hda - Clear cached_write flag in snd_hda_codec_resume_*() Takashi Iwai
2013-01-08 11:38 ` [PATCH 057/112] ALSA: hda - Check CORB overflow Takashi Iwai
2013-01-08 11:38 ` [PATCH 058/112] ALSA: hda - Flush dirty amp caches before writing inv_dmic fix Takashi Iwai
2013-01-08 11:38 ` [PATCH 059/112] ALSA: hda - Add snd_hda_codec_flush_*_cache() aliases Takashi Iwai
2013-01-08 11:38 ` [PATCH 060/112] ALSA: hda - Add missing amp cache flush for bound capture vol/sw ctls Takashi Iwai
2013-01-08 11:38 ` [PATCH 061/112] ALSA: hda - Add / fix comments about capture vol/sw controls in hda_generic.c Takashi Iwai
2013-01-08 11:38 ` [PATCH 062/112] ALSA: hda - Do sequential writes in snd_hda_gen_init() Takashi Iwai
2013-01-08 11:38 ` [PATCH 063/112] ALSA: hda - Fix wrong dirty check in snd_hda_codec_resume_amp() Takashi Iwai
2013-01-08 11:38 ` [PATCH 064/112] ALSA: hda - Avoid access of amp cache element outside mutex Takashi Iwai
2013-01-08 11:38 ` [PATCH 065/112] ALSA: hda - Increase the max depth of widget connections Takashi Iwai
2013-01-08 11:38 ` [PATCH 066/112] ALSA: hda - Begin HDA_GEN_* event tag from 1 Takashi Iwai
2013-01-08 11:39 ` [PATCH 067/112] ALSA: hda - Add spec->vmaster_mute_enum flag to generic parser Takashi Iwai
2013-01-08 11:39 ` [PATCH 068/112] ALSA: hda - Clear unsol enable bits on unused pins in " Takashi Iwai
2013-01-08 11:39 ` [PATCH 069/112] ALSA: hda - Refactor init_extra_out() in hda_generic.c Takashi Iwai
2013-01-08 11:39 ` [PATCH 070/112] ALSA: hda - Fix initialization of primary outputs " Takashi Iwai
2013-01-08 11:39 ` [PATCH 071/112] ALSA: hda - Dynamically turn on/off EAPD in generic codec driver Takashi Iwai
2013-01-08 11:39 ` [PATCH 072/112] ALSA: hda - Use cached version for changing pins in hda_generic.c Takashi Iwai
2013-01-08 11:39 ` [PATCH 073/112] ALSA: hda - Fix PCM name string for generic parser Takashi Iwai
2013-01-08 11:39 ` [PATCH 074/112] ALSA: hda - Drop spec->channel_mode field from hda_gen_spec Takashi Iwai
2013-01-08 11:39 ` [PATCH 075/112] ALSA: hda - Add more debug prints about new paths Takashi Iwai
2013-01-08 11:39 ` [PATCH 076/112] ALSA: hda - Fix typos in debug_show_configs() Takashi Iwai
2013-01-08 11:39 ` [PATCH 077/112] ALSA: hda - Define HDA_PARSE_* for snd_hda_parse_nid_path() argument Takashi Iwai
2013-01-08 11:39 ` [PATCH 078/112] ALSA: hda - Allow aamix in the primary output path Takashi Iwai
2013-01-08 11:39 ` [PATCH 079/112] ALSA: hda - Implement independent HP control Takashi Iwai
2013-01-08 11:39 ` [PATCH 080/112] ALSA: hda - Add inv_eapd flag to struct hda_codec Takashi Iwai
2013-01-08 11:39 ` [PATCH 081/112] ALSA: hda - Add codec->inv_jack_detect flag Takashi Iwai
2013-01-08 11:39 ` [PATCH 082/112] ALSA: hda - Revive snd_hda_get_conn_list() Takashi Iwai
2013-01-08 11:39 ` [PATCH 083/112] ALSA: hda - Add hooks for HP/line/mic auto switching Takashi Iwai
2013-01-08 11:39 ` [PATCH 084/112] ALSA: hda - Don't skip amp init for activated paths Takashi Iwai
2013-01-08 11:39 ` [PATCH 085/112] ALSA: hda - Initialize output paths with current active states Takashi Iwai
2013-01-08 11:39 ` [PATCH 086/112] ALSA: hda - Avoid duplicated path creations Takashi Iwai
2013-01-08 11:39 ` [PATCH 087/112] ALSA: hda - Check the existing path in snd_hda_add_new_path() Takashi Iwai
2013-01-08 11:39 ` [PATCH 088/112] ALSA: hda - Simplify the multi-io assignment with multi speakers Takashi Iwai
2013-01-08 11:39 ` [PATCH 089/112] ALSA: hda - Fix multi-io pin assignment in create_multi_out_ctls() Takashi Iwai
2013-01-08 11:39 ` [PATCH 090/112] ALSA: hda - Manage using output/loopback path indices Takashi Iwai
2013-01-08 11:39 ` [PATCH 091/112] ALSA: hda - Initialize digital-input path properly Takashi Iwai
2013-01-08 11:39 ` [PATCH 092/112] ALSA: hda - Correct aamix output paths Takashi Iwai
2013-01-08 11:39 ` [PATCH 093/112] ALSA: hda - Add Loopback Mixing control Takashi Iwai
2013-01-08 11:39 ` [PATCH 094/112] ALSA: hda - Fix truncated control names Takashi Iwai
2013-01-08 11:39 ` [PATCH 095/112] ALSA: hda - Prefer binding the primary CLFE output Takashi Iwai
2013-01-08 11:39 ` [PATCH 096/112] ALSA: hda - Add missing slave names for Speaker Surround, etc Takashi Iwai
2013-01-08 11:39 ` [PATCH 097/112] ALSA: hda - Drop unneeded pin argument from set_output_and_unmute() Takashi Iwai
2013-01-08 11:39 ` [PATCH 098/112] ALSA: hda - Drop bind-volume workaround Takashi Iwai
2013-01-08 11:39 ` [PATCH 099/112] ALSA: hda - Add pcm_playback_hook to hda_gen_spec Takashi Iwai
2013-01-08 11:39 ` [PATCH 100/112] ALSA: hda - Allow jack detection when polling is enabled Takashi Iwai
2013-01-08 11:39 ` [PATCH 101/112] ALSA: hda - Add snd_hda_gen_free() and snd_hda_gen_check_power_status() Takashi Iwai
2013-01-08 11:39 ` [PATCH 102/112] ALSA: hda - Remove dead HDA_CTL_BIND_VOL and HDA_CTL_BIND_SW codes Takashi Iwai
2013-01-08 11:39 ` [PATCH 103/112] ALSA: hda - Add brief comments to exported snd_hda_gen_*_() functions Takashi Iwai
2013-01-08 11:39 ` [PATCH 104/112] ALSA: hda - Clear path indices properly at each re-evaluation Takashi Iwai
2013-01-08 11:39 ` [PATCH 105/112] ALSA: hda - Use direct path reference in assign_out_path_ctls() Takashi Iwai
2013-01-08 11:39 ` [PATCH 106/112] ALSA: hda - Remove unused dac reference in create_multi_out_ctls() Takashi Iwai
2013-01-08 11:39 ` [PATCH 107/112] ALSA: hda - Don't set up active streams twice Takashi Iwai
2013-01-08 11:39 ` [PATCH 108/112] ALSA: hda - Fix multi-io channel mode management Takashi Iwai
2013-02-21  8:03   ` Raymond Yau
2013-01-08 11:39 ` [PATCH 109/112] ALSA: hda - Manage input paths via path indices Takashi Iwai
2013-01-08 11:39 ` [PATCH 110/112] ALSA: hda - Re-define snd_hda_parse_nid_path() Takashi Iwai
2013-01-08 11:39 ` [PATCH 111/112] ALSA: hda - Handle BOTH jack port as a fixed output Takashi Iwai
2013-01-08 11:39 ` [PATCH 112/112] ALSA: hda - Add a flag to suppress mic auto-switch Takashi Iwai
2013-01-08 11:47 ` [PATCH RFC 000/112] HD-audio generic parser improvements Takashi Iwai
2013-01-08 12:20 ` David Henningsson
2013-01-08 12:44   ` Takashi Iwai
2013-01-08 13:03 ` Jaroslav Kysela
2013-01-08 13:20   ` Takashi Iwai
2013-01-08 14:10     ` Jaroslav Kysela

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).