[ALC668] : Asus N751JK - mic selection not working
diff mbox

Message ID CAN8ccibaxJR3rrXrFr1_pYV8V7KkoaHo1y9yyjFHTp+_sG9sFg@mail.gmail.com
State New
Headers show

Commit Message

Raymond Yau Aug. 5, 2015, 1:40 a.m. UTC
> --------------------------------
> 2. External microphone
> --------------------------------

> The Asus N751JK notebook (ALC668) has a 3,5mm 4-ring combo jack for
> headphones and microphone.
> The headphone is detected correct and auto-mute works good (jack
detection).
> The microphone is not detected and does not work.

> I was unable to get it to work with hdajackretask or any of the Asus model
settings, such as:
> * options snd_hda_intel model=,asus-mode4

> I was able to get the microphone to work with the following setting:
> * options snd_hda_intel model=,dell-headset-multi

> This setting creates two new items in PulseAudio making a total of three:
> * Internal microphone (works out of the box)
> * Headset microphone (works indeed with a headset)
> * Microphone (don't know if this works, since I don't have a single
> microphone, but it does not work with the headset)

> Unfortunately the auto-input-device-switch to the Headset microphone does
> not seem to work.
> When I plugin a headset, the headphones get detected and the Internal
speakers are muted.
> The Input device does not automatically switch from Internal microphone to
Headset microphone.
> I have to do this manually, after which it works.
> I don't know if the microphone has a jack detection.
> If it does not, maybe it is possible to use the headphones jack detection?

> I have added the alsa-info without any snd_intel / alsa modifications
here:
http://www.alsa-project.org/db/?f=0b5135bd12f28bb5cb182f83bfa21bc50022faa6

> Result of "pactl list sources" plugged and unplugged in this order, with
your previous patches applied.
>
>
> Nothing: http://pastebin.com/RWuiW0RG
> Headphones: http://pastebin.com/GvEkQ8HB
> Microphone: http://pastebin.com/NRiREULS
> Headset: http://pastebin.com/QuGLFD8A
> Subwoofer: http://pastebin.com/nxZNGgyY
>
> For dell-headset-multi, [JACK Headphone Mic] may need fix in pulseaudio
conf files

However it seem that Sound Preference in Mate Ubutnu 15.10 Alpha only show
active port of the source and You have to use pavucontrol to change the
Capture Source/Active port manually after you plug the jack




Following is a variant of the dell-headset-multi which support headset only

asus_headset model require

1) pin fixup only the headset mic pin and no headphone mic pin
2) create "Headset Mic Jack" kctl by spec->hs_mic_use_hp_sense and new
version of __snd_hda_jack_add_kct() with hp_pin as sense_nid
"Headset Mic Jack" is also slave of "Headphone Jack"

3) modify check_auto_mic_availability() to allow auto mic selection for
headset mic which use hs_mic_use_hp_sense
4) update mux_pin for alc668 codec when spec->auto_mic and
alc668_combo_type is equal to ALC_HEADSET_MODE_HEADSET by
snd_hda_gen_mic_autoswitch()

This new model only support headset and it does not support headphone nor
mic, only headphone has event input device and no headset mic event input

For Dell Alienware 14, 17 and 18 which have three jacks: headset, headphone
and mic, auto_mic is not enabled since two of  the three capture sources
are detectable

Comments

Arthur Borsboom Aug. 5, 2015, 9:30 p.m. UTC | #1
I have applied the patch against the mainline kernel 4.2.0-rc5.
I have created the file /etc/modprobe.d/alsa-base.conf and added this line
and rebooted.

options snd_hda_intel model=,asus-headset

Works
---------
Subwoofer
Plugging and unplugging 3,5mm jack: PA switches from speakers to headphones
and from internal microphone to headset microphone.
Playing music on speakers
Playing music on headset

Does not work
-------------------
Microphone: My voice by the headset is not getting to PA.
When playing music the VU meters of the Headphones (= good) and the
Microphone (= not good) in PA show both the music playing.

Patch
diff mbox

diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index b077bb6..a96c56b 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -2097,14 +2097,21 @@  static int create_extra_out(struct hda_codec *codec, int path_idx,
 {
 	struct nid_path *path;
 	int err;
+	struct hda_gen_spec *spec = codec->spec;
 
 	path = snd_hda_get_path_from_idx(codec, path_idx);
 	if (!path)
 		return 0;
-	err = add_stereo_vol(codec, pfx, cidx, path);
+	if ((strcmp(pfx, "Headphone") == 0) && spec->hs_mic_use_hp_sense)
+		err = add_stereo_vol(codec, "Headset", cidx, path);
+	else
+		err = add_stereo_vol(codec, pfx, cidx, path);
 	if (err < 0)
 		return err;
-	err = add_stereo_sw(codec, pfx, cidx, path);
+	if ((strcmp(pfx, "Headphone") == 0) && spec->hs_mic_use_hp_sense)
+		err = add_stereo_sw(codec, "Headset", cidx, path);
+	else
+		err = add_stereo_sw(codec, pfx, cidx, path);
 	if (err < 0)
 		return err;
 	return 0;
@@ -4665,7 +4672,9 @@  static int check_auto_mic_availability(struct hda_codec *codec)
 			if (!spec->line_in_auto_switch &&
 			    cfg->inputs[i].type != AUTO_PIN_MIC)
 				return 0; /* only mic is allowed */
-			if (!is_jack_detectable(codec, nid))
+			if (!is_jack_detectable(codec, nid) &&
+				!(spec->hs_mic_use_hp_sense &&
+				cfg->inputs[i].is_headset_mic))
 				return 0; /* no unsol support */
 			break;
 		}
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index 56e4139..96f8214 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -236,6 +236,7 @@  struct hda_gen_spec {
 	unsigned int indep_hp_enabled:1; /* independent HP enabled */
 	unsigned int have_aamix_ctl:1;
 	unsigned int hp_mic_jack_modes:1;
+	unsigned int hs_mic_use_hp_sense:1;
 
 	/* additional mute flags (only effective with auto_mute_via_amp=1) */
 	u64 mute_bits;
diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c
index 366efbf..2f308a5 100644
--- a/sound/pci/hda/hda_jack.c
+++ b/sound/pci/hda/hda_jack.c
@@ -19,6 +19,7 @@ 
 #include "hda_local.h"
 #include "hda_auto_parser.h"
 #include "hda_jack.h"
+#include "hda_generic.h"
 
 /**
  * is_jack_detectable - Check whether the given pin is jack-detectable
@@ -157,7 +158,15 @@  static void jack_detect_update(struct hda_codec *codec,
 	if (jack->phantom_jack)
 		jack->pin_sense = AC_PINSENSE_PRESENCE;
 	else
-		jack->pin_sense = read_pin_sense(codec, jack->nid);
+		jack->pin_sense = read_pin_sense(codec,
+				jack->sense_nid ? jack->sense_nid : jack->nid);
+
+	if (jack->slave_nid) {
+		struct hda_jack_tbl *slave =
+			snd_hda_jack_tbl_get(codec, jack->slave_nid);
+		if (slave)
+			slave->pin_sense = jack->pin_sense;
+	}
 
 	/* A gating jack indicates the jack is invalid if gating is unplugged */
 	if (jack->gating_jack && !snd_hda_jack_detect(codec, jack->gating_jack))
@@ -209,6 +218,8 @@  u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid)
 		jack_detect_update(codec, jack);
 		return jack->pin_sense;
 	}
+	if (jack->sense_nid)
+		return read_pin_sense(codec, jack->sense_nid);
 	return read_pin_sense(codec, nid);
 }
 EXPORT_SYMBOL_GPL(snd_hda_pin_sense);
@@ -384,7 +395,7 @@  static void hda_free_jack_priv(struct snd_jack *jack)
  * will have the given name and index.
  */
 static int __snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
-			  const char *name, bool phantom_jack)
+		  const char *name, bool phantom_jack, hda_nid_t sense_nid)
 {
 	struct hda_jack_tbl *jack;
 	int err, state, type;
@@ -405,6 +416,7 @@  static int __snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
 	jack->type = type;
 	jack->jack->private_data = jack;
 	jack->jack->private_free = hda_free_jack_priv;
+	jack->sense_nid = sense_nid;
 	state = snd_hda_jack_detect(codec, nid);
 	snd_jack_report(jack->jack, state ? jack->type : 0);
 
@@ -422,7 +434,7 @@  static int __snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
 int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
 			  const char *name)
 {
-	return __snd_hda_jack_add_kctl(codec, nid, name, false);
+	return __snd_hda_jack_add_kctl(codec, nid, name, false, nid);
 }
 EXPORT_SYMBOL_GPL(snd_hda_jack_add_kctl);
 
@@ -451,7 +463,7 @@  static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid,
 	if (phantom_jack)
 		/* Example final name: "Internal Mic Phantom Jack" */
 		strncat(name, " Phantom", sizeof(name) - strlen(name) - 1);
-	err = __snd_hda_jack_add_kctl(codec, nid, name, phantom_jack);
+	err = __snd_hda_jack_add_kctl(codec, nid, name, phantom_jack, nid);
 	if (err < 0)
 		return err;
 
@@ -469,6 +481,9 @@  int snd_hda_jack_add_kctls(struct hda_codec *codec,
 			   const struct auto_pin_cfg *cfg)
 {
 	const hda_nid_t *p;
+	struct hda_jack_tbl *jack;
+	hda_nid_t headset_mic_nid = 0;
+	struct hda_gen_spec *spec = codec->spec;
 	int i, err;
 
 	for (i = 0; i < cfg->num_inputs; i++) {
@@ -482,6 +497,13 @@  int snd_hda_jack_add_kctls(struct hda_codec *codec,
 				err = add_jack_kctl(codec, cfg->inputs[i].pin,
 						    cfg, "Headphone Mic");
 		} else
+	    if (cfg->inputs[i].is_headset_mic &&
+			spec->hs_mic_use_hp_sense &&
+			!is_jack_detectable(codec, cfg->inputs[i].pin)) {
+			headset_mic_nid = cfg->inputs[i].pin;
+			err = __snd_hda_jack_add_kctl(codec, cfg->inputs[i].pin,
+				"Headset Mic", false, auto_cfg_hp_pins(cfg)[0]);
+		} else
 			err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg,
 					    NULL);
 		if (err < 0)
@@ -489,7 +511,10 @@  int snd_hda_jack_add_kctls(struct hda_codec *codec,
 	}
 
 	for (i = 0, p = cfg->line_out_pins; i < cfg->line_outs; i++, p++) {
-		err = add_jack_kctl(codec, *p, cfg, NULL);
+		if ((cfg->line_outs == 2) && (i == 1))
+			err = add_jack_kctl(codec, *p, cfg, "External Subwoofer");
+		else
+			err = add_jack_kctl(codec, *p, cfg, NULL);
 		if (err < 0)
 			return err;
 	}
@@ -499,6 +524,11 @@  int snd_hda_jack_add_kctls(struct hda_codec *codec,
 		err = add_jack_kctl(codec, *p, cfg, NULL);
 		if (err < 0)
 			return err;
+	    if ((i == 0) && spec->hs_mic_use_hp_sense) {
+			jack = snd_hda_jack_tbl_get(codec, *p);
+		    if (jack)
+				jack->slave_nid = headset_mic_nid;
+		}
 	}
 	for (i = 0, p = cfg->speaker_pins; i < cfg->speaker_outs; i++, p++) {
 		if (*p == *cfg->line_out_pins) /* might be duplicated */
diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h
index 387d309..ef3d162 100644
--- a/sound/pci/hda/hda_jack.h
+++ b/sound/pci/hda/hda_jack.h
@@ -41,6 +41,8 @@  struct hda_jack_tbl {
 	hda_nid_t gated_jack;		/* gated is dependent on this jack */
 	int type;
 	struct snd_jack *jack;
+	hda_nid_t sense_nid;
+	hda_nid_t slave_nid;
 };
 
 struct hda_jack_tbl *
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index c456c04..a402ed5 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -104,6 +104,7 @@  struct alc_spec {
 	hda_nid_t headphone_mic_pin;
 	int current_headset_mode;
 	int current_headset_type;
+	int alc668_combo_type;
 
 	/* hooks */
 	void (*init_hook)(struct hda_codec *codec);
@@ -3984,6 +3985,15 @@  static void alc_update_headset_mode(struct hda_codec *codec)
 
 	int new_headset_mode;
 
+	if (codec->core.vendor_id == 0x10ec0668 &&
+		spec->alc668_combo_type) {
+		if (spec->alc668_combo_type == ALC_HEADSET_MODE_HEADSET &&
+		    spec->gen.auto_mic) {
+			snd_hda_gen_mic_autoswitch(codec, NULL);
+		    mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]];
+		}
+	}
+
 	if (!snd_hda_jack_detect(codec, hp_pin))
 		new_headset_mode = ALC_HEADSET_MODE_UNPLUGGED;
 	else if (mux_pin == spec->headset_mic_pin)
@@ -6232,6 +6242,37 @@  static void alc_fixup_bass_chmap(struct hda_codec *codec,
 	}
 }
 
+static void alc668_fixup_asus_headset(struct hda_codec *codec,
+				    const struct hda_fixup *fix, int action)
+{
+	const struct hda_pintbl pincfgs[] = {
+	    { 0x1a, 0x04110011 }, /* external subwoofer */
+		{ 0x1b, 0x03a1113c }, /* headset mic */
+		{ }
+	};
+
+	struct alc_spec *spec = codec->spec;
+
+	switch (action) {
+	case HDA_FIXUP_ACT_PRE_PROBE:
+		snd_hda_apply_pincfgs(codec, pincfgs);
+	    spec->gen.hs_mic_use_hp_sense = 1;
+		spec->alc668_combo_type = ALC_HEADSET_MODE_HEADSET;
+		spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+		break;
+	case HDA_FIXUP_ACT_PROBE:
+		alc_probe_headset_mode(codec);
+		break;
+	case HDA_FIXUP_ACT_INIT:
+		spec->current_headset_mode = 0;
+		alc_update_headset_mode(codec);
+		break;
+	case HDA_FIXUP_ACT_BUILD:
+		alc_fixup_bass_chmap(codec, fix, action);
+		break;
+	}
+}
+
 /* avoid D3 for keeping GPIO up */
 static unsigned int gpio_led_power_filter(struct hda_codec *codec,
 					  hda_nid_t nid,
@@ -6313,6 +6354,7 @@  enum {
 	ALC662_FIXUP_INV_DMIC,
 	ALC662_FIXUP_DELL_MIC_NO_PRESENCE,
 	ALC668_FIXUP_DELL_MIC_NO_PRESENCE,
+	ALC668_FIXUP_ASUS_HEADSET,
 	ALC662_FIXUP_HEADSET_MODE,
 	ALC668_FIXUP_HEADSET_MODE,
 	ALC662_FIXUP_BASS_MODE4_CHMAP,
@@ -6530,6 +6572,10 @@  static const struct hda_fixup alc662_fixups[] = {
 		.chained = true,
 		.chain_id = ALC668_FIXUP_HEADSET_MODE
 	},
+	[ALC668_FIXUP_ASUS_HEADSET] = {
+		.type = HDA_FIXUP_FUNC,
+		.v.func = alc668_fixup_asus_headset,
+	},
 	[ALC668_FIXUP_HEADSET_MODE] = {
 		.type = HDA_FIXUP_FUNC,
 		.v.func = alc_fixup_headset_mode_alc668,
@@ -6667,6 +6713,7 @@  static const struct hda_model_fixup alc662_fixup_models[] = {
 	{.id = ALC662_FIXUP_ASUS_MODE8, .name = "asus-mode8"},
 	{.id = ALC662_FIXUP_INV_DMIC, .name = "inv-dmic"},
 	{.id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE, .name = "dell-headset-multi"},
+	{.id = ALC668_FIXUP_ASUS_HEADSET, .name = "asus-headset"},
 	{}
 };