ALSA: hda - Add auto-parser support to cxt5045 / CX20549 Venice

Signed-off-by: Takashi Iwai <tiwai@suse.de>
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index cdb9f49..623cd9b 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -1059,6 +1059,7 @@
 #ifdef CONFIG_SND_DEBUG
 	CXT5045_TEST,
 #endif
+	CXT5045_AUTO,
 	CXT5045_MODELS
 };
 
@@ -1071,6 +1072,7 @@
 #ifdef CONFIG_SND_DEBUG
 	[CXT5045_TEST]		= "test",
 #endif
+	[CXT5045_AUTO]			= "auto",
 };
 
 static const struct snd_pci_quirk cxt5045_cfg_tbl[] = {
@@ -1097,6 +1099,14 @@
 	struct conexant_spec *spec;
 	int board_config;
 
+	board_config = snd_hda_check_board_config(codec, CXT5045_MODELS,
+						  cxt5045_models,
+						  cxt5045_cfg_tbl);
+	if (board_config < 0)
+		board_config = CXT5045_AUTO;
+	if (board_config == CXT5045_AUTO)
+		return patch_conexant_auto(codec);
+
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (!spec)
 		return -ENOMEM;
@@ -1123,9 +1133,6 @@
 
 	codec->patch_ops = conexant_patch_ops;
 
-	board_config = snd_hda_check_board_config(codec, CXT5045_MODELS,
-						  cxt5045_models,
-						  cxt5045_cfg_tbl);
 	switch (board_config) {
 	case CXT5045_LAPTOP_HPSENSE:
 		codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
@@ -1956,11 +1963,8 @@
 						  cxt5051_cfg_tbl);
 	if (board_config < 0)
 		board_config = CXT5051_AUTO;
-	if (board_config == CXT5051_AUTO) {
-		printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
-		       codec->chip_name);
+	if (board_config == CXT5051_AUTO)
 		return patch_conexant_auto(codec);
-	}
 
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (!spec)
@@ -3688,15 +3692,15 @@
 				    AC_VERB_SET_CONNECT_SEL, idx);
 }
 
-static void cx_auto_init_output(struct hda_codec *codec)
+static void mute_outputs(struct hda_codec *codec, int num_nids,
+			 const hda_nid_t *nids)
 {
-	struct conexant_spec *spec = codec->spec;
-	struct auto_pin_cfg *cfg = &spec->autocfg;
-	hda_nid_t nid;
 	int i, val;
 
-	for (i = 0; i < spec->multiout.num_dacs; i++) {
-		nid = spec->multiout.dac_nids[i];
+	for (i = 0; i < num_nids; i++) {
+		hda_nid_t nid = nids[i];
+		if (!(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
+			continue;
 		if (query_amp_caps(codec, nid, HDA_OUTPUT) & AC_AMPCAP_MUTE)
 			val = AMP_OUT_MUTE;
 		else
@@ -3704,10 +3708,22 @@
 		snd_hda_codec_write(codec, nid, 0,
 				    AC_VERB_SET_AMP_GAIN_MUTE, val);
 	}
+}
 
+static void cx_auto_init_output(struct hda_codec *codec)
+{
+	struct conexant_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	hda_nid_t nid;
+	int i;
+
+	mute_outputs(codec, spec->multiout.num_dacs, spec->multiout.dac_nids);
 	for (i = 0; i < cfg->hp_outs; i++)
 		snd_hda_codec_write(codec, cfg->hp_pins[i], 0,
 				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+	mute_outputs(codec, cfg->hp_outs, cfg->hp_pins);
+	mute_outputs(codec, cfg->line_outs, cfg->line_out_pins);
+	mute_outputs(codec, cfg->speaker_outs, cfg->speaker_pins);
 	if (spec->auto_mute) {
 		for (i = 0; i < cfg->hp_outs; i++) {
 			snd_hda_codec_write(codec, cfg->hp_pins[i], 0,
@@ -3747,6 +3763,8 @@
 
 	for (i = 0; i < spec->num_adc_nids; i++) {
 		hda_nid_t nid = spec->adc_nids[i];
+		if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP))
+			continue;
 		if (query_amp_caps(codec, nid, HDA_INPUT) & AC_AMPCAP_MUTE)
 			val = AMP_IN_MUTE(0);
 		else
@@ -3839,6 +3857,19 @@
 #define cx_auto_add_pb_volume(codec, nid, str, idx)			\
 	cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT)
 
+static int try_add_pb_volume(struct hda_codec *codec, hda_nid_t dac,
+			     hda_nid_t pin, const char *name, int idx)
+{
+	unsigned int caps;
+	caps = query_amp_caps(codec, dac, HDA_OUTPUT);
+	if (caps & AC_AMPCAP_NUM_STEPS)
+		return cx_auto_add_pb_volume(codec, dac, name, idx);
+	caps = query_amp_caps(codec, pin, HDA_OUTPUT);
+	if (caps & AC_AMPCAP_NUM_STEPS)
+		return cx_auto_add_pb_volume(codec, pin, name, idx);
+	return 0;
+}
+
 static int cx_auto_build_output_controls(struct hda_codec *codec)
 {
 	struct conexant_spec *spec = codec->spec;
@@ -3847,8 +3878,10 @@
 	static const char * const texts[3] = { "Front", "Surround", "CLFE" };
 
 	if (spec->dac_info_filled == 1)
-		return cx_auto_add_pb_volume(codec, spec->dac_info[0].dac,
-					     "Master", 0);
+		return try_add_pb_volume(codec, spec->dac_info[0].dac,
+					 spec->dac_info[0].pin,
+					 "Master", 0);
+
 	for (i = 0; i < spec->dac_info_filled; i++) {
 		const char *label;
 		int idx, type;
@@ -3872,8 +3905,9 @@
 			idx = num_spk++;
 			break;
 		}
-		err = cx_auto_add_pb_volume(codec, spec->dac_info[i].dac,
-					    label, idx);
+		err = try_add_pb_volume(codec, spec->dac_info[i].dac,
+					spec->dac_info[i].pin,
+					label, idx);
 		if (err < 0)
 			return err;
 	}
@@ -3976,19 +4010,31 @@
 	struct conexant_spec *spec;
 	int err;
 
+	printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+	       codec->chip_name);
+
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (!spec)
 		return -ENOMEM;
 	codec->spec = spec;
-	if (codec->vendor_id == 0x14f15051) {
+	switch (codec->vendor_id) {
+	case 0x14f15051:
 		codec->pin_amp_workaround = 1;
 		spec->adc_nids = cxt5051_adc_nids;
 		spec->num_adc_nids = ARRAY_SIZE(cxt5051_adc_nids);
 		spec->capsrc_nids = spec->adc_nids;
-	} else {
+		break;
+	case 0x14f15045:
+		codec->pin_amp_workaround = 1;
+		spec->adc_nids = cxt5045_adc_nids;
+		spec->num_adc_nids = ARRAY_SIZE(cxt5045_adc_nids);
+		spec->capsrc_nids = spec->adc_nids;
+		break;
+	default:
 		spec->adc_nids = cx_auto_adc_nids;
 		spec->num_adc_nids = ARRAY_SIZE(cx_auto_adc_nids);
 		spec->capsrc_nids = spec->adc_nids;
+		break;
 	}
 	err = cx_auto_parse_auto_config(codec);
 	if (err < 0) {