ALSA: hda - Fix power of pins used for mute LED with vrefs

Some pins are used for controlling the LED with the VREF value.
This patch changes the power behavior of such pins to be constantly
up.  A new state, pin_fixed, is introduced to nid_path to indicate
that the path contains the fixed pin.  This improves also the
readability a bit for other static routes, too.

Then a helper function snd_hda_gen_fix_pin_power() is called from the
codec driver for such fixed pins, and it will create fake paths
containing only these pins with pin_fixed=1 flag.

Reported-by: David Henningsson <david.henningsson@canonical.com>
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 d7ca388..1cafcbb 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -665,7 +665,7 @@
 			if (!path->stream_enabled)
 				continue;
 			/* ignore unplugged paths except for DAC/ADC */
-			if (!path->pin_enabled &&
+			if (!(path->pin_enabled || path->pin_fixed) &&
 			    type != AC_WID_AUD_OUT && type != AC_WID_AUD_IN)
 				continue;
 		}
@@ -1607,7 +1607,7 @@
 		return 0;
 	/* print_nid_path(codec, "output-aamix", path); */
 	path->active = false; /* unused as default */
-	path->pin_enabled = true; /* static route */
+	path->pin_fixed = true; /* static route */
 	return snd_hda_get_path_idx(codec, path);
 }
 
@@ -3044,7 +3044,7 @@
 		if (path) {
 			print_nid_path(codec, "loopback-merge", path);
 			path->active = true;
-			path->pin_enabled = true; /* static route */
+			path->pin_fixed = true; /* static route */
 			path->stream_enabled = true; /* no DAC/ADC involved */
 			spec->loopback_merge_path =
 				snd_hda_get_path_idx(codec, path);
@@ -3847,7 +3847,7 @@
 			continue;
 		print_nid_path(codec, "digout", path);
 		path->active = true;
-		path->pin_enabled = true; /* no jack detection */
+		path->pin_fixed = true; /* no jack detection */
 		spec->digout_paths[i] = snd_hda_get_path_idx(codec, path);
 		set_pin_target(codec, pin, PIN_OUT, false);
 		if (!nums) {
@@ -3875,7 +3875,7 @@
 			if (path) {
 				print_nid_path(codec, "digin", path);
 				path->active = true;
-				path->pin_enabled = true; /* no jack */
+				path->pin_fixed = true; /* no jack */
 				spec->dig_in_nid = dig_nid;
 				spec->digin_path = snd_hda_get_path_idx(codec, path);
 				set_pin_target(codec, pin, PIN_IN, false);
@@ -3959,8 +3959,8 @@
 				path->pin_enabled = pin_state;
 			if (stream_state >= 0)
 				path->stream_enabled = stream_state;
-			if (path->pin_enabled != pin_old ||
-			    path->stream_enabled != stream_old) {
+			if ((!path->pin_fixed && path->pin_enabled != pin_old)
+			    || path->stream_enabled != stream_old) {
 				last = path_power_update(codec, path, true);
 				if (last)
 					changed = last;
@@ -4136,6 +4136,29 @@
 	set_path_power(beep->codec, beep->nid, -1, on);
 }
 
+/**
+ * snd_hda_gen_fix_pin_power - Fix the power of the given pin widget to D0
+ * @codec: the HDA codec
+ * @pin: NID of pin to fix
+ */
+int snd_hda_gen_fix_pin_power(struct hda_codec *codec, hda_nid_t pin)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	struct nid_path *path;
+
+	path = snd_array_new(&spec->paths);
+	if (!path)
+		return -ENOMEM;
+	memset(path, 0, sizeof(*path));
+	path->depth = 1;
+	path->path[0] = pin;
+	path->active = true;
+	path->pin_fixed = true;
+	path->stream_enabled = true;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_fix_pin_power);
+
 /*
  * Jack detections for HP auto-mute and mic-switch
  */
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index 54659b5..56e4139 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -48,6 +48,7 @@
 	unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */
 	bool active:1;		/* activated by driver */
 	bool pin_enabled:1;	/* pins are enabled */
+	bool pin_fixed:1;	/* path with fixed pin */
 	bool stream_enabled:1;	/* stream is active */
 };
 
@@ -343,5 +344,6 @@
 					   hda_nid_t nid,
 					   unsigned int power_state);
 void snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on);
+int snd_hda_gen_fix_pin_power(struct hda_codec *codec, hda_nid_t pin);
 
 #endif /* __SOUND_HDA_GENERIC_H */
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 86b944a..7e531d5 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -4225,6 +4225,12 @@
 	if (err < 0)
 		return err;
 
+	if (spec->vref_mute_led_nid) {
+		err = snd_hda_gen_fix_pin_power(codec, spec->vref_mute_led_nid);
+		if (err < 0)
+			return err;
+	}
+
 	/* setup analog beep controls */
 	if (spec->anabeep_nid > 0) {
 		err = stac_auto_create_beep_ctls(codec,