ALSA: hda/realtek - Allow multiple individual capture volume/switch controls

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>
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 13d4548..6fb3922 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -221,6 +221,7 @@
 	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 @@
 		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 @@
 	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 @@
 		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 @@
 	}
 
 	for (n = 0; n < nums; n++) {
+		bool multi = false;
 		int vol, sw;
 
 		vol = sw = 0;
@@ -2577,26 +2694,22 @@
 			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;