--- sound/pci/hda/patch_conexant.c	2010/02/12 10:12:04
+++ sound/pci/hda/patch_conexant.c	2010/02/12 11:48:27
@@ -80,6 +80,7 @@
 	unsigned int num_adc_nids;
 	hda_nid_t *adc_nids;
 	hda_nid_t dig_in_nid;		/* digital-in NID; optional */
+	hda_nid_t int_mic_nid;		/* internal microphone NID; optional */
 
 	unsigned int cur_adc_idx;
 	hda_nid_t cur_adc;
@@ -1987,38 +1988,65 @@
 static void cxt5066_automic(struct hda_codec *codec)
 {
 	struct conexant_spec *spec = codec->spec;
-	struct hda_verb ext_mic_present[] = {
-		/* enable external mic, port B */
-		{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, spec->ext_mic_bias},
-
-		/* switch to external mic input */
-		{0x17, AC_VERB_SET_CONNECT_SEL, 0},
-
-		/* disable internal mic, port C */
-		{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-		{}
-	};
-	static struct hda_verb ext_mic_absent[] = {
-		/* enable internal mic, port C */
-		{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-
-		/* switch to internal mic input */
-		{0x17, AC_VERB_SET_CONNECT_SEL, 1},
-
-		/* disable external mic, port B */
-		{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-		{}
-	};
 	unsigned int present;
 
-	present = snd_hda_codec_read(codec, 0x1a, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	if (present) {
-		snd_printdd("CXT5066: external microphone detected\n");
-		snd_hda_sequence_write(codec, ext_mic_present);
+	if (spec->int_mic_nid == 0x23) {
+		/* IdeaPad microphone detection.. */
+		static struct hda_verb ext_mic_present_ideapad[] = {
+			{0x14, AC_VERB_SET_CONNECT_SEL, 0},
+			{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+			{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+		};
+		static struct hda_verb ext_mic_absent_ideapad[] = {
+			{0x14, AC_VERB_SET_CONNECT_SEL, 2},
+			{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+			{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+		};
+		present = snd_hda_codec_read(codec, 0x1b, 0,
+					     AC_VERB_GET_PIN_SENSE, 0) &
+			  0x80000000;
+		if (present) {
+			snd_printdd("CXT5066: external microphone detected\n");
+			snd_hda_sequence_write(codec, ext_mic_present_ideapad);
+		} else {
+			snd_printdd("CXT5066: external microphone absent\n");
+			snd_hda_sequence_write(codec, ext_mic_absent_ideapad);
+		}
 	} else {
-		snd_printdd("CXT5066: external microphone absent\n");
-		snd_hda_sequence_write(codec, ext_mic_absent);
+		struct hda_verb ext_mic_present[] = {
+			/* enable external mic, port B */
+			{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL,
+			 spec->ext_mic_bias},
+
+			/* switch to external mic input */
+			{0x17, AC_VERB_SET_CONNECT_SEL, 0},
+
+			/* disable internal mic, port C */
+			{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+			{}
+		};
+		static struct hda_verb ext_mic_absent[] = {
+			/* enable internal mic, port C */
+			{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+
+			/* switch to internal mic input */
+			{0x17, AC_VERB_SET_CONNECT_SEL, 1},
+
+			/* disable external mic, port B */
+			{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+			{}
+		};
+
+		present = snd_hda_codec_read(codec, 0x1a, 0,
+					     AC_VERB_GET_PIN_SENSE, 0) &
+			  0x80000000;
+		if (present) {
+			snd_printdd("CXT5066: external microphone detected\n");
+			snd_hda_sequence_write(codec, ext_mic_present);
+		} else {
+			snd_printdd("CXT5066: external microphone absent\n");
+			snd_hda_sequence_write(codec, ext_mic_absent);
+		}
 	}
 }
 
@@ -2090,6 +2118,7 @@
 					  struct snd_ctl_elem_value *ucontrol)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct conexant_spec *spec = codec->spec;
 	const struct hda_input_mux *imux = &cxt5066_analog_mic_boost;
 	unsigned int idx;
 
@@ -2103,6 +2132,14 @@
 		AC_VERB_SET_AMP_GAIN_MUTE,
 		AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT |
 			imux->items[idx].index);
+	if (spec->int_mic_nid) {
+		/* adjust the internal microphone's gain simultaneously (for
+		 * IdeaPad) */
+		snd_hda_codec_write_cache(codec, spec->int_mic_nid, 0,
+			AC_VERB_SET_AMP_GAIN_MUTE,
+			AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_INPUT |
+				imux->items[idx].index);
+	}
 
 	return 1;
 }
@@ -2302,6 +2339,64 @@
 	{ } /* end */
 };
 
+static struct hda_verb cxt5066_init_verbs_ideapad[] = {
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port B */
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port C */
+	{0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */
+	{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port E */
+
+	/* Speakers  */
+	{0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+	/* HP, Amp  */
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x1c, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+	/* DAC1 */
+	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+	/* Node 14 connections: 0x17 0x18 0x23 0x24 0x27 */
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2) | 0x50},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+	{0x14, AC_VERB_SET_CONNECT_SEL, 2},	/* default to int mic */
+
+	/* Audio input selector */
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x2},
+	{0x17, AC_VERB_SET_CONNECT_SEL, 1},	/* route ext mic */
+
+	/* SPDIF route: PCM */
+	{0x20, AC_VERB_SET_CONNECT_SEL, 0x0},
+	{0x22, AC_VERB_SET_CONNECT_SEL, 0x0},
+
+	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+
+	/* internal microphone */
+	{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* enable int mic */
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)|0x02}, /* 20dB */
+
+	/* EAPD */
+	{0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+
+	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
+	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
+	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+	{0x20, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+	{0x22, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+
+	{ } /* end */
+};
+
 /* initialize jack-sensing, too */
 static int cxt5066_init(struct hda_codec *codec)
 {
@@ -2318,6 +2413,7 @@
 	CXT5066_LAPTOP,			/* Laptops w/ EAPD support */
 	CXT5066_DELL_LAPTOP,	/* Dell Laptop */
 	CXT5066_OLPC_XO_1_5,	/* OLPC XO 1.5 */
+	CXT5066_IDEAPAD,	/* Lenovo IdeaPad U150 */
 	CXT5066_MODELS
 };
 
@@ -2325,6 +2421,7 @@
 	[CXT5066_LAPTOP]		= "laptop",
 	[CXT5066_DELL_LAPTOP]	= "dell-laptop",
 	[CXT5066_OLPC_XO_1_5]	= "olpc-xo-1_5",
+	[CXT5066_IDEAPAD]	= "ideapad",
 };
 
 static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
@@ -2333,6 +2430,7 @@
 	SND_PCI_QUIRK(0x1028, 0x02f5, "Dell",
 		      CXT5066_DELL_LAPTOP),
 	SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5),
+	SND_PCI_QUIRK(0x17aa, 0x3a0d, "ideapad", CXT5066_IDEAPAD),
 	{}
 };
 
@@ -2399,6 +2497,14 @@
 
 		/* input source automatically selected */
 		spec->input_mux = NULL;
+		break;
+	case CXT5066_IDEAPAD:
+		codec->patch_ops.unsol_event = cxt5066_unsol_event;
+		spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
+		spec->mixers[spec->num_mixers++] = cxt5066_mixers;
+		spec->init_verbs[spec->num_init_verbs++] = cxt5066_init_verbs_ideapad;
+		spec->int_mic_nid = 0x23;
+		spec->input_mux = NULL;		/* automatic selection */
 		break;
 	}
 
