Browse Source

Merge branch 'topic/hda' into for-next

The updates of HD-audio drivers for 3.15
Takashi Iwai 11 years ago
parent
commit
63f4b3a475
3 changed files with 395 additions and 434 deletions
  1. 1 1
      sound/pci/hda/hda_generic.c
  2. 9 14
      sound/pci/hda/hda_intel.c
  3. 385 419
      sound/pci/hda/patch_conexant.c

+ 1 - 1
sound/pci/hda/hda_generic.c

@@ -762,7 +762,7 @@ void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path,
 						    AC_PWRST_D0);
 		}
 		if (enable && path->multi[i])
-			snd_hda_codec_write_cache(codec, nid, 0,
+			snd_hda_codec_update_cache(codec, nid, 0,
 					    AC_VERB_SET_CONNECT_SEL,
 					    path->idx[i]);
 		if (has_amp_in(codec, path, i))

+ 9 - 14
sound/pci/hda/hda_intel.c

@@ -834,18 +834,6 @@ static unsigned int azx_command_addr(u32 cmd)
 	return addr;
 }
 
-static unsigned int azx_response_addr(u32 res)
-{
-	unsigned int addr = res & 0xf;
-
-	if (addr >= AZX_MAX_CODECS) {
-		snd_BUG();
-		addr = 0;
-	}
-
-	return addr;
-}
-
 /* send a command */
 static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
 {
@@ -907,8 +895,15 @@ static void azx_update_rirb(struct azx *chip)
 		rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */
 		res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]);
 		res = le32_to_cpu(chip->rirb.buf[rp]);
-		addr = azx_response_addr(res_ex);
-		if (res_ex & ICH6_RIRB_EX_UNSOL_EV)
+		addr = res_ex & 0xf;
+		if ((addr >= AZX_MAX_CODECS) || !(chip->codec_mask & (1 << addr))) {
+			snd_printk(KERN_ERR SFX "%s: spurious response %#x:%#x, rp = %d, wp = %d",
+				   pci_name(chip->pci),
+				   res, res_ex,
+				   chip->rirb.rp, wp);
+			snd_BUG();
+		}
+		else if (res_ex & ICH6_RIRB_EX_UNSOL_EV)
 			snd_hda_queue_unsol_event(chip->bus, res, res_ex);
 		else if (chip->rirb.cmds[addr]) {
 			chip->rirb.res[addr] = res;

+ 385 - 419
sound/pci/hda/patch_conexant.c

@@ -35,7 +35,7 @@
 #include "hda_jack.h"
 #include "hda_generic.h"
 
-#define ENABLE_CXT_STATIC_QUIRKS
+#undef ENABLE_CXT_STATIC_QUIRKS
 
 #define CXT_PIN_DIR_IN              0x00
 #define CXT_PIN_DIR_OUT             0x01
@@ -68,6 +68,12 @@ struct conexant_spec {
 
 	unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
 
+	/* OPLC XO specific */
+	bool recording;
+	bool dc_enable;
+	unsigned int dc_input_bias; /* offset into olpc_xo_dc_bias */
+	struct nid_path *dc_mode_path;
+
 #ifdef ENABLE_CXT_STATIC_QUIRKS
 	const struct snd_kcontrol_new *mixers[5];
 	int num_mixers;
@@ -123,19 +129,6 @@ struct conexant_spec {
 	unsigned int hp_laptop:1;
 	unsigned int asus:1;
 
-	unsigned int ext_mic_present;
-	unsigned int recording;
-	void (*capture_prepare)(struct hda_codec *codec);
-	void (*capture_cleanup)(struct hda_codec *codec);
-
-	/* OLPC XO-1.5 supports DC input mode (e.g. for use with analog sensors)
-	 * through the microphone jack.
-	 * When the user enables this through a mixer switch, both internal and
-	 * external microphones are disabled. Gain is fixed at 0dB. In this mode,
-	 * we also allow the bias to be configured through a separate mixer
-	 * control. */
-	unsigned int dc_enable;
-	unsigned int dc_input_bias; /* offset into cxt5066_olpc_dc_bias */
 	unsigned int mic_boost; /* offset into cxt5066_analog_mic_boost */
 #endif /* ENABLE_CXT_STATIC_QUIRKS */
 };
@@ -253,8 +246,6 @@ static int conexant_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
 				      struct snd_pcm_substream *substream)
 {
 	struct conexant_spec *spec = codec->spec;
-	if (spec->capture_prepare)
-		spec->capture_prepare(codec);
 	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
 				   stream_tag, 0, format);
 	return 0;
@@ -266,8 +257,6 @@ static int conexant_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
 {
 	struct conexant_spec *spec = codec->spec;
 	snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
-	if (spec->capture_cleanup)
-		spec->capture_cleanup(codec);
 	return 0;
 }
 
@@ -673,14 +662,6 @@ static const struct hda_input_mux cxt5045_capture_source_benq = {
 	}
 };
 
-static const struct hda_input_mux cxt5045_capture_source_hp530 = {
-	.num_items = 2,
-	.items = {
-		{ "Mic",          0x1 },
-		{ "Internal Mic", 0x2 },
-	}
-};
-
 /* turn on/off EAPD (+ mute HP) as a master switch */
 static int cxt5045_hp_master_sw_put(struct snd_kcontrol *kcontrol,
 				    struct snd_ctl_elem_value *ucontrol)
@@ -796,28 +777,6 @@ static const struct snd_kcontrol_new cxt5045_benq_mixers[] = {
 	{}
 };
 
-static const struct snd_kcontrol_new cxt5045_mixers_hp530[] = {
-	HDA_CODEC_VOLUME("Capture Volume", 0x1a, 0x00, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x1a, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME("PCM Playback Volume", 0x17, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("PCM Playback Switch", 0x17, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x2, HDA_INPUT),
-	HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0x2, HDA_INPUT),
-	HDA_CODEC_VOLUME("Mic Playback Volume", 0x17, 0x1, HDA_INPUT),
-	HDA_CODEC_MUTE("Mic Playback Switch", 0x17, 0x1, HDA_INPUT),
-	HDA_BIND_VOL("Master Playback Volume", &cxt5045_hp_bind_master_vol),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Master Playback Switch",
-		.info = cxt_eapd_info,
-		.get = cxt_eapd_get,
-		.put = cxt5045_hp_master_sw_put,
-		.private_value = 0x10,
-	},
-
-	{}
-};
-
 static const struct hda_verb cxt5045_init_verbs[] = {
 	/* Line in, Mic */
 	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
@@ -1000,7 +959,6 @@ enum {
 	CXT5045_LAPTOP_MICSENSE,
 	CXT5045_LAPTOP_HPMICSENSE,
 	CXT5045_BENQ,
-	CXT5045_LAPTOP_HP530,
 #ifdef CONFIG_SND_DEBUG
 	CXT5045_TEST,
 #endif
@@ -1013,7 +971,6 @@ static const char * const cxt5045_models[CXT5045_MODELS] = {
 	[CXT5045_LAPTOP_MICSENSE]	= "laptop-micsense",
 	[CXT5045_LAPTOP_HPMICSENSE]	= "laptop-hpmicsense",
 	[CXT5045_BENQ]			= "benq",
-	[CXT5045_LAPTOP_HP530]		= "laptop-hp530",
 #ifdef CONFIG_SND_DEBUG
 	[CXT5045_TEST]		= "test",
 #endif
@@ -1021,8 +978,6 @@ static const char * const cxt5045_models[CXT5045_MODELS] = {
 };
 
 static const struct snd_pci_quirk cxt5045_cfg_tbl[] = {
-	SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HP530),
-	SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT5045_LAPTOP_MICSENSE),
 	SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ),
 	SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE),
 	SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP_HPMICSENSE),
@@ -1113,14 +1068,6 @@ static int patch_cxt5045(struct hda_codec *codec)
 		spec->num_mixers = 2;
 		codec->patch_ops.init = cxt5045_init;
 		break;
-	case CXT5045_LAPTOP_HP530:
-		codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
-		spec->input_mux = &cxt5045_capture_source_hp530;
-		spec->num_init_verbs = 2;
-		spec->init_verbs[1] = cxt5045_hp_sense_init_verbs;
-		spec->mixers[0] = cxt5045_mixers_hp530;
-		codec->patch_ops.init = cxt5045_init;
-		break;
 #ifdef CONFIG_SND_DEBUG
 	case CXT5045_TEST:
 		spec->input_mux = &cxt5045_test_capture_source;
@@ -1940,11 +1887,6 @@ static const hda_nid_t cxt5066_adc_nids[3] = { 0x14, 0x15, 0x16 };
 static const hda_nid_t cxt5066_capsrc_nids[1] = { 0x17 };
 static const hda_nid_t cxt5066_digout_pin_nids[2] = { 0x20, 0x22 };
 
-/* OLPC's microphone port is DC coupled for use with external sensors,
- * therefore we use a 50% mic bias in order to center the input signal with
- * the DC input range of the codec. */
-#define CXT5066_OLPC_EXT_MIC_BIAS PIN_VREF50
-
 static const struct hda_channel_mode cxt5066_modes[1] = {
 	{ 2, NULL },
 };
@@ -1997,88 +1939,6 @@ static int cxt5066_hp_master_sw_put(struct snd_kcontrol *kcontrol,
 	return 1;
 }
 
-static const struct hda_input_mux cxt5066_olpc_dc_bias = {
-	.num_items = 3,
-	.items = {
-		{ "Off", PIN_IN },
-		{ "50%", PIN_VREF50 },
-		{ "80%", PIN_VREF80 },
-	},
-};
-
-static int cxt5066_set_olpc_dc_bias(struct hda_codec *codec)
-{
-	struct conexant_spec *spec = codec->spec;
-	/* Even though port F is the DC input, the bias is controlled on port B.
-	 * we also leave that port as an active input (but unselected) in DC mode
-	 * just in case that is necessary to make the bias setting take effect. */
-	return snd_hda_set_pin_ctl_cache(codec, 0x1a,
-		cxt5066_olpc_dc_bias.items[spec->dc_input_bias].index);
-}
-
-/* OLPC defers mic widget control until when capture is started because the
- * microphone LED comes on as soon as these settings are put in place. if we
- * did this before recording, it would give the false indication that recording
- * is happening when it is not. */
-static void cxt5066_olpc_select_mic(struct hda_codec *codec)
-{
-	struct conexant_spec *spec = codec->spec;
-	if (!spec->recording)
-		return;
-
-	if (spec->dc_enable) {
-		/* in DC mode we ignore presence detection and just use the jack
-		 * through our special DC port */
-		const struct hda_verb enable_dc_mode[] = {
-			/* disble internal mic, port C */
-			{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
-			/* enable DC capture, port F */
-			{0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-			{},
-		};
-
-		snd_hda_sequence_write(codec, enable_dc_mode);
-		/* port B input disabled (and bias set) through the following call */
-		cxt5066_set_olpc_dc_bias(codec);
-		return;
-	}
-
-	/* disable DC (port F) */
-	snd_hda_set_pin_ctl(codec, 0x1e, 0);
-
-	/* external mic, port B */
-	snd_hda_set_pin_ctl(codec, 0x1a,
-		spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0);
-
-	/* internal mic, port C */
-	snd_hda_set_pin_ctl(codec, 0x1b,
-		spec->ext_mic_present ? 0 : PIN_VREF80);
-}
-
-/* toggle input of built-in and mic jack appropriately */
-static void cxt5066_olpc_automic(struct hda_codec *codec)
-{
-	struct conexant_spec *spec = codec->spec;
-	unsigned int present;
-
-	if (spec->dc_enable) /* don't do presence detection in DC mode */
-		return;
-
-	present = snd_hda_codec_read(codec, 0x1a, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	if (present)
-		snd_printdd("CXT5066: external microphone detected\n");
-	else
-		snd_printdd("CXT5066: external microphone absent\n");
-
-	snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_CONNECT_SEL,
-		present ? 0 : 1);
-	spec->ext_mic_present = !!present;
-
-	cxt5066_olpc_select_mic(codec);
-}
-
 /* toggle input of built-in digital mic and mic jack appropriately */
 static void cxt5066_vostro_automic(struct hda_codec *codec)
 {
@@ -2251,23 +2111,6 @@ static void cxt5066_automic(struct hda_codec *codec)
 		cxt5066_asus_automic(codec);
 }
 
-/* unsolicited event for jack sensing */
-static void cxt5066_olpc_unsol_event(struct hda_codec *codec, unsigned int res)
-{
-	struct conexant_spec *spec = codec->spec;
-	snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26);
-	switch (res >> 26) {
-	case CONEXANT_HP_EVENT:
-		cxt5066_hp_automute(codec);
-		break;
-	case CONEXANT_MIC_EVENT:
-		/* ignore mic events in DC mode; we're always using the jack */
-		if (!spec->dc_enable)
-			cxt5066_olpc_automic(codec);
-		break;
-	}
-}
-
 /* unsolicited event for jack sensing */
 static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res)
 {
@@ -2338,124 +2181,10 @@ static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol,
 		idx = imux->num_items - 1;
 
 	spec->mic_boost = idx;
-	if (!spec->dc_enable)
-		cxt5066_set_mic_boost(codec);
-	return 1;
-}
-
-static void cxt5066_enable_dc(struct hda_codec *codec)
-{
-	const struct hda_verb enable_dc_mode[] = {
-		/* disable gain */
-		{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
-		/* switch to DC input */
-		{0x17, AC_VERB_SET_CONNECT_SEL, 3},
-		{}
-	};
-
-	/* configure as input source */
-	snd_hda_sequence_write(codec, enable_dc_mode);
-	cxt5066_olpc_select_mic(codec); /* also sets configured bias */
-}
-
-static void cxt5066_disable_dc(struct hda_codec *codec)
-{
-	/* reconfigure input source */
 	cxt5066_set_mic_boost(codec);
-	/* automic also selects the right mic if we're recording */
-	cxt5066_olpc_automic(codec);
-}
-
-static int cxt5066_olpc_dc_get(struct snd_kcontrol *kcontrol,
-			     struct snd_ctl_elem_value *ucontrol)
-{
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct conexant_spec *spec = codec->spec;
-	ucontrol->value.integer.value[0] = spec->dc_enable;
-	return 0;
-}
-
-static int cxt5066_olpc_dc_put(struct snd_kcontrol *kcontrol,
-			     struct snd_ctl_elem_value *ucontrol)
-{
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct conexant_spec *spec = codec->spec;
-	int dc_enable = !!ucontrol->value.integer.value[0];
-
-	if (dc_enable == spec->dc_enable)
-		return 0;
-
-	spec->dc_enable = dc_enable;
-	if (dc_enable)
-		cxt5066_enable_dc(codec);
-	else
-		cxt5066_disable_dc(codec);
-
-	return 1;
-}
-
-static int cxt5066_olpc_dc_bias_enum_info(struct snd_kcontrol *kcontrol,
-					   struct snd_ctl_elem_info *uinfo)
-{
-	return snd_hda_input_mux_info(&cxt5066_olpc_dc_bias, uinfo);
-}
-
-static int cxt5066_olpc_dc_bias_enum_get(struct snd_kcontrol *kcontrol,
-					  struct snd_ctl_elem_value *ucontrol)
-{
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct conexant_spec *spec = codec->spec;
-	ucontrol->value.enumerated.item[0] = spec->dc_input_bias;
-	return 0;
-}
-
-static int cxt5066_olpc_dc_bias_enum_put(struct snd_kcontrol *kcontrol,
-					  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;
-
-	idx = ucontrol->value.enumerated.item[0];
-	if (idx >= imux->num_items)
-		idx = imux->num_items - 1;
-
-	spec->dc_input_bias = idx;
-	if (spec->dc_enable)
-		cxt5066_set_olpc_dc_bias(codec);
 	return 1;
 }
 
-static void cxt5066_olpc_capture_prepare(struct hda_codec *codec)
-{
-	struct conexant_spec *spec = codec->spec;
-	/* mark as recording and configure the microphone widget so that the
-	 * recording LED comes on. */
-	spec->recording = 1;
-	cxt5066_olpc_select_mic(codec);
-}
-
-static void cxt5066_olpc_capture_cleanup(struct hda_codec *codec)
-{
-	struct conexant_spec *spec = codec->spec;
-	const struct hda_verb disable_mics[] = {
-		/* disable external mic, port B */
-		{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
-		/* disble internal mic, port C */
-		{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
-		/* disable DC capture, port F */
-		{0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-		{},
-	};
-
-	snd_hda_sequence_write(codec, disable_mics);
-	spec->recording = 0;
-}
-
 static void conexant_check_dig_outs(struct hda_codec *codec,
 				    const hda_nid_t *dig_pins,
 				    int num_pins)
@@ -2506,43 +2235,6 @@ static const struct snd_kcontrol_new cxt5066_mixer_master[] = {
 	{}
 };
 
-static const struct snd_kcontrol_new cxt5066_mixer_master_olpc[] = {
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Master Playback Volume",
-		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
-				  SNDRV_CTL_ELEM_ACCESS_TLV_READ |
-				  SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
-		.subdevice = HDA_SUBDEV_AMP_FLAG,
-		.info = snd_hda_mixer_amp_volume_info,
-		.get = snd_hda_mixer_amp_volume_get,
-		.put = snd_hda_mixer_amp_volume_put,
-		.tlv = { .c = snd_hda_mixer_amp_tlv },
-		/* offset by 28 volume steps to limit minimum gain to -46dB */
-		.private_value =
-			HDA_COMPOSE_AMP_VAL_OFS(0x10, 3, 0, HDA_OUTPUT, 28),
-	},
-	{}
-};
-
-static const struct snd_kcontrol_new cxt5066_mixer_olpc_dc[] = {
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "DC Mode Enable Switch",
-		.info = snd_ctl_boolean_mono_info,
-		.get = cxt5066_olpc_dc_get,
-		.put = cxt5066_olpc_dc_put,
-	},
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "DC Input Bias Enum",
-		.info = cxt5066_olpc_dc_bias_enum_info,
-		.get = cxt5066_olpc_dc_bias_enum_get,
-		.put = cxt5066_olpc_dc_bias_enum_put,
-	},
-	{}
-};
-
 static const struct snd_kcontrol_new cxt5066_mixers[] = {
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -2633,67 +2325,6 @@ static const struct hda_verb cxt5066_init_verbs[] = {
 	{ } /* end */
 };
 
-static const struct hda_verb cxt5066_init_verbs_olpc[] = {
-	/* Port A: headphones */
-	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-	{0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
-	/* Port B: external microphone */
-	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
-	/* Port C: internal microphone */
-	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
-	/* Port D: unused */
-	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
-	/* Port E: unused, but has primary EAPD */
-	{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-	{0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
-
-	/* Port F: external DC input through microphone port */
-	{0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
-	/* Port G: internal speakers */
-	{0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-	{0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
-	/* DAC1 */
-	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
-	/* DAC2: unused */
-	{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
-	{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_MUTE(2)},
-	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-
-	/* Disable digital microphone port */
-	{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
-	/* Audio input selectors */
-	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3},
-	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-
-	/* Disable SPDIF */
-	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-	{0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
-	/* enable unsolicited events for Port A and B */
-	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
-	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
-	{ } /* end */
-};
-
 static const struct hda_verb cxt5066_init_verbs_vostro[] = {
 	/* Port A: headphones */
 	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
@@ -2889,25 +2520,9 @@ static int cxt5066_init(struct hda_codec *codec)
 	return 0;
 }
 
-static int cxt5066_olpc_init(struct hda_codec *codec)
-{
-	struct conexant_spec *spec = codec->spec;
-	snd_printdd("CXT5066: init\n");
-	conexant_init(codec);
-	cxt5066_hp_automute(codec);
-	if (!spec->dc_enable) {
-		cxt5066_set_mic_boost(codec);
-		cxt5066_olpc_automic(codec);
-	} else {
-		cxt5066_enable_dc(codec);
-	}
-	return 0;
-}
-
 enum {
 	CXT5066_LAPTOP,		/* Laptops w/ EAPD support */
 	CXT5066_DELL_LAPTOP,	/* Dell Laptop */
-	CXT5066_OLPC_XO_1_5,	/* OLPC XO 1.5 */
 	CXT5066_DELL_VOSTRO,	/* Dell Vostro 1015i */
 	CXT5066_IDEAPAD,	/* Lenovo IdeaPad U150 */
 	CXT5066_THINKPAD,	/* Lenovo ThinkPad T410s, others? */
@@ -2920,7 +2535,6 @@ enum {
 static const char * const cxt5066_models[CXT5066_MODELS] = {
 	[CXT5066_LAPTOP]	= "laptop",
 	[CXT5066_DELL_LAPTOP]	= "dell-laptop",
-	[CXT5066_OLPC_XO_1_5]	= "olpc-xo-1_5",
 	[CXT5066_DELL_VOSTRO]	= "dell-vostro",
 	[CXT5066_IDEAPAD]	= "ideapad",
 	[CXT5066_THINKPAD]	= "thinkpad",
@@ -2941,10 +2555,8 @@ static const struct snd_pci_quirk cxt5066_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x1043, 0x1643, "Asus K52JU", CXT5066_ASUS),
 	SND_PCI_QUIRK(0x1043, 0x1993, "Asus U50F", CXT5066_ASUS),
 	SND_PCI_QUIRK(0x1179, 0xff1e, "Toshiba Satellite C650D", CXT5066_IDEAPAD),
-	SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba Satellite P500-PSPGSC-01800T", CXT5066_OLPC_XO_1_5),
 	SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
 		      CXT5066_LAPTOP),
-	SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5),
 	SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400s", CXT5066_THINKPAD),
 	SND_PCI_QUIRK(0x17aa, 0x21c5, "Thinkpad Edge 13", CXT5066_THINKPAD),
 	SND_PCI_QUIRK(0x17aa, 0x21c6, "Thinkpad Edge 13", CXT5066_ASUS),
@@ -3030,32 +2642,11 @@ static int patch_cxt5066(struct hda_codec *codec)
 		spec->mic_boost = 3; /* default 30dB gain */
 		break;
 
-	case CXT5066_OLPC_XO_1_5:
-		codec->patch_ops.init = cxt5066_olpc_init;
-		codec->patch_ops.unsol_event = cxt5066_olpc_unsol_event;
-		spec->init_verbs[0] = cxt5066_init_verbs_olpc;
-		spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
-		spec->mixers[spec->num_mixers++] = cxt5066_mixer_olpc_dc;
-		spec->mixers[spec->num_mixers++] = cxt5066_mixers;
-		spec->port_d_mode = 0;
-		spec->mic_boost = 3; /* default 30dB gain */
-
-		/* no S/PDIF out */
-		spec->multiout.dig_out_nid = 0;
-
-		/* input source automatically selected */
-		spec->input_mux = NULL;
-
-		/* our capture hooks which allow us to turn on the microphone LED
-		 * at the right time */
-		spec->capture_prepare = cxt5066_olpc_capture_prepare;
-		spec->capture_cleanup = cxt5066_olpc_capture_cleanup;
-		break;
 	case CXT5066_DELL_VOSTRO:
 		codec->patch_ops.init = cxt5066_init;
 		codec->patch_ops.unsol_event = cxt5066_unsol_event;
 		spec->init_verbs[0] = cxt5066_init_verbs_vostro;
-		spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
+		spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
 		spec->mixers[spec->num_mixers++] = cxt5066_mixers;
 		spec->mixers[spec->num_mixers++] = cxt5066_vostro_mixers;
 		spec->port_d_mode = 0;
@@ -3238,6 +2829,11 @@ enum {
 	CXT_FIXUP_HEADPHONE_MIC,
 	CXT_FIXUP_GPIO1,
 	CXT_FIXUP_THINKPAD_ACPI,
+	CXT_FIXUP_OLPC_XO,
+	CXT_FIXUP_CAP_MIX_AMP,
+	CXT_FIXUP_TOSHIBA_P105,
+	CXT_FIXUP_HP_530,
+	CXT_FIXUP_CAP_MIX_AMP_5047,
 };
 
 /* for hda_fixup_thinkpad_acpi() */
@@ -3315,6 +2911,288 @@ static void cxt_fixup_headphone_mic(struct hda_codec *codec,
 	}
 }
 
+/* OPLC XO 1.5 fixup */
+
+/* OLPC XO-1.5 supports DC input mode (e.g. for use with analog sensors)
+ * through the microphone jack.
+ * When the user enables this through a mixer switch, both internal and
+ * external microphones are disabled. Gain is fixed at 0dB. In this mode,
+ * we also allow the bias to be configured through a separate mixer
+ * control. */
+
+#define update_mic_pin(codec, nid, val)					\
+	snd_hda_codec_update_cache(codec, nid, 0,			\
+				   AC_VERB_SET_PIN_WIDGET_CONTROL, val)
+
+static const struct hda_input_mux olpc_xo_dc_bias = {
+	.num_items = 3,
+	.items = {
+		{ "Off", PIN_IN },
+		{ "50%", PIN_VREF50 },
+		{ "80%", PIN_VREF80 },
+	},
+};
+
+static void olpc_xo_update_mic_boost(struct hda_codec *codec)
+{
+	struct conexant_spec *spec = codec->spec;
+	int ch, val;
+
+	for (ch = 0; ch < 2; ch++) {
+		val = AC_AMP_SET_OUTPUT |
+			(ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT);
+		if (!spec->dc_enable)
+			val |= snd_hda_codec_amp_read(codec, 0x17, ch, HDA_OUTPUT, 0);
+		snd_hda_codec_write(codec, 0x17, 0,
+				    AC_VERB_SET_AMP_GAIN_MUTE, val);
+	}
+}
+
+static void olpc_xo_update_mic_pins(struct hda_codec *codec)
+{
+	struct conexant_spec *spec = codec->spec;
+	int cur_input, val;
+	struct nid_path *path;
+
+	cur_input = spec->gen.input_paths[0][spec->gen.cur_mux[0]];
+
+	/* Set up mic pins for port-B, C and F dynamically as the recording
+	 * LED is turned on/off by these pin controls
+	 */
+	if (!spec->dc_enable) {
+		/* disable DC bias path and pin for port F */
+		update_mic_pin(codec, 0x1e, 0);
+		snd_hda_activate_path(codec, spec->dc_mode_path, false, false);
+
+		/* update port B (ext mic) and C (int mic) */
+		/* OLPC defers mic widget control until when capture is
+		 * started because the microphone LED comes on as soon as
+		 * these settings are put in place. if we did this before
+		 * recording, it would give the false indication that
+		 * recording is happening when it is not.
+		 */
+		update_mic_pin(codec, 0x1a, spec->recording ?
+			       snd_hda_codec_get_pin_target(codec, 0x1a) : 0);
+		update_mic_pin(codec, 0x1b, spec->recording ?
+			       snd_hda_codec_get_pin_target(codec, 0x1b) : 0);
+		/* enable normal mic path */
+		path = snd_hda_get_path_from_idx(codec, cur_input);
+		if (path)
+			snd_hda_activate_path(codec, path, true, false);
+	} else {
+		/* disable normal mic path */
+		path = snd_hda_get_path_from_idx(codec, cur_input);
+		if (path)
+			snd_hda_activate_path(codec, path, false, false);
+
+		/* Even though port F is the DC input, the bias is controlled
+		 * on port B.  We also leave that port as an active input (but
+		 * unselected) in DC mode just in case that is necessary to
+		 * make the bias setting take effect.
+		 */
+		if (spec->recording)
+			val = olpc_xo_dc_bias.items[spec->dc_input_bias].index;
+		else
+			val = 0;
+		update_mic_pin(codec, 0x1a, val);
+		update_mic_pin(codec, 0x1b, 0);
+		/* enable DC bias path and pin */
+		update_mic_pin(codec, 0x1e, spec->recording ? PIN_IN : 0);
+		snd_hda_activate_path(codec, spec->dc_mode_path, true, false);
+	}
+}
+
+/* mic_autoswitch hook */
+static void olpc_xo_automic(struct hda_codec *codec, struct hda_jack_tbl *jack)
+{
+	struct conexant_spec *spec = codec->spec;
+	int saved_cached_write = codec->cached_write;
+
+	codec->cached_write = 1;
+	/* in DC mode, we don't handle automic */
+	if (!spec->dc_enable)
+		snd_hda_gen_mic_autoswitch(codec, jack);
+	olpc_xo_update_mic_pins(codec);
+	snd_hda_codec_flush_cache(codec);
+	codec->cached_write = saved_cached_write;
+	if (spec->dc_enable)
+		olpc_xo_update_mic_boost(codec);
+}
+
+/* pcm_capture hook */
+static void olpc_xo_capture_hook(struct hda_pcm_stream *hinfo,
+				 struct hda_codec *codec,
+				 struct snd_pcm_substream *substream,
+				 int action)
+{
+	struct conexant_spec *spec = codec->spec;
+
+	/* toggle spec->recording flag and update mic pins accordingly
+	 * for turning on/off LED
+	 */
+	switch (action) {
+	case HDA_GEN_PCM_ACT_PREPARE:
+		spec->recording = 1;
+		olpc_xo_update_mic_pins(codec);
+		break;
+	case HDA_GEN_PCM_ACT_CLEANUP:
+		spec->recording = 0;
+		olpc_xo_update_mic_pins(codec);
+		break;
+	}
+}
+
+static int olpc_xo_dc_mode_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct conexant_spec *spec = codec->spec;
+	ucontrol->value.integer.value[0] = spec->dc_enable;
+	return 0;
+}
+
+static int olpc_xo_dc_mode_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct conexant_spec *spec = codec->spec;
+	int dc_enable = !!ucontrol->value.integer.value[0];
+
+	if (dc_enable == spec->dc_enable)
+		return 0;
+
+	spec->dc_enable = dc_enable;
+	olpc_xo_update_mic_pins(codec);
+	olpc_xo_update_mic_boost(codec);
+	return 1;
+}
+
+static int olpc_xo_dc_bias_enum_get(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct conexant_spec *spec = codec->spec;
+	ucontrol->value.enumerated.item[0] = spec->dc_input_bias;
+	return 0;
+}
+
+static int olpc_xo_dc_bias_enum_info(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_info *uinfo)
+{
+	return snd_hda_input_mux_info(&olpc_xo_dc_bias, uinfo);
+}
+
+static int olpc_xo_dc_bias_enum_put(struct snd_kcontrol *kcontrol,
+				    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 = &olpc_xo_dc_bias;
+	unsigned int idx;
+
+	idx = ucontrol->value.enumerated.item[0];
+	if (idx >= imux->num_items)
+		idx = imux->num_items - 1;
+	if (spec->dc_input_bias == idx)
+		return 0;
+
+	spec->dc_input_bias = idx;
+	if (spec->dc_enable)
+		olpc_xo_update_mic_pins(codec);
+	return 1;
+}
+
+static const struct snd_kcontrol_new olpc_xo_mixers[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "DC Mode Enable Switch",
+		.info = snd_ctl_boolean_mono_info,
+		.get = olpc_xo_dc_mode_get,
+		.put = olpc_xo_dc_mode_put,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "DC Input Bias Enum",
+		.info = olpc_xo_dc_bias_enum_info,
+		.get = olpc_xo_dc_bias_enum_get,
+		.put = olpc_xo_dc_bias_enum_put,
+	},
+	{}
+};
+
+/* overriding mic boost put callback; update mic boost volume only when
+ * DC mode is disabled
+ */
+static int olpc_xo_mic_boost_put(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct conexant_spec *spec = codec->spec;
+	int ret = snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
+	if (ret > 0 && spec->dc_enable)
+		olpc_xo_update_mic_boost(codec);
+	return ret;
+}
+
+static void cxt_fixup_olpc_xo(struct hda_codec *codec,
+				    const struct hda_fixup *fix, int action)
+{
+	struct conexant_spec *spec = codec->spec;
+	int i;
+
+	if (action != HDA_FIXUP_ACT_PROBE)
+		return;
+
+	spec->gen.mic_autoswitch_hook = olpc_xo_automic;
+	spec->gen.pcm_capture_hook = olpc_xo_capture_hook;
+	spec->dc_mode_path = snd_hda_add_new_path(codec, 0x1e, 0x14, 0);
+
+	snd_hda_add_new_ctls(codec, olpc_xo_mixers);
+
+	/* OLPC's microphone port is DC coupled for use with external sensors,
+	 * therefore we use a 50% mic bias in order to center the input signal
+	 * with the DC input range of the codec.
+	 */
+	snd_hda_codec_set_pin_target(codec, 0x1a, PIN_VREF50);
+
+	/* override mic boost control */
+	for (i = 0; i < spec->gen.kctls.used; i++) {
+		struct snd_kcontrol_new *kctl =
+			snd_array_elem(&spec->gen.kctls, i);
+		if (!strcmp(kctl->name, "Mic Boost Volume")) {
+			kctl->put = olpc_xo_mic_boost_put;
+			break;
+		}
+	}
+}
+
+/*
+ * Fix max input level on mixer widget to 0dB
+ * (originally it has 0x2b steps with 0dB offset 0x14)
+ */
+static void cxt_fixup_cap_mix_amp(struct hda_codec *codec,
+				  const struct hda_fixup *fix, int action)
+{
+	snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT,
+				  (0x14 << AC_AMPCAP_OFFSET_SHIFT) |
+				  (0x14 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+				  (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+				  (1 << AC_AMPCAP_MUTE_SHIFT));
+}
+
+/*
+ * Fix max input level on mixer widget to 0dB
+ * (originally it has 0x1e steps with 0 dB offset 0x17)
+ */
+static void cxt_fixup_cap_mix_amp_5047(struct hda_codec *codec,
+				  const struct hda_fixup *fix, int action)
+{
+	snd_hda_override_amp_caps(codec, 0x10, HDA_INPUT,
+				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
+				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+				  (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+				  (1 << AC_AMPCAP_MUTE_SHIFT));
+}
 
 /* ThinkPad X200 & co with cxt5051 */
 static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = {
@@ -3400,6 +3278,68 @@ static const struct hda_fixup cxt_fixups[] = {
 		.type = HDA_FIXUP_FUNC,
 		.v.func = hda_fixup_thinkpad_acpi,
 	},
+	[CXT_FIXUP_OLPC_XO] = {
+		.type = HDA_FIXUP_FUNC,
+		.v.func = cxt_fixup_olpc_xo,
+	},
+	[CXT_FIXUP_CAP_MIX_AMP] = {
+		.type = HDA_FIXUP_FUNC,
+		.v.func = cxt_fixup_cap_mix_amp,
+	},
+	[CXT_FIXUP_TOSHIBA_P105] = {
+		.type = HDA_FIXUP_PINS,
+		.v.pins = (const struct hda_pintbl[]) {
+			{ 0x10, 0x961701f0 }, /* speaker/hp */
+			{ 0x12, 0x02a1901e }, /* ext mic */
+			{ 0x14, 0x95a70110 }, /* int mic */
+			{}
+		},
+	},
+	[CXT_FIXUP_HP_530] = {
+		.type = HDA_FIXUP_PINS,
+		.v.pins = (const struct hda_pintbl[]) {
+			{ 0x12, 0x90a60160 }, /* int mic */
+			{}
+		},
+		.chained = true,
+		.chain_id = CXT_FIXUP_CAP_MIX_AMP,
+	},
+	[CXT_FIXUP_CAP_MIX_AMP_5047] = {
+		.type = HDA_FIXUP_FUNC,
+		.v.func = cxt_fixup_cap_mix_amp_5047,
+	},
+};
+
+static const struct snd_pci_quirk cxt5045_fixups[] = {
+	SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT_FIXUP_HP_530),
+	SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT_FIXUP_TOSHIBA_P105),
+	/* HP, Packard Bell, Fujitsu-Siemens & Lenovo laptops have
+	 * really bad sound over 0dB on NID 0x17.
+	 */
+	SND_PCI_QUIRK_VENDOR(0x103c, "HP", CXT_FIXUP_CAP_MIX_AMP),
+	SND_PCI_QUIRK_VENDOR(0x1631, "Packard Bell", CXT_FIXUP_CAP_MIX_AMP),
+	SND_PCI_QUIRK_VENDOR(0x1734, "Fujitsu", CXT_FIXUP_CAP_MIX_AMP),
+	SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", CXT_FIXUP_CAP_MIX_AMP),
+	{}
+};
+
+static const struct hda_model_fixup cxt5045_fixup_models[] = {
+	{ .id = CXT_FIXUP_CAP_MIX_AMP, .name = "cap-mix-amp" },
+	{ .id = CXT_FIXUP_TOSHIBA_P105, .name = "toshiba-p105" },
+	{ .id = CXT_FIXUP_HP_530, .name = "hp-530" },
+	{}
+};
+
+static const struct snd_pci_quirk cxt5047_fixups[] = {
+	/* HP laptops have really bad sound over 0 dB on NID 0x10.
+	 */
+	SND_PCI_QUIRK_VENDOR(0x103c, "HP", CXT_FIXUP_CAP_MIX_AMP_5047),
+	{}
+};
+
+static const struct hda_model_fixup cxt5047_fixup_models[] = {
+	{ .id = CXT_FIXUP_CAP_MIX_AMP_5047, .name = "cap-mix-amp" },
+	{}
 };
 
 static const struct snd_pci_quirk cxt5051_fixups[] = {
@@ -3407,10 +3347,16 @@ static const struct snd_pci_quirk cxt5051_fixups[] = {
 	{}
 };
 
+static const struct hda_model_fixup cxt5051_fixup_models[] = {
+	{ .id = CXT_PINCFG_LENOVO_X200, .name = "lenovo-x200" },
+	{}
+};
+
 static const struct snd_pci_quirk cxt5066_fixups[] = {
 	SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC),
 	SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT_FIXUP_GPIO1),
 	SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN),
+	SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT_FIXUP_OLPC_XO),
 	SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410),
 	SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T410", CXT_PINCFG_LENOVO_TP410),
 	SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410),
@@ -3427,6 +3373,17 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
 	{}
 };
 
+static const struct hda_model_fixup cxt5066_fixup_models[] = {
+	{ .id = CXT_FIXUP_STEREO_DMIC, .name = "stereo-dmic" },
+	{ .id = CXT_FIXUP_GPIO1, .name = "gpio1" },
+	{ .id = CXT_FIXUP_HEADPHONE_MIC_PIN, .name = "headphone-mic-pin" },
+	{ .id = CXT_PINCFG_LENOVO_TP410, .name = "tp410" },
+	{ .id = CXT_FIXUP_THINKPAD_ACPI, .name = "thinkpad" },
+	{ .id = CXT_PINCFG_LEMOTE_A1004, .name = "lemote-a1004" },
+	{ .id = CXT_FIXUP_OLPC_XO, .name = "olpc-xo" },
+	{}
+};
+
 /* add "fake" mute amp-caps to DACs on cx5051 so that mixer mute switches
  * can be created (bko#42825)
  */
@@ -3466,19 +3423,28 @@ static int patch_conexant_auto(struct hda_codec *codec)
 	switch (codec->vendor_id) {
 	case 0x14f15045:
 		codec->single_adc_amp = 1;
+		spec->gen.mixer_nid = 0x17;
+		spec->gen.add_stereo_mix_input = 1;
+		snd_hda_pick_fixup(codec, cxt5045_fixup_models,
+				   cxt5045_fixups, cxt_fixups);
 		break;
 	case 0x14f15047:
 		codec->pin_amp_workaround = 1;
 		spec->gen.mixer_nid = 0x19;
+		spec->gen.add_stereo_mix_input = 1;
+		snd_hda_pick_fixup(codec, cxt5047_fixup_models,
+				   cxt5047_fixups, cxt_fixups);
 		break;
 	case 0x14f15051:
 		add_cx5051_fake_mutes(codec);
 		codec->pin_amp_workaround = 1;
-		snd_hda_pick_fixup(codec, NULL, cxt5051_fixups, cxt_fixups);
+		snd_hda_pick_fixup(codec, cxt5051_fixup_models,
+				   cxt5051_fixups, cxt_fixups);
 		break;
 	default:
 		codec->pin_amp_workaround = 1;
-		snd_hda_pick_fixup(codec, NULL, cxt5066_fixups, cxt_fixups);
+		snd_hda_pick_fixup(codec, cxt5066_fixup_models,
+				   cxt5066_fixups, cxt_fixups);
 		break;
 	}