From patchwork Mon Jul 6 03:19:00 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Raymond Yau X-Patchwork-Id: 6719581 Return-Path: X-Original-To: patchwork-alsa-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 400629F380 for ; Mon, 6 Jul 2015 03:19:22 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id CB6E520619 for ; Mon, 6 Jul 2015 03:19:20 +0000 (UTC) Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.kernel.org (Postfix) with ESMTP id CF2CA20610 for ; Mon, 6 Jul 2015 03:19:18 +0000 (UTC) Received: by alsa0.perex.cz (Postfix, from userid 1000) id 3EEFE26051D; Mon, 6 Jul 2015 05:19:17 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Level: X-Spam-Status: No, score=-1.8 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, NO_DNS_FOR_FROM, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=no version=3.3.1 Received: from alsa0.perex.cz (localhost [IPv6:::1]) by alsa0.perex.cz (Postfix) with ESMTP id 25B632604F4; Mon, 6 Jul 2015 05:19:08 +0200 (CEST) X-Original-To: alsa-devel@alsa-project.org Delivered-To: alsa-devel@alsa-project.org Received: by alsa0.perex.cz (Postfix, from userid 1000) id 259FC2604F8; Mon, 6 Jul 2015 05:19:07 +0200 (CEST) Received: from mail-oi0-f47.google.com (mail-oi0-f47.google.com [209.85.218.47]) by alsa0.perex.cz (Postfix) with ESMTP id 898652604ED for ; Mon, 6 Jul 2015 05:19:01 +0200 (CEST) Received: by oihr66 with SMTP id r66so53135571oih.2 for ; Sun, 05 Jul 2015 20:19:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type; bh=jaCAbQ8YR43dG0UV0K937Rhr0IU7MY9cX0CtFEWNst4=; b=zDQnm9owskiZ5zW+k1Uia9nz8LzIufmFT3kWsNyHWsh5T486YF5I8Yo7UeM+Rvc9sz ZpeEhq1iCTAaJMl/o0GOTw1T8F+j6T18ACOhvTAV+bfvzCFOKqKmHjmvPGQ2mOqswWoh v4PwbSG2+QyWHV4+QQl7gXWgOFWkkaaWnzEoid2WY8Iibtmqw1AlzAdMtmuD0klqFdjH 9T0ocb+qUAJhhlK4fnVumaJqkkvqe7QUn6ApbVZ618EER9eDnoxtD8h+PEPY/Sx3uvi4 tq07UHvovJSkCTAcBuOYM0GRD7Rm2zw5HalZh5Cfvqc36hCJJ04kjQlnZs62e0cT/w+z /U9Q== MIME-Version: 1.0 X-Received: by 10.60.178.33 with SMTP id cv1mr14323615oec.11.1436152740489; Sun, 05 Jul 2015 20:19:00 -0700 (PDT) Received: by 10.202.197.15 with HTTP; Sun, 5 Jul 2015 20:19:00 -0700 (PDT) In-Reply-To: References: Date: Mon, 6 Jul 2015 11:19:00 +0800 Message-ID: From: Raymond Yau To: Arthur Borsboom , tiwai@suse.de, david.henningsson@canonical.com X-Content-Filtered-By: Mailman/MimeDel 2.1.14 Cc: ALSA Development Mailing List Subject: Re: [alsa-devel] [ALC668]: Asus N751JK - Incorrect default pin assignment for external base speaker and external microphone not working X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org X-Virus-Scanned: ClamAV using ClamSMTP > > I have tested the patch by manually adding the lines of code into mainline kernel 4.1.1. > > The base speakers works. Do you need special name for the external subwoofer jack detect control for pulseaudio automatically switch from stereo profile to 2.1 profile ? int snd_hda_jack_add_kctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { const hda_nid_t *p; int i, err; ... for (i = 0, p = cfg->line_out_pins; i < cfg->line_outs; i++, p++) { + 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; } > The headset microphone works. > The headphone, headset microphone and microphone jack detection seems to work in PulseAudio (showing by plugged or unplugged status). > However it does not automatically switch the (un)plugged microphone. > My guess is that it does not know if it should use the headset microphone or the microphone (instead of the default internal microphone). > > Do you think there is a way to automate the forward and backward switching between the internal microphone and headset microphone? > http://bazaar.launchpad.net/~unity-settings-daemon-team/unity-settings-daemon/trunk/view/head:/plugins/media-keys/what-did-you-plug-in/pa-backend.c Headphone Mic Jack - indicates headphone and mic-in mode share the same jack, i e, not two separate jacks. Hardware cannot distinguish between headphone and a mic. Headset Mic Phantom Jack - indicates headset jack where hardware can not distinguish between headphones and headsets Headset Mic Jack - indicates headset jack where hardware can distinguish between headphones and headsets. There is no use popping up a dialog in this case, unless we already need to do this for the mic-in mode. Auto mic need headset mic support jack detection There are two methods, both methods need to give up the capability of using headphone and mic cannot use hint for user to select this feature since user hint is applied after pin fixup 1) create headset mic jack as slave of headphone jack , this use headphone jack sense for the jack state of headset mic spec->gen.combo_use_only_as_headset = 0; 2) change headphone jack to headset jack, this require add [Element Headset] to pulseaudio conf files spec->gen.headset_and_no_hp = 0; Method 1 - Cons - hda-emu cannot emulate this master slave jack ctl and gated/gating jack ctl Method 2 Cons - some notebook has a) headset and headphone jacks b)r headset and dock headset alc_headset_mode_unplugged(codec); @@ -4056,8 +4066,10 @@ static void alc_probe_headset_mode(struct hda_codec *codec) /* Find mic pins */ for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].is_headset_mic && !spec->headset_mic_pin) + if (cfg->inputs[i].is_headset_mic && !spec->headset_mic_pin) { spec->headset_mic_pin = cfg->inputs[i].pin; + spec->gen.headset_mic_pin = spec->headset_mic_pin; + } if (cfg->inputs[i].is_headphone_mic && !spec->headphone_mic_pin) spec->headphone_mic_pin = cfg->inputs[i].pin; } @@ -4074,7 +4086,11 @@ static void alc_fixup_headset_mode(struct hda_codec *codec, switch (action) { case HDA_FIXUP_ACT_PRE_PROBE: - spec->parse_flags |= HDA_PINCFG_HEADSET_MIC | HDA_PINCFG_HEADPHONE_MIC; + if (spec->gen.combo_use_only_as_headset || + spec->gen.headset_and_no_hp) + spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; + else + spec->parse_flags |= HDA_PINCFG_HEADSET_MIC | HDA_PINCFG_HEADPHONE_MIC; break; case HDA_FIXUP_ACT_PROBE: alc_probe_headset_mode(codec); @@ -6149,6 +6165,43 @@ static void alc668_restore_default_value(struct hda_codec *codec) alc_process_coef_fw(codec, alc668_coefs); } +static void alc668_fixup_asus_n751jk(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + static const struct hda_pintbl normal_cfgs[] = { + { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */ + { 0x1a, 0x04110011 }, /* bass speaker at Ext Right with jack detect */ + { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */ + { } + }; + static const struct hda_pintbl headset_cfgs[] = { + { 0x19, 0x411111f0 }, /* N/A */ + { 0x1a, 0x04110011 }, /* bass speaker at Ext Right with jack detect */ + { 0x1b, 0x03a1103c }, /* use as headset mic, without its own jack detect */ + { } + }; + switch(action){ + case HDA_FIXUP_ACT_PRE_PROBE: + spec->gen.combo_use_only_as_headset = 0; + spec->gen.headset_and_no_hp = 0; + codec_info(codec, "use as headset %d\n", spec->gen.combo_use_only_as_headset); + if (spec->gen.combo_use_only_as_headset || + spec->gen.headset_and_no_hp) { + snd_hda_apply_pincfgs(codec, headset_cfgs); + spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; + } + else { + snd_hda_apply_pincfgs(codec, normal_cfgs); + spec->parse_flags |= HDA_PINCFG_HEADSET_MIC | HDA_PINCFG_HEADPHONE_MIC; + } + break; + case HDA_FIXUP_ACT_BUILD: + alc_fixup_bass_chmap(codec, fix, action); + break; + } +} + enum { ALC662_FIXUP_ASPIRE, ALC662_FIXUP_LED_GPIO1, @@ -6179,6 +6232,7 @@ enum { ALC668_FIXUP_AUTO_MUTE, ALC668_FIXUP_DELL_DISABLE_AAMIX, ALC668_FIXUP_DELL_XPS13, + ALC668_FIXUP_ASUS_N751JK, }; static const struct hda_fixup alc662_fixups[] = { @@ -6419,6 +6473,12 @@ static const struct hda_fixup alc662_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_bass_chmap, }, + [ALC668_FIXUP_ASUS_N751JK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc668_fixup_asus_n751jk, + .chained = true, + .chain_id = ALC668_FIXUP_HEADSET_MODE, + }, }; static const struct snd_pci_quirk alc662_fixup_tbl[] = { @@ -6441,6 +6501,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800), SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_BASS_1A), SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_BASS_MODE4_CHMAP), + SND_PCI_QUIRK(0x1043, 0x17bd, "Asus N751JK", ALC668_FIXUP_ASUS_N751JK), SND_PCI_QUIRK(0x1043, 0x15a7, "ASUS UX51VZH", ALC662_FIXUP_BASS_16), SND_PCI_QUIRK(0x1043, 0x1b73, "ASUS N55SF", ALC662_FIXUP_BASS_16), SND_PCI_QUIRK(0x1043, 0x1bf3, "ASUS N76VZ", ALC662_FIXUP_BASS_MODE4_CHMAP), diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index ac0db16..7e9b7ae 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -4384,6 +4384,9 @@ void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, /* don't detect pins retasked as outputs */ if (snd_hda_codec_get_pin_target(codec, pin) & AC_PINCTL_OUT_EN) continue; + if (pin == spec->headset_mic_pin) + if (spec->headset_and_no_hp) + pin = spec->autocfg.hp_pins[0]; if (snd_hda_jack_detect_state(codec, pin) == HDA_JACK_PRESENT) { mux_select(codec, 0, spec->am_entry[i].idx); return; diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 56e4139..4c3e043 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -236,6 +236,10 @@ 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 combo_use_only_as_headset:1; /* headphone mic jack - slave of headphone jack */ + unsigned int headset_and_no_hp:1; /* headset jack */ + + hda_nid_t headset_mic_pin; /* 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..771e84f 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,10 @@ 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); + if (jack->master_nid) + jack->pin_sense = read_pin_sense(codec, jack->master_nid); + else + jack->pin_sense = read_pin_sense(codec, jack->nid); /* A gating jack indicates the jack is invalid if gating is unplugged */ if (jack->gating_jack && !snd_hda_jack_detect(codec, jack->gating_jack)) @@ -205,11 +209,20 @@ EXPORT_SYMBOL_GPL(snd_hda_jack_set_dirty_all); u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid) { struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid); + struct hda_jack_tbl *slave; + u32 sense; if (jack) { jack_detect_update(codec, jack); return jack->pin_sense; } - return read_pin_sense(codec, nid); + if (jack->master_nid) + return read_pin_sense(codec, jack->master_nid); + sense = read_pin_sense(codec, nid); + if (jack->slave_nid) { + slave = snd_hda_jack_tbl_get(codec, jack->slave_nid); + slave->pin_sense = sense; + } + return sense; } EXPORT_SYMBOL_GPL(snd_hda_pin_sense); @@ -317,6 +330,28 @@ int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid, EXPORT_SYMBOL_GPL(snd_hda_jack_set_gating_jack); /** + * snd_hda_jack_set_master_slave + * @codec: the HDA codec + * @master_nid: use this nid for pin sense + * @slave_nid: update slave jack pin sense + * use master pin sense for slave pin sense + */ +int snd_hda_jack_set_master_slave(struct hda_codec *codec, hda_nid_t master_nid, + hda_nid_t slave_nid) +{ + struct hda_jack_tbl *master = snd_hda_jack_tbl_get(codec, master_nid); + struct hda_jack_tbl *slave = snd_hda_jack_tbl_get(codec, slave_nid); + if (master) + master->slave_nid = slave_nid; + if (slave) { + slave->master_nid = master_nid; + snd_hda_codec_write_cache(codec, slave_nid, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, 0); + } + return 0; +} + +/** * snd_hda_jack_report_sync - sync the states of all jacks and report if changed * @codec: the HDA codec */ @@ -469,7 +504,8 @@ int snd_hda_jack_add_kctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { const hda_nid_t *p; - int i, err; + int i, err, loc; + struct hda_gen_spec *spec = codec->spec; for (i = 0; i < cfg->num_inputs; i++) { /* If we have headphone mics; make sure they get the right name @@ -481,22 +517,37 @@ int snd_hda_jack_add_kctls(struct hda_codec *codec, else err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg, "Headphone Mic"); - } else - err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg, + } else { + if (cfg->inputs[i].is_headset_mic) { + if (!spec->headset_and_no_hp) + err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg, NULL); + else + err = 0; + } + else + err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg, NULL); + } if (err < 0) return err; } for (i = 0, p = cfg->line_out_pins; i < cfg->line_outs; i++, p++) { - err = add_jack_kctl(codec, *p, cfg, NULL); + loc = get_defcfg_location(get_wcaps(codec, *p)); + if (i == 1 && cfg->line_outs == 2 && loc == AC_JACK_LOC_EXTERNAL) + err = add_jack_kctl(codec, *p, cfg, "External Subwoofer"); + else + err = add_jack_kctl(codec, *p, cfg, NULL); if (err < 0) return err; } for (i = 0, p = cfg->hp_pins; i < cfg->hp_outs; i++, p++) { if (*p == *cfg->line_out_pins) /* might be duplicated */ break; - err = add_jack_kctl(codec, *p, cfg, NULL); + if (spec->headset_and_no_hp && i == 0) + err = add_jack_kctl(codec, *p, cfg, "Headset"); + else + err = add_jack_kctl(codec, *p, cfg, NULL); if (err < 0) return err; } @@ -507,6 +558,13 @@ int snd_hda_jack_add_kctls(struct hda_codec *codec, if (err < 0) return err; } + + if (spec->combo_use_only_as_headset) + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].is_headset_mic) + snd_hda_jack_set_master_slave(codec, cfg->hp_pins[0], cfg->inputs[i].pin); + } + for (i = 0, p = cfg->dig_out_pins; i < cfg->dig_outs; i++, p++) { err = add_jack_kctl(codec, *p, cfg, NULL); if (err < 0) diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index 387d309..364cfa5 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -39,6 +39,8 @@ struct hda_jack_tbl { unsigned int block_report:1; /* in a transitional state - do not report to userspace */ hda_nid_t gating_jack; /* valid when gating jack plugged */ hda_nid_t gated_jack; /* gated is dependent on this jack */ + hda_nid_t master_nid; /* use master_nid for jack sense */ + hda_nid_t slave_nid; /* update slave_nid jack state */ int type; struct snd_jack *jack; }; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8e02cdf..598a442 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3998,6 +3998,16 @@ static void alc_update_headset_mode(struct hda_codec *codec) return; } + if (spec->gen.combo_use_only_as_headset || + spec->gen.headset_and_no_hp) { + if (snd_hda_jack_detect(codec, hp_pin)) { + new_headset_mode = ALC_HEADSET_MODE_HEADSET; + alc_determine_headset_type(codec); + codec_info(codec, "mic_autoswitch\n"); + snd_hda_gen_mic_autoswitch(codec, NULL); + } + } + switch (new_headset_mode) { case ALC_HEADSET_MODE_UNPLUGGED: