ALSA: hda - Manage current pinctl values in generic parser

Use the new pin target accessors for managing the current pinctl
values in the generic parser.  The pinctl values of all active pins
are once determined at the initialization phase, and stored via
snd_hda_codec_set_pin_target().  This will be referred again in the
codec init or resume phase to set the actual pinctl.

This value is kept while the auto-mute.  When a line-out or a speaker
pin is muted by auto-mute, the driver simply disables the pin, but it
doesn't touch the cached pinctl target value.  Upon unmute, this value
is used to restore the original pinctl in return.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index a9bf188..e786f10 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -84,6 +84,41 @@
 EXPORT_SYMBOL_HDA(snd_hda_gen_spec_free);
 
 /*
+ * pin control value accesses
+ */
+
+#define update_pin_ctl(codec, pin, val) \
+	snd_hda_codec_update_cache(codec, pin, 0, \
+				   AC_VERB_SET_PIN_WIDGET_CONTROL, val)
+
+/* restore the pinctl based on the cached value */
+static inline void restore_pin_ctl(struct hda_codec *codec, hda_nid_t pin)
+{
+	update_pin_ctl(codec, pin, snd_hda_codec_get_pin_target(codec, pin));
+}
+
+/* set the pinctl target value and write it if requested */
+static void set_pin_target(struct hda_codec *codec, hda_nid_t pin,
+			   unsigned int val, bool do_write)
+{
+	if (!pin)
+		return;
+	val = snd_hda_correct_pin_ctl(codec, pin, val);
+	snd_hda_codec_set_pin_target(codec, pin, val);
+	if (do_write)
+		update_pin_ctl(codec, pin, val);
+}
+
+/* set pinctl target values for all given pins */
+static void set_pin_targets(struct hda_codec *codec, int num_pins,
+			    hda_nid_t *pins, unsigned int val)
+{
+	int i;
+	for (i = 0; i < num_pins; i++)
+		set_pin_target(codec, pins[i], val, false);
+}
+
+/*
  * parsing paths
  */
 
@@ -1317,6 +1352,15 @@
 				   spec->multiout.extra_out_nid,
 				   spec->speaker_paths);
 
+	/* set initial pinctl targets */
+	set_pin_targets(codec, cfg->line_outs, cfg->line_out_pins,
+			cfg->line_out_type == AUTO_PIN_HP_OUT ? PIN_HP : PIN_OUT);
+	if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+		set_pin_targets(codec, cfg->hp_outs, cfg->hp_pins, PIN_HP);
+	if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+		set_pin_targets(codec, cfg->speaker_outs,
+				cfg->speaker_pins, PIN_OUT);
+
 	return badness;
 }
 
@@ -1715,14 +1759,13 @@
 		return 0;
 
 	if (output) {
-		snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT);
+		set_pin_target(codec, nid, PIN_OUT, true);
 		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);
+		set_pin_target(codec, nid, spec->multi_io[idx].ctl_in, true);
 	}
 	return 0;
 }
@@ -1871,7 +1914,7 @@
 	}
 
 	val = set_as_mic ? val | PIN_IN : PIN_HP;
-	snd_hda_set_pin_ctl_cache(codec, pin, val);
+	set_pin_target(codec, pin, val, true);
 
 	spec->automute_speaker = !set_as_mic;
 	call_update_outputs(codec);
@@ -2126,6 +2169,7 @@
 	int num_adcs;
 	int i, err, type_idx = 0;
 	const char *prev_label = NULL;
+	unsigned int val;
 
 	num_adcs = fill_adc_nids(codec);
 	if (num_adcs < 0)
@@ -2146,6 +2190,11 @@
 			type_idx = 0;
 		prev_label = label;
 
+		val = PIN_IN;
+		if (cfg->inputs[i].type == AUTO_PIN_MIC)
+			val |= snd_hda_get_default_vref(codec, pin);
+		set_pin_target(codec, pin, val, false);
+
 		if (mixer) {
 			if (is_reachable_path(codec, pin, mixer)) {
 				err = new_analog_input(codec, i, pin,
@@ -2611,12 +2660,12 @@
 	struct hda_gen_spec *spec = codec->spec;
 	struct nid_path *path;
 	int i, nums;
-	hda_nid_t dig_nid;
+	hda_nid_t dig_nid, pin;
 
 	/* 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];
+		pin = spec->autocfg.dig_out_pins[i];
 		dig_nid = look_for_dac(codec, pin, true);
 		if (!dig_nid)
 			continue;
@@ -2626,6 +2675,7 @@
 		print_nid_path("digout", path);
 		path->active = true;
 		spec->digout_paths[i] = snd_hda_get_path_idx(codec, path);
+		set_pin_target(codec, pin, PIN_OUT, false);
 		if (!nums) {
 			spec->multiout.dig_out_nid = dig_nid;
 			spec->dig_out_type = spec->autocfg.dig_out_type[0];
@@ -2639,6 +2689,7 @@
 	}
 
 	if (spec->autocfg.dig_in_pin) {
+		pin = spec->autocfg.dig_in_pin;
 		dig_nid = codec->start_nid;
 		for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
 			unsigned int wcaps = get_wcaps(codec, dig_nid);
@@ -2646,14 +2697,13 @@
 				continue;
 			if (!(wcaps & AC_WCAP_DIGITAL))
 				continue;
-			path = snd_hda_add_new_path(codec,
-						    spec->autocfg.dig_in_pin,
-						    dig_nid, 0);
+			path = snd_hda_add_new_path(codec, pin, dig_nid, 0);
 			if (path) {
 				print_nid_path("digin", path);
 				path->active = true;
 				spec->dig_in_nid = dig_nid;
 				spec->digin_path = snd_hda_get_path_idx(codec, path);
+				set_pin_target(codec, pin, PIN_IN, false);
 				break;
 			}
 		}
@@ -2730,10 +2780,9 @@
 
 /* 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)
+			bool mute)
 {
 	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++) {
@@ -2744,14 +2793,18 @@
 		/* 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
+		if (spec->keep_vref_in_automute)
+			val = snd_hda_codec_get_pin_target(codec, nid) & ~PIN_HP;
+		else
 			val = 0;
-		val |= pin_bits;
-		snd_hda_set_pin_ctl_cache(codec, nid, val);
+		if (!mute)
+			val |= snd_hda_codec_get_pin_target(codec, nid);
+		/* here we call update_pin_ctl() so that the pinctl is changed
+		 * without changing the pinctl target value;
+		 * the original target value will be still referred at the
+		 * init / resume again
+		 */
+		update_pin_ctl(codec, nid, val);
 		set_pin_eapd(codec, nid, !mute);
 	}
 }
@@ -2768,7 +2821,7 @@
 	 */
 	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);
+		    spec->autocfg.hp_pins, spec->master_mute);
 
 	if (!spec->automute_speaker)
 		on = 0;
@@ -2776,7 +2829,7 @@
 		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);
+		    spec->autocfg.speaker_pins, on);
 
 	/* toggle line-out mutes if needed, too */
 	/* if LO is a copy of either HP or Speaker, don't need to handle it */
@@ -2789,7 +2842,7 @@
 		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);
+		    spec->autocfg.line_out_pins, on);
 }
 EXPORT_SYMBOL_HDA(snd_hda_gen_update_outputs);
 
@@ -3806,8 +3859,7 @@
  */
 
 /* configure the given path as a proper output */
-static void set_output_and_unmute(struct hda_codec *codec,
-				  int pin_type, int path_idx)
+static void set_output_and_unmute(struct hda_codec *codec, int path_idx)
 {
 	struct nid_path *path;
 	hda_nid_t pin;
@@ -3816,7 +3868,7 @@
 	if (!path || !path->depth)
 		return;
 	pin = path->path[path->depth - 1];
-	snd_hda_set_pin_ctl_cache(codec, pin, pin_type);
+	restore_pin_ctl(codec, pin);
 	snd_hda_activate_path(codec, path, path->active, true);
 	set_pin_eapd(codec, pin, path->active);
 }
@@ -3825,26 +3877,19 @@
 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 < spec->autocfg.line_outs; i++)
-		set_output_and_unmute(codec, pin_type, spec->out_paths[i]);
+		set_output_and_unmute(codec, spec->out_paths[i]);
 }
 
 
-static void __init_extra_out(struct hda_codec *codec, int num_outs,
-			     int *paths, int type)
+static void __init_extra_out(struct hda_codec *codec, int num_outs, int *paths)
 {
 	int i;
 
 	for (i = 0; i < num_outs; i++)
-		set_output_and_unmute(codec, type, paths[i]);
+		set_output_and_unmute(codec, paths[i]);
 }
 
 /* initialize hp and speaker paths */
@@ -3853,11 +3898,10 @@
 	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->hp_paths, PIN_HP);
+		__init_extra_out(codec, spec->autocfg.hp_outs, spec->hp_paths);
 	if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT)
 		__init_extra_out(codec, spec->autocfg.speaker_outs,
-				 spec->speaker_paths, PIN_OUT);
+				 spec->speaker_paths);
 }
 
 /* initialize multi-io paths */
@@ -3874,22 +3918,11 @@
 			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_codec_get_pin_target(codec, pin);
 		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_cache(codec, nid, val);
-}
-
 /* set up input pins and loopback paths */
 static void init_analog_input(struct hda_codec *codec)
 {
@@ -3900,7 +3933,7 @@
 	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);
+			restore_pin_ctl(codec, nid);
 
 		/* init loopback inputs */
 		if (spec->mixer_nid) {
@@ -3953,11 +3986,11 @@
 	hda_nid_t pin;
 
 	for (i = 0; i < spec->autocfg.dig_outs; i++)
-		set_output_and_unmute(codec, PIN_OUT, spec->digout_paths[i]);
+		set_output_and_unmute(codec, spec->digout_paths[i]);
 	pin = spec->autocfg.dig_in_pin;
 	if (pin) {
 		struct nid_path *path;
-		snd_hda_set_pin_ctl_cache(codec, pin, PIN_IN);
+		restore_pin_ctl(codec, pin);
 		path = snd_hda_get_path_from_idx(codec, spec->digin_path);
 		if (path)
 			snd_hda_activate_path(codec, path, path->active, false);