Browse Source

Merge branch 'test/hda-gen-parser' into test/hda-migrate

* test/hda-gen-parser:
  ALSA: hda - Improve naming rule for primary output
  ALSA: hda - Add PCM capture hook to hda_gen_spec
  ALSA: hda - Record all detected ADCs in hda_gen_spec
  ALSA: hda - Move vmaster TLV parsing to snd_hda_gen_parse_auto_config()
  ALSA: hda - Add input jack mode enum controls to generic parser
  ALSA: hda - Give more comments to hda_gen_spec flags
  ALSA: hda - Add suppress_auto_mute flag to hda_gen_spec
  ALSA: hda - Record the current speaker / LO mute status in hda_gen_spec
  ALSA: hda - Properly call automute/switch hooks at init
Takashi Iwai 12 years ago
parent
commit
8f0fdc09aa
2 changed files with 315 additions and 35 deletions
  1. 285 27
      sound/pci/hda/hda_generic.c
  2. 30 8
      sound/pci/hda/hda_generic.h

+ 285 - 27
sound/pci/hda/hda_generic.c

@@ -26,6 +26,7 @@
 #include <linux/sort.h>
 #include <linux/ctype.h>
 #include <linux/string.h>
+#include <linux/bitops.h>
 #include <sound/core.h>
 #include <sound/jack.h>
 #include "hda_codec.h"
@@ -119,6 +120,9 @@ static void parse_user_hints(struct hda_codec *codec)
 	if (val >= 0)
 		codec->single_adc_amp = !!val;
 
+	val = snd_hda_get_bool_hint(codec, "auto_mute");
+	if (val >= 0)
+		spec->suppress_auto_mute = !val;
 	val = snd_hda_get_bool_hint(codec, "auto_mic");
 	if (val >= 0)
 		spec->suppress_auto_mic = !val;
@@ -146,6 +150,9 @@ static void parse_user_hints(struct hda_codec *codec)
 	val = snd_hda_get_bool_hint(codec, "add_out_jack_modes");
 	if (val >= 0)
 		spec->add_out_jack_modes = !!val;
+	val = snd_hda_get_bool_hint(codec, "add_in_jack_modes");
+	if (val >= 0)
+		spec->add_in_jack_modes = !!val;
 
 	if (!snd_hda_get_int_hint(codec, "mixer_nid", &val))
 		spec->mixer_nid = val;
@@ -818,19 +825,27 @@ static int add_stereo_sw(struct hda_codec *codec, const char *pfx,
 	return add_sw_ctl(codec, pfx, cidx, chs, path);
 }
 
+/* any ctl assigned to the path with the given index? */
+static bool path_has_mixer(struct hda_codec *codec, int path_idx, int ctl_type)
+{
+	struct nid_path *path = snd_hda_get_path_from_idx(codec, path_idx);
+	return path && path->ctls[ctl_type];
+}
+
 static const char * const channel_name[4] = {
 	"Front", "Surround", "CLFE", "Side"
 };
 
 /* give some appropriate ctl name prefix for the given line out channel */
-static const char *get_line_out_pfx(struct hda_gen_spec *spec, int ch,
-				    bool can_be_master, int *index)
+static const char *get_line_out_pfx(struct hda_codec *codec, int ch,
+				    int *index, int ctl_type)
 {
+	struct hda_gen_spec *spec = codec->spec;
 	struct auto_pin_cfg *cfg = &spec->autocfg;
 
 	*index = 0;
 	if (cfg->line_outs == 1 && !spec->multi_ios &&
-	    !cfg->hp_outs && !cfg->speaker_outs && can_be_master)
+	    !cfg->hp_outs && !cfg->speaker_outs)
 		return spec->vmaster_mute.hook ? "PCM" : "Master";
 
 	/* if there is really a single DAC used in the whole output paths,
@@ -840,24 +855,41 @@ static const char *get_line_out_pfx(struct hda_gen_spec *spec, int ch,
 	    !spec->multiout.hp_out_nid[0] && !spec->multiout.extra_out_nid[0])
 		return spec->vmaster_mute.hook ? "PCM" : "Master";
 
+	/* multi-io channels */
+	if (ch >= cfg->line_outs)
+		return channel_name[ch];
+
 	switch (cfg->line_out_type) {
 	case AUTO_PIN_SPEAKER_OUT:
+		/* if the primary channel vol/mute is shared with HP volume,
+		 * don't name it as Speaker
+		 */
+		if (!ch && cfg->hp_outs &&
+		    !path_has_mixer(codec, spec->hp_paths[0], ctl_type))
+			break;
 		if (cfg->line_outs == 1)
 			return "Speaker";
 		if (cfg->line_outs == 2)
 			return ch ? "Bass Speaker" : "Speaker";
 		break;
 	case AUTO_PIN_HP_OUT:
+		/* if the primary channel vol/mute is shared with spk volume,
+		 * don't name it as Headphone
+		 */
+		if (!ch && cfg->speaker_outs &&
+		    !path_has_mixer(codec, spec->speaker_paths[0], ctl_type))
+			break;
 		/* for multi-io case, only the primary out */
 		if (ch && spec->multi_ios)
 			break;
 		*index = ch;
 		return "Headphone";
-	default:
-		if (cfg->line_outs == 1 && !spec->multi_ios)
-			return "PCM";
-		break;
 	}
+
+	/* for a single channel output, we don't have to name the channel */
+	if (cfg->line_outs == 1 && !spec->multi_ios)
+		return "PCM";
+
 	if (ch >= ARRAY_SIZE(channel_name)) {
 		snd_BUG();
 		return "PCM";
@@ -1594,6 +1626,9 @@ static int parse_output_paths(struct hda_codec *codec)
 		path = snd_hda_get_path_from_idx(codec, spec->out_paths[0]);
 		if (path)
 			spec->vmaster_nid = look_for_out_vol_nid(codec, path);
+		if (spec->vmaster_nid)
+			snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
+						HDA_OUTPUT, spec->vmaster_tlv);
 	}
 
 	kfree(best_cfg);
@@ -1616,16 +1651,11 @@ static int create_multi_out_ctls(struct hda_codec *codec,
 		int index;
 		struct nid_path *path;
 
-		if (i >= cfg->line_outs) {
-			index = 0;
-			name = channel_name[i];
-		} else {
-			name = get_line_out_pfx(spec, i, true, &index);
-		}
-
 		path = snd_hda_get_path_from_idx(codec, spec->out_paths[i]);
 		if (!path)
 			continue;
+
+		name = get_line_out_pfx(codec, i, &index, NID_PATH_VOL_CTL);
 		if (!name || !strcmp(name, "CLFE")) {
 			/* Center/LFE */
 			err = add_vol_ctl(codec, "Center", 0, 1, path);
@@ -1634,6 +1664,14 @@ static int create_multi_out_ctls(struct hda_codec *codec,
 			err = add_vol_ctl(codec, "LFE", 0, 2, path);
 			if (err < 0)
 				return err;
+		} else {
+			err = add_stereo_vol(codec, name, index, path);
+			if (err < 0)
+				return err;
+		}
+
+		name = get_line_out_pfx(codec, i, &index, NID_PATH_MUTE_CTL);
+		if (!name || !strcmp(name, "CLFE")) {
 			err = add_sw_ctl(codec, "Center", 0, 1, path);
 			if (err < 0)
 				return err;
@@ -1641,9 +1679,6 @@ static int create_multi_out_ctls(struct hda_codec *codec,
 			if (err < 0)
 				return err;
 		} else {
-			err = add_stereo_vol(codec, name, index, path);
-			if (err < 0)
-				return err;
 			err = add_stereo_sw(codec, name, index, path);
 			if (err < 0)
 				return err;
@@ -1826,6 +1861,8 @@ get_multiio_path(struct hda_codec *codec, int idx)
 		spec->out_paths[spec->autocfg.line_outs + idx]);
 }
 
+static void update_automute_all(struct hda_codec *codec);
+
 static int set_multi_io(struct hda_codec *codec, int idx, bool output)
 {
 	struct hda_gen_spec *spec = codec->spec;
@@ -1850,9 +1887,7 @@ static int set_multi_io(struct hda_codec *codec, int idx, bool output)
 	}
 
 	/* update jack retasking in case it modifies any of them */
-	snd_hda_gen_hp_automute(codec, NULL);
-	snd_hda_gen_line_automute(codec, NULL);
-	snd_hda_gen_mic_autoswitch(codec, NULL);
+	update_automute_all(codec);
 
 	return 0;
 }
@@ -2135,6 +2170,136 @@ static int create_out_jack_modes(struct hda_codec *codec, int num_pins,
 	return 0;
 }
 
+/*
+ * input jack mode
+ */
+
+/* from AC_PINCTL_VREF_HIZ to AC_PINCTL_VREF_100 */
+#define NUM_VREFS	6
+
+static const char * const vref_texts[NUM_VREFS] = {
+	"Line In", "Mic 50pc Bias", "Mic 0V Bias",
+	"", "Mic 80pc Bias", "Mic 100pc Bias"
+};
+
+static unsigned int get_vref_caps(struct hda_codec *codec, hda_nid_t pin)
+{
+	unsigned int pincap;
+
+	pincap = snd_hda_query_pin_caps(codec, pin);
+	pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
+	/* filter out unusual vrefs */
+	pincap &= ~(AC_PINCAP_VREF_GRD | AC_PINCAP_VREF_100);
+	return pincap;
+}
+
+/* convert from the enum item index to the vref ctl index (0=HIZ, 1=50%...) */
+static int get_vref_idx(unsigned int vref_caps, unsigned int item_idx)
+{
+	unsigned int i, n = 0;
+
+	for (i = 0; i < NUM_VREFS; i++) {
+		if (vref_caps & (1 << i)) {
+			if (n == item_idx)
+				return i;
+			n++;
+		}
+	}
+	return 0;
+}
+
+/* convert back from the vref ctl index to the enum item index */
+static int cvt_from_vref_idx(unsigned int vref_caps, unsigned int idx)
+{
+	unsigned int i, n = 0;
+
+	for (i = 0; i < NUM_VREFS; i++) {
+		if (i == idx)
+			return n;
+		if (vref_caps & (1 << i))
+			n++;
+	}
+	return 0;
+}
+
+static int in_jack_mode_info(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_info *uinfo)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	hda_nid_t nid = kcontrol->private_value;
+	unsigned int vref_caps = get_vref_caps(codec, nid);
+
+	snd_hda_enum_helper_info(kcontrol, uinfo, hweight32(vref_caps),
+				 vref_texts);
+	/* set the right text */
+	strcpy(uinfo->value.enumerated.name,
+	       vref_texts[get_vref_idx(vref_caps, uinfo->value.enumerated.item)]);
+	return 0;
+}
+
+static int in_jack_mode_get(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	hda_nid_t nid = kcontrol->private_value;
+	unsigned int vref_caps = get_vref_caps(codec, nid);
+	unsigned int idx;
+
+	idx = snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_VREFEN;
+	ucontrol->value.enumerated.item[0] = cvt_from_vref_idx(vref_caps, idx);
+	return 0;
+}
+
+static int in_jack_mode_put(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	hda_nid_t nid = kcontrol->private_value;
+	unsigned int vref_caps = get_vref_caps(codec, nid);
+	unsigned int val, idx;
+
+	val = snd_hda_codec_get_pin_target(codec, nid);
+	idx = cvt_from_vref_idx(vref_caps, val & AC_PINCTL_VREFEN);
+	if (idx == ucontrol->value.enumerated.item[0])
+		return 0;
+
+	val &= ~AC_PINCTL_VREFEN;
+	val |= get_vref_idx(vref_caps, ucontrol->value.enumerated.item[0]);
+	snd_hda_set_pin_ctl_cache(codec, nid, val);
+	return 1;
+}
+
+static const struct snd_kcontrol_new in_jack_mode_enum = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.info = in_jack_mode_info,
+	.get = in_jack_mode_get,
+	.put = in_jack_mode_put,
+};
+
+static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	unsigned int defcfg;
+	struct snd_kcontrol_new *knew;
+	char name[44];
+
+	/* no jack mode for fixed pins */
+	defcfg = snd_hda_codec_get_pincfg(codec, pin);
+	if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT)
+		return 0;
+
+	/* no multiple vref caps? */
+	if (hweight32(get_vref_caps(codec, pin)) <= 1)
+		return 0;
+
+	get_jack_mode_name(codec, pin, name, sizeof(name));
+	knew = snd_hda_gen_add_kctl(spec, name, &in_jack_mode_enum);
+	if (!knew)
+		return -ENOMEM;
+	knew->private_value = pin;
+	return 0;
+}
+
 
 /*
  * Parse input paths
@@ -2228,6 +2393,11 @@ static int fill_adc_nids(struct hda_codec *codec)
 			break;
 	}
 	spec->num_adc_nids = nums;
+
+	/* copy the detected ADCs to all_adcs[] */
+	spec->num_all_adcs = nums;
+	memcpy(spec->all_adcs, spec->adc_nids, nums * sizeof(hda_nid_t));
+
 	return nums;
 }
 
@@ -2389,6 +2559,12 @@ static int create_input_ctls(struct hda_codec *codec)
 		err = parse_capture_source(codec, pin, num_adcs, label, -mixer);
 		if (err < 0)
 			return err;
+
+		if (spec->add_in_jack_modes) {
+			err = create_in_jack_mode(codec, pin);
+			if (err < 0)
+				return err;
+		}
 	}
 
 	if (mixer && spec->add_stereo_mix_input) {
@@ -3047,6 +3223,7 @@ void snd_hda_gen_update_outputs(struct hda_codec *codec)
 	else
 		on = spec->hp_jack_present | spec->line_jack_present;
 	on |= spec->master_mute;
+	spec->speaker_muted = on;
 	do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins),
 		    spec->autocfg.speaker_pins, on);
 
@@ -3060,6 +3237,7 @@ void snd_hda_gen_update_outputs(struct hda_codec *codec)
 	else
 		on = spec->hp_jack_present;
 	on |= spec->master_mute;
+	spec->line_out_muted = on;
 	do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins),
 		    spec->autocfg.line_out_pins, on);
 }
@@ -3131,6 +3309,25 @@ void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *ja
 }
 EXPORT_SYMBOL_HDA(snd_hda_gen_mic_autoswitch);
 
+/* update jack retasking */
+static void update_automute_all(struct hda_codec *codec)
+{
+	struct hda_gen_spec *spec = codec->spec;
+
+	if (spec->hp_automute_hook)
+		spec->hp_automute_hook(codec, NULL);
+	else
+		snd_hda_gen_hp_automute(codec, NULL);
+	if (spec->line_automute_hook)
+		spec->line_automute_hook(codec, NULL);
+	else
+		snd_hda_gen_line_automute(codec, NULL);
+	if (spec->mic_autoswitch_hook)
+		spec->mic_autoswitch_hook(codec, NULL);
+	else
+		snd_hda_gen_mic_autoswitch(codec, NULL);
+}
+
 /*
  * Auto-Mute mode mixer enum support
  */
@@ -3232,6 +3429,9 @@ static int check_auto_mute_availability(struct hda_codec *codec)
 	int present = 0;
 	int i, err;
 
+	if (spec->suppress_auto_mute)
+		return 0;
+
 	if (cfg->hp_pins[0])
 		present++;
 	if (cfg->line_out_pins[0])
@@ -3585,11 +3785,8 @@ int snd_hda_gen_build_controls(struct hda_codec *codec)
 	/* if we have no master control, let's create it */
 	if (!spec->no_analog &&
 	    !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
-		unsigned int vmaster_tlv[4];
-		snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
-					HDA_OUTPUT, vmaster_tlv);
 		err = snd_hda_add_vmaster(codec, "Master Playback Volume",
-					  vmaster_tlv, slave_pfxs,
+					  spec->vmaster_tlv, slave_pfxs,
 					  "Playback Volume");
 		if (err < 0)
 			return err;
@@ -3643,6 +3840,16 @@ static void call_pcm_playback_hook(struct hda_pcm_stream *hinfo,
 		spec->pcm_playback_hook(hinfo, codec, substream, action);
 }
 
+static void call_pcm_capture_hook(struct hda_pcm_stream *hinfo,
+				  struct hda_codec *codec,
+				  struct snd_pcm_substream *substream,
+				  int action)
+{
+	struct hda_gen_spec *spec = codec->spec;
+	if (spec->pcm_capture_hook)
+		spec->pcm_capture_hook(hinfo, codec, substream, action);
+}
+
 /*
  * Analog playback callbacks
  */
@@ -3710,6 +3917,44 @@ static int playback_pcm_close(struct hda_pcm_stream *hinfo,
 	return 0;
 }
 
+static int capture_pcm_open(struct hda_pcm_stream *hinfo,
+			    struct hda_codec *codec,
+			    struct snd_pcm_substream *substream)
+{
+	call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_OPEN);
+	return 0;
+}
+
+static int capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+			       struct hda_codec *codec,
+			       unsigned int stream_tag,
+			       unsigned int format,
+			       struct snd_pcm_substream *substream)
+{
+	snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
+	call_pcm_capture_hook(hinfo, codec, substream,
+			      HDA_GEN_PCM_ACT_PREPARE);
+	return 0;
+}
+
+static int capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+			       struct hda_codec *codec,
+			       struct snd_pcm_substream *substream)
+{
+	snd_hda_codec_cleanup_stream(codec, hinfo->nid);
+	call_pcm_capture_hook(hinfo, codec, substream,
+			      HDA_GEN_PCM_ACT_CLEANUP);
+	return 0;
+}
+
+static int capture_pcm_close(struct hda_pcm_stream *hinfo,
+			     struct hda_codec *codec,
+			     struct snd_pcm_substream *substream)
+{
+	call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLOSE);
+	return 0;
+}
+
 static int alt_playback_pcm_open(struct hda_pcm_stream *hinfo,
 				 struct hda_codec *codec,
 				 struct snd_pcm_substream *substream)
@@ -3804,6 +4049,9 @@ static int dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
 /*
  * Analog capture
  */
+#define alt_capture_pcm_open	capture_pcm_open
+#define alt_capture_pcm_close	capture_pcm_close
+
 static int alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
 				   struct hda_codec *codec,
 				   unsigned int stream_tag,
@@ -3814,6 +4062,8 @@ static int alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
 
 	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1],
 				   stream_tag, 0, format);
+	call_pcm_capture_hook(hinfo, codec, substream,
+			      HDA_GEN_PCM_ACT_PREPARE);
 	return 0;
 }
 
@@ -3825,6 +4075,8 @@ static int alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
 
 	snd_hda_codec_cleanup_stream(codec,
 				     spec->adc_nids[substream->number + 1]);
+	call_pcm_capture_hook(hinfo, codec, substream,
+			      HDA_GEN_PCM_ACT_CLEANUP);
 	return 0;
 }
 
@@ -3848,6 +4100,12 @@ static const struct hda_pcm_stream pcm_analog_capture = {
 	.channels_min = 2,
 	.channels_max = 2,
 	/* NID is set in build_pcms */
+	.ops = {
+		.open = capture_pcm_open,
+		.close = capture_pcm_close,
+		.prepare = capture_pcm_prepare,
+		.cleanup = capture_pcm_cleanup
+	},
 };
 
 static const struct hda_pcm_stream pcm_analog_alt_playback = {
@@ -3869,6 +4127,8 @@ static const struct hda_pcm_stream pcm_analog_alt_capture = {
 	.channels_max = 2,
 	/* NID is set in build_pcms */
 	.ops = {
+		.open = alt_capture_pcm_open,
+		.close = alt_capture_pcm_close,
 		.prepare = alt_capture_pcm_prepare,
 		.cleanup = alt_capture_pcm_cleanup
 	},
@@ -4281,9 +4541,7 @@ int snd_hda_gen_init(struct hda_codec *codec)
 	clear_unsol_on_unused_pins(codec);
 
 	/* call init functions of standard auto-mute helpers */
-	snd_hda_gen_hp_automute(codec, NULL);
-	snd_hda_gen_line_automute(codec, NULL);
-	snd_hda_gen_mic_autoswitch(codec, NULL);
+	update_automute_all(codec);
 
 	snd_hda_codec_flush_amp_cache(codec);
 	snd_hda_codec_flush_cmd_cache(codec);

+ 30 - 8
sound/pci/hda/hda_generic.h

@@ -142,9 +142,11 @@ struct hda_gen_spec {
 	unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS];
 	hda_nid_t shared_mic_vref_pin;
 
-	/* DAC list */
+	/* DAC/ADC lists */
 	int num_all_dacs;
 	hda_nid_t all_dacs[16];
+	int num_all_adcs;
+	hda_nid_t all_adcs[AUTO_CFG_MAX_OUTS];
 
 	/* path list */
 	struct snd_array paths;
@@ -164,24 +166,34 @@ struct hda_gen_spec {
 	struct automic_entry am_entry[MAX_AUTO_MIC_PINS];
 
 	/* for pin sensing */
+	/* current status; set in hda_geneic.c */
 	unsigned int hp_jack_present:1;
 	unsigned int line_jack_present:1;
-	unsigned int master_mute:1;
+	unsigned int speaker_muted:1; /* current status of speaker mute */
+	unsigned int line_out_muted:1; /* current status of LO mute */
+
+	/* internal states of automute / autoswitch behavior */
 	unsigned int auto_mic:1;
 	unsigned int automute_speaker:1; /* automute speaker outputs */
 	unsigned int automute_lo:1; /* automute LO outputs */
+
+	/* capabilities detected by parser */
 	unsigned int detect_hp:1;	/* Headphone detection enabled */
 	unsigned int detect_lo:1;	/* Line-out detection enabled */
 	unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */
 	unsigned int automute_lo_possible:1;	  /* there are line outs and HP */
+
+	/* additional parameters set by codec drivers */
+	unsigned int master_mute:1;	/* master mute over all */
 	unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */
-	unsigned int suppress_auto_mic:1; /* suppress input jack auto switch */
 	unsigned int line_in_auto_switch:1; /* allow line-in auto switch */
 
-	/* other flags */
+	/* parser behavior flags; set before snd_hda_gen_parse_auto_config() */
+	unsigned int suppress_auto_mute:1; /* suppress input jack auto mute */
+	unsigned int suppress_auto_mic:1; /* suppress input jack auto switch */
+
+	/* other parse behavior flags */
 	unsigned int need_dac_fix:1; /* need to limit DACs for multi channels */
-	unsigned int no_analog:1; /* digital I/O only */
-	unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */
 	unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */
 	unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */
 	unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */
@@ -189,16 +201,22 @@ struct hda_gen_spec {
 	unsigned int own_eapd_ctl:1; /* set EAPD by own function */
 	unsigned int vmaster_mute_enum:1; /* add vmaster mute mode enum */
 	unsigned int indep_hp:1; /* independent HP supported */
-	unsigned int indep_hp_enabled:1; /* independent HP enabled */
 	unsigned int prefer_hp_amp:1; /* enable HP amp for speaker if any */
 	unsigned int add_stereo_mix_input:1; /* add aamix as a capture src */
 	unsigned int add_out_jack_modes:1; /* add output jack mode enum ctls */
+	unsigned int add_in_jack_modes:1; /* add input jack mode enum ctls */
+
+	/* other internal flags */
+	unsigned int no_analog:1; /* digital I/O only */
+	unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */
+	unsigned int indep_hp_enabled:1; /* independent HP enabled */
 
 	/* loopback mixing mode */
 	bool aamix_mode;
 
 	/* for virtual master */
 	hda_nid_t vmaster_nid;
+	unsigned int vmaster_tlv[4];
 	struct hda_vmaster_mute_hook vmaster_mute;
 #ifdef CONFIG_PM
 	struct hda_loopback_check loopback;
@@ -215,11 +233,15 @@ struct hda_gen_spec {
 	void (*automute_hook)(struct hda_codec *codec);
 	void (*cap_sync_hook)(struct hda_codec *codec);
 
-	/* PCM playback hook */
+	/* PCM hooks */
 	void (*pcm_playback_hook)(struct hda_pcm_stream *hinfo,
 				  struct hda_codec *codec,
 				  struct snd_pcm_substream *substream,
 				  int action);
+	void (*pcm_capture_hook)(struct hda_pcm_stream *hinfo,
+				 struct hda_codec *codec,
+				 struct snd_pcm_substream *substream,
+				 int action);
 
 	/* automute / autoswitch hooks */
 	void (*hp_automute_hook)(struct hda_codec *codec,