[ALSA] hda: Add 5.1 support for second headphone jack

Several 92hd7xxx and STAC9228 laptops have multiple headphone jacks,
the second headphone jack should be used for the 5.1 surround sound.
Add support for 'Headphone as Line Out' switch, which allows it be used
in 5.1 surround sound.

Signed-off-by: Matthew Ranostay <mranostay@embeddedalley.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index e1d07ab..b3a15d6 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -181,6 +181,7 @@
 	/* i/o switches */
 	unsigned int io_switch[2];
 	unsigned int clfe_swap;
+	unsigned int hp_switch;
 	unsigned int aloopback;
 
 	struct hda_pcm pcm_rec[2];	/* PCM information */
@@ -1274,7 +1275,7 @@
 
 static unsigned int dell_m6_pin_configs[13] = {
 	0x0321101f, 0x4f00000f, 0x4f0000f0, 0x90170110,
-	0x03a11020, 0x03011050, 0x4f0000f0, 0x4f0000f0,
+	0x03a11020, 0x0321101f, 0x4f0000f0, 0x4f0000f0,
 	0x4f0000f0, 0x90a60160, 0x4f0000f0, 0x4f0000f0,
 	0x4f0000f0,
 };
@@ -2052,6 +2053,34 @@
 				  AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
 }
 
+#define stac92xx_hp_switch_info		snd_ctl_boolean_mono_info
+
+static int stac92xx_hp_switch_get(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sigmatel_spec *spec = codec->spec;
+
+	ucontrol->value.integer.value[0] = spec->hp_switch;
+	return 0;
+}
+
+static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sigmatel_spec *spec = codec->spec;
+
+	spec->hp_switch = ucontrol->value.integer.value[0];
+
+	/* check to be sure that the ports are upto date with
+	 * switch changes
+	 */
+	codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+
+	return 1;
+}
+
 #define stac92xx_io_switch_info		snd_ctl_boolean_mono_info
 
 static int stac92xx_io_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -2123,6 +2152,15 @@
 	return 1;
 }
 
+#define STAC_CODEC_HP_SWITCH(xname) \
+	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+	  .name = xname, \
+	  .index = 0, \
+	  .info = stac92xx_hp_switch_info, \
+	  .get = stac92xx_hp_switch_get, \
+	  .put = stac92xx_hp_switch_put, \
+	}
+
 #define STAC_CODEC_IO_SWITCH(xname, xpval) \
 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
 	  .name = xname, \
@@ -2147,6 +2185,7 @@
 	STAC_CTL_WIDGET_VOL,
 	STAC_CTL_WIDGET_MUTE,
 	STAC_CTL_WIDGET_MONO_MUX,
+	STAC_CTL_WIDGET_HP_SWITCH,
 	STAC_CTL_WIDGET_IO_SWITCH,
 	STAC_CTL_WIDGET_CLFE_SWITCH
 };
@@ -2155,6 +2194,7 @@
 	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
 	HDA_CODEC_MUTE(NULL, 0, 0, 0),
 	STAC_MONO_MUX,
+	STAC_CODEC_HP_SWITCH(NULL),
 	STAC_CODEC_IO_SWITCH(NULL, 0),
 	STAC_CODEC_CLFE_SWITCH(NULL, 0),
 };
@@ -2419,6 +2459,14 @@
 		}
 	}
 
+	if (cfg->hp_outs > 1) {
+		err = stac92xx_add_control(spec,
+			STAC_CTL_WIDGET_HP_SWITCH,
+			"Headphone as Line Out Switch", 0);
+		if (err < 0)
+			return err;
+	}
+
 	if (spec->line_switch) {
 		nid = cfg->input_pins[AUTO_PIN_LINE];
 		pincap = snd_hda_param_read(codec, nid,
@@ -3163,6 +3211,7 @@
 {
 	struct sigmatel_spec *spec = codec->spec;
 	struct auto_pin_cfg *cfg = &spec->autocfg;
+	int nid = cfg->hp_pins[cfg->hp_outs - 1];
 	int i, presence;
 
 	presence = 0;
@@ -3173,11 +3222,15 @@
 	for (i = 0; i < cfg->hp_outs; i++) {
 		if (presence)
 			break;
+		if (spec->hp_switch && cfg->hp_pins[i] == nid)
+			break;
 		presence = get_hp_pin_presence(codec, cfg->hp_pins[i]);
 	}
 
 	if (presence) {
 		/* disable lineouts, enable hp */
+		if (spec->hp_switch)
+			stac92xx_reset_pinctl(codec, nid, AC_PINCTL_OUT_EN);
 		for (i = 0; i < cfg->line_outs; i++)
 			stac92xx_reset_pinctl(codec, cfg->line_out_pins[i],
 						AC_PINCTL_OUT_EN);
@@ -3190,6 +3243,8 @@
 				~spec->eapd_mask);
 	} else {
 		/* enable lineouts, disable hp */
+		if (spec->hp_switch)
+			stac92xx_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
 		for (i = 0; i < cfg->line_outs; i++)
 			stac92xx_set_pinctl(codec, cfg->line_out_pins[i],
 						AC_PINCTL_OUT_EN);
@@ -3201,6 +3256,8 @@
 				spec->gpio_dir, spec->gpio_data |
 				spec->eapd_mask);
 	}
+	if (!spec->hp_switch && cfg->hp_outs > 1 && presence)
+		stac92xx_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
 } 
 
 static void stac92xx_pin_sense(struct hda_codec *codec, int idx)
@@ -3459,6 +3516,7 @@
 
 	switch (spec->multiout.num_dacs) {
 	case 0x3: /* 6 Channel */
+		spec->multiout.hp_nid = 0x17;
 		spec->mixer = stac92hd73xx_6ch_mixer;
 		spec->init = stac92hd73xx_6ch_core_init;
 		break;