ALSA: hda - proc - introduce Control: lines to show mixer<->NID assignment

This is an initial patch to show universal control<->NID assigment in
proc codec file. The change helps to debug codec related problems.

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 7fd2abe..1ed1d88 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -946,7 +946,7 @@
 	mutex_init(&codec->control_mutex);
 	init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
 	init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
-	snd_array_init(&codec->mixers, sizeof(struct snd_kcontrol *), 32);
+	snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 60);
 	snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
 	snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
 	if (codec->bus->modelname) {
@@ -1517,18 +1517,20 @@
 EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl);
 
 /* Add a control element and assign to the codec */
-int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl)
+int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
+		    struct snd_kcontrol *kctl)
 {
 	int err;
-	struct snd_kcontrol **knewp;
+	struct hda_nid_item *item;
 
 	err = snd_ctl_add(codec->bus->card, kctl);
 	if (err < 0)
 		return err;
-	knewp = snd_array_new(&codec->mixers);
-	if (!knewp)
+	item = snd_array_new(&codec->mixers);
+	if (!item)
 		return -ENOMEM;
-	*knewp = kctl;
+	item->kctl = kctl;
+	item->nid = nid;
 	return 0;
 }
 EXPORT_SYMBOL_HDA(snd_hda_ctl_add);
@@ -1537,9 +1539,9 @@
 void snd_hda_ctls_clear(struct hda_codec *codec)
 {
 	int i;
-	struct snd_kcontrol **kctls = codec->mixers.list;
+	struct hda_nid_item *items = codec->mixers.list;
 	for (i = 0; i < codec->mixers.used; i++)
-		snd_ctl_remove(codec->bus->card, kctls[i]);
+		snd_ctl_remove(codec->bus->card, items[i].kctl);
 	snd_array_free(&codec->mixers);
 }
 
@@ -1645,7 +1647,7 @@
 	kctl = snd_ctl_make_virtual_master(name, tlv);
 	if (!kctl)
 		return -ENOMEM;
-	err = snd_hda_ctl_add(codec, kctl);
+	err = snd_hda_ctl_add(codec, 0, kctl);
 	if (err < 0)
 		return err;
 	
@@ -2139,7 +2141,7 @@
 			return -ENOMEM;
 		kctl->id.index = idx;
 		kctl->private_value = nid;
-		err = snd_hda_ctl_add(codec, kctl);
+		err = snd_hda_ctl_add(codec, nid, kctl);
 		if (err < 0)
 			return err;
 	}
@@ -2184,8 +2186,8 @@
 	if (!mout->dig_out_nid)
 		return 0;
 	/* ATTENTION: here mout is passed as private_data, instead of codec */
-	return snd_hda_ctl_add(codec,
-			   snd_ctl_new1(&spdif_share_sw, mout));
+	return snd_hda_ctl_add(codec, mout->dig_out_nid,
+			      snd_ctl_new1(&spdif_share_sw, mout));
 }
 EXPORT_SYMBOL_HDA(snd_hda_create_spdif_share_sw);
 
@@ -2289,7 +2291,7 @@
 		if (!kctl)
 			return -ENOMEM;
 		kctl->private_value = nid;
-		err = snd_hda_ctl_add(codec, kctl);
+		err = snd_hda_ctl_add(codec, nid, kctl);
 		if (err < 0)
 			return err;
 	}
@@ -3165,7 +3167,7 @@
 		kctl = snd_ctl_new1(knew, codec);
 		if (!kctl)
 			return -ENOMEM;
-		err = snd_hda_ctl_add(codec, kctl);
+		err = snd_hda_ctl_add(codec, 0, kctl);
 		if (err < 0) {
 			if (!codec->addr)
 				return err;
@@ -3173,7 +3175,7 @@
 			if (!kctl)
 				return -ENOMEM;
 			kctl->id.device = codec->addr;
-			err = snd_hda_ctl_add(codec, kctl);
+			err = snd_hda_ctl_add(codec, 0, kctl);
 			if (err < 0)
 				return err;
 		}
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index b36f6c5..092c6a7 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -727,7 +727,8 @@
 		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, snd_ctl_new1(&knew, codec));
+		err = snd_hda_ctl_add(codec, node->nid,
+					snd_ctl_new1(&knew, codec));
 		if (err < 0)
 			return err;
 		created = 1;
@@ -737,7 +738,8 @@
 		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, snd_ctl_new1(&knew, codec));
+		err = snd_hda_ctl_add(codec, node->nid,
+					snd_ctl_new1(&knew, codec));
 		if (err < 0)
 			return err;
 		created = 1;
@@ -751,7 +753,8 @@
 	    (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, snd_ctl_new1(&knew, codec));
+		err = snd_hda_ctl_add(codec, node->nid,
+					snd_ctl_new1(&knew, codec));
 		if (err < 0)
 			return err;
 		created = 1;
@@ -759,7 +762,8 @@
 		   (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, snd_ctl_new1(&knew, codec));
+		err = snd_hda_ctl_add(codec, node->nid,
+					snd_ctl_new1(&knew, codec));
 		if (err < 0)
 			return err;
 		created = 1;
@@ -857,7 +861,7 @@
 	}
 
 	/* create input MUX if multiple sources are available */
-	err = snd_hda_ctl_add(codec, snd_ctl_new1(&cap_sel, codec));
+	err = snd_hda_ctl_add(codec, 0, snd_ctl_new1(&cap_sel, codec));
 	if (err < 0)
 		return err;
 
@@ -875,7 +879,8 @@
 			HDA_CODEC_VOLUME(name, adc_node->nid,
 					 spec->input_mux.items[i].index,
 					 HDA_INPUT);
-		err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+		err = snd_hda_ctl_add(codec, adc_node->nid,
+					snd_ctl_new1(&knew, codec));
 		if (err < 0)
 			return err;
 	}
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 3001794..e6a0918 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -440,7 +440,13 @@
 			      unsigned int caps);
 u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid);
 
-int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl);
+struct hda_nid_item {
+	struct snd_kcontrol *kctl;
+	hda_nid_t nid;
+};
+
+int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
+		    struct snd_kcontrol *kctl);
 void snd_hda_ctls_clear(struct hda_codec *codec);
 
 /*
@@ -514,7 +520,8 @@
  * AMP control callbacks
  */
 /* retrieve parameters from private_value */
-#define get_amp_nid(kc)		((kc)->private_value & 0xffff)
+#define get_amp_nid_(pv)	((pv) & 0xffff)
+#define get_amp_nid(kc)		get_amp_nid_((kc)->private_value)
 #define get_amp_channels(kc)	(((kc)->private_value >> 16) & 0x3)
 #define get_amp_direction(kc)	(((kc)->private_value >> 18) & 0x1)
 #define get_amp_index(kc)	(((kc)->private_value >> 19) & 0xf)
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index f5b783c..f465cff 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -46,6 +46,41 @@
 		return "UNKNOWN Widget";
 }
 
+static void print_nid_mixers(struct snd_info_buffer *buffer,
+			     struct hda_codec *codec, hda_nid_t nid)
+{
+	int i;
+	struct hda_nid_item *items = codec->mixers.list;
+	struct snd_kcontrol *kctl;
+	for (i = 0; i < codec->mixers.used; i++) {
+		if (items[i].nid == nid) {
+			kctl = items[i].kctl;
+			snd_iprintf(buffer,
+			  "  Control: name=\"%s\", index=%i, device=%i\n",
+			  kctl->id.name, kctl->id.index, kctl->id.device);
+		}
+	}
+}
+
+static void print_nid_pcms(struct snd_info_buffer *buffer,
+			   struct hda_codec *codec, hda_nid_t nid)
+{
+	int pcm, type;
+	struct hda_pcm *cpcm;
+	for (pcm = 0; pcm < codec->num_pcms; pcm++) {
+		cpcm = &codec->pcm_info[pcm];
+		for (type = 0; type < 2; type++) {
+			if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL)
+				continue;
+			snd_iprintf(buffer, "  Device: name=\"%s\", "
+				    "type=\"%s\", device=%i\n",
+				    cpcm->name,
+				    snd_hda_pcm_type_name[cpcm->pcm_type],
+				    cpcm->pcm->device);
+		}
+	}
+}
+
 static void print_amp_caps(struct snd_info_buffer *buffer,
 			   struct hda_codec *codec, hda_nid_t nid, int dir)
 {
@@ -309,21 +344,7 @@
 			   struct hda_codec *codec, hda_nid_t nid,
 			   unsigned int wid_type)
 {
-	int pcm, conv;
-	for (pcm = 0; pcm < codec->num_pcms; pcm++) {
-		int type;
-		struct hda_pcm *cpcm = &codec->pcm_info[pcm];
-		for (type = 0; type < 2; type++) {
-			if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL)
-				continue;
-			snd_iprintf(buffer, "  Device: name=\"%s\", "
-				    "type=\"%s\", device=%i\n",
-				    cpcm->name,
-				    snd_hda_pcm_type_name[cpcm->pcm_type],
-				    cpcm->pcm->device);
-		}
-	}
-	conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
+	int conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
 	snd_iprintf(buffer,
 		    "  Converter: stream=%d, channel=%d\n",
 		    (conv & AC_CONV_STREAM) >> AC_CONV_STREAM_SHIFT,
@@ -471,6 +492,7 @@
 			    (data & (1<<i)) ? 1 : 0,
 			    (unsol & (1<<i)) ? 1 : 0);
 	/* FIXME: add GPO and GPI pin information */
+	print_nid_mixers(buffer, codec, nid);
 }
 
 static void print_codec_info(struct snd_info_entry *entry,
@@ -550,6 +572,9 @@
 			snd_iprintf(buffer, " CP");
 		snd_iprintf(buffer, "\n");
 
+		print_nid_mixers(buffer, codec, nid);
+		print_nid_pcms(buffer, codec, nid);
+
 		/* volume knob is a special widget that always have connection
 		 * list
 		 */
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index a029361..ef33839 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -202,7 +202,9 @@
 			if (!kctl)
 				return -ENOMEM;
 			kctl->private_value = spec->beep_amp;
-			err = snd_hda_ctl_add(codec, kctl);
+			err = snd_hda_ctl_add(codec,
+						get_amp_nid_(spec->beep_amp),
+						kctl);
 			if (err < 0)
 				return err;
 		}
diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c
index d08353d..af47801 100644
--- a/sound/pci/hda/patch_ca0110.c
+++ b/sound/pci/hda/patch_ca0110.c
@@ -144,7 +144,7 @@
 	struct snd_kcontrol_new knew =
 		HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type);
 	sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
-	return snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+	return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
 }
 
 static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
@@ -155,7 +155,7 @@
 	struct snd_kcontrol_new knew =
 		HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type);
 	sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]);
-	return snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+	return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
 }
 
 #define add_out_switch(codec, nid, pfx)	_add_switch(codec, nid, pfx, 3, 0)
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index 8ba3068..9ac09e4 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -500,7 +500,7 @@
 	knew.private_value = pval;
 	snprintf(tmp, sizeof(tmp), "%s %s Switch", name, dir_sfx[dir]);
 	*kctlp = snd_ctl_new1(&knew, codec);
-	return snd_hda_ctl_add(codec, *kctlp);
+	return snd_hda_ctl_add(codec, get_amp_nid_(pval), *kctlp);
 }
 
 static int add_volume(struct hda_codec *codec, const char *name,
@@ -513,7 +513,7 @@
 	knew.private_value = pval;
 	snprintf(tmp, sizeof(tmp), "%s %s Volume", name, dir_sfx[dir]);
 	*kctlp = snd_ctl_new1(&knew, codec);
-	return snd_hda_ctl_add(codec, *kctlp);
+	return snd_hda_ctl_add(codec, get_amp_nid_(pval), *kctlp);
 }
 
 static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac)
@@ -536,14 +536,14 @@
 
 	spec->vmaster_sw =
 		snd_ctl_make_virtual_master("Master Playback Switch", NULL);
-	err = snd_hda_ctl_add(codec, spec->vmaster_sw);
+	err = snd_hda_ctl_add(codec, dac, spec->vmaster_sw);
 	if (err < 0)
 		return err;
 
 	snd_hda_set_vmaster_tlv(codec, dac, HDA_OUTPUT, tlv);
 	spec->vmaster_vol =
 		snd_ctl_make_virtual_master("Master Playback Volume", tlv);
-	err = snd_hda_ctl_add(codec, spec->vmaster_vol);
+	err = snd_hda_ctl_add(codec, dac, spec->vmaster_vol);
 	if (err < 0)
 		return err;
 	return 0;
@@ -756,13 +756,13 @@
 		if (!kctl)
 			return -ENOMEM;
 		kctl->private_value = (long)spec->capture_bind[i];
-		err = snd_hda_ctl_add(codec, kctl);
+		err = snd_hda_ctl_add(codec, 0, kctl);
 		if (err < 0)
 			return err;
 	}
 	
 	if (spec->num_inputs > 1 && !spec->mic_detect) {
-		err = snd_hda_ctl_add(codec,
+		err = snd_hda_ctl_add(codec, 0,
 				      snd_ctl_new1(&cs_capture_source, codec));
 		if (err < 0)
 			return err;
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 8c04e0e..fff9de2 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -2461,7 +2461,8 @@
 			if (!kctl)
 				return -ENOMEM;
 			kctl->private_value = spec->beep_amp;
-			err = snd_hda_ctl_add(codec, kctl);
+			err = snd_hda_ctl_add(codec,
+					get_amp_nid_(spec->beep_amp), kctl);
 			if (err < 0)
 				return err;
 		}
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 87ba239..a3872b9 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -1085,7 +1085,7 @@
 	if (!spec->auto_mic && spec->num_dmuxes > 0 &&
 	    snd_hda_get_bool_hint(codec, "separate_dmux") == 1) {
 		stac_dmux_mixer.count = spec->num_dmuxes;
-		err = snd_hda_ctl_add(codec,
+		err = snd_hda_ctl_add(codec, 0,
 				  snd_ctl_new1(&stac_dmux_mixer, codec));
 		if (err < 0)
 			return err;
@@ -1101,7 +1101,7 @@
 			spec->spdif_mute = 1;
 		}
 		stac_smux_mixer.count = spec->num_smuxes;
-		err = snd_hda_ctl_add(codec,
+		err = snd_hda_ctl_add(codec, 0,
 				  snd_ctl_new1(&stac_smux_mixer, codec));
 		if (err < 0)
 			return err;