diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index b077bb6..af206a2 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1166,6 +1166,17 @@ static const char *get_line_out_pfx(struct hda_codec *codec, int ch, /* for multi-io case, only the primary out */ if (ch && spec->multi_ios) break; + if (ch > 0) { + *index = 0; + switch (ch) { + case 1: + return "Headphone2"; + case 2: + return "Headphone3"; + case 3: + return "Headphone4"; + } + } *index = ch; return "Headphone"; case AUTO_PIN_LINE_OUT: @@ -2092,19 +2103,70 @@ static int create_multi_out_ctls(struct hda_codec *codec, return 0; } +#define is_dock_pin(codec, nid) \ + ((get_defcfg_location(snd_hda_codec_get_pincfg(codec, nid)) & 0x30) \ + == AC_JACK_LOC_SEPARATE) + +char *get_hp_pfx(struct hda_codec *codec, hda_nid_t nid) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + + if (is_dock_pin(codec, nid)) + return "Dock Headphone"; + if ((nid == auto_cfg_hp_pins(cfg)[0]) && (spec->hs_mic_use_hp_sense)) + return "Headset"; + return "Headphone"; +} + static int create_extra_out(struct hda_codec *codec, int path_idx, const char *pfx, int cidx) { struct nid_path *path; - int err; + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + hda_nid_t nid; + const hda_nid_t *p; + int err, i; + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; 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) { + p = auto_cfg_hp_pins(cfg); + nid = path->ctls[NID_PATH_VOL_CTL]; + if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_PIN) + strcpy(name, get_hp_pfx(codec, nid)); + else { + strcpy(name, get_hp_pfx(codec, *p)); + for (i = 1; i < auto_cfg_hp_outs(cfg); i++) { + p++; + strcat(name, "+"); + strcat(name, get_hp_pfx(codec, *p)); + } + } + err = add_stereo_vol(codec, name, 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) { + p = auto_cfg_hp_pins(cfg); + nid = path->ctls[NID_PATH_MUTE_CTL]; + if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_PIN) + strcpy(name, get_hp_pfx(codec, nid)); + else { + strcpy(name, get_hp_pfx(codec, *p)); + for (i = 1; i < auto_cfg_hp_outs(cfg); i++) { + p++; + strcat(name, "+"); + strcat(name, get_hp_pfx(codec, *p)); + } + } + err = add_stereo_sw(codec, name, cidx, path); + } else + err = add_stereo_sw(codec, pfx, cidx, path); if (err < 0) return err; return 0; @@ -4385,10 +4447,12 @@ void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, if (snd_hda_codec_get_pin_target(codec, pin) & AC_PINCTL_OUT_EN) continue; if (snd_hda_jack_detect_state(codec, pin) == HDA_JACK_PRESENT) { + codec_info(codec, "Mic 0x%x selected\n", pin); mux_select(codec, 0, spec->am_entry[i].idx); return; } } + codec_info(codec, "Internal Mic selected\n"); mux_select(codec, 0, spec->am_entry[0].idx); } EXPORT_SYMBOL_GPL(snd_hda_gen_mic_autoswitch); @@ -4665,7 +4729,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..0803afa 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -160,6 +160,8 @@ struct hda_gen_spec { hda_nid_t hp_mic_pin; int hp_mic_mux_idx; + hda_nid_t ext_sub_sense_nid; + /* DAC/ADC lists */ int num_all_dacs; hda_nid_t all_dacs[16]; @@ -236,6 +238,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..f9b61b6 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; @@ -460,6 +472,14 @@ static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, return 0; } +#define is_subwoofer(codec, nid, i, n) ((i == 1) && (n == 2) && \ + get_defcfg_device(snd_hda_codec_get_pincfg(codec, nid)) \ + == AC_JACK_SPEAKER) + +#define is_external_pin(codec, nid) \ +((get_defcfg_location(snd_hda_codec_get_pincfg(codec, nid)) & 0x30) \ + == AC_JACK_LOC_EXTERNAL) + /** * snd_hda_jack_add_kctls - Add kctls for all pins included in the given pincfg * @codec: the HDA codec @@ -469,6 +489,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 +505,14 @@ 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, auto_cfg_hp_pins(cfg)[0]) && + !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 +520,18 @@ 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 (is_subwoofer(codec, *p, i, cfg->line_outs) && + is_external_pin(codec, *p)) { + if ((codec->core.vendor_id == 0x10ec0663) && + !is_jack_detectable(codec, *p) && + ((codec->core.subsystem_id >> 16) == 0x1043) && + (spec->ext_sub_sense_nid == 0x1e)) + err = __snd_hda_jack_add_kctl(codec, *p, "Ext Suboofer", + false, 0x1e); + else + err = add_jack_kctl(codec, *p, cfg, "Ext Subwoofer"); + } else + err = add_jack_kctl(codec, *p, cfg, NULL); if (err < 0) return err; } @@ -499,6 +541,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..3df56c8 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3593,7 +3593,7 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec) alc_process_coef_fw(codec, coef0668); break; } - codec_dbg(codec, "Headset jack set to unplugged mode.\n"); + codec_info(codec, "Headset jack set to unplugged mode.\n"); } @@ -3683,7 +3683,7 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); break; } - codec_dbg(codec, "Headset jack set to mic-in mode.\n"); + codec_info(codec, "Headset jack set to mic-in mode.\n"); } static void alc_headset_mode_default(struct hda_codec *codec) @@ -3752,7 +3752,7 @@ static void alc_headset_mode_default(struct hda_codec *codec) alc_process_coef_fw(codec, coef0688); break; } - codec_dbg(codec, "Headset jack set to headphone (default) mode.\n"); + codec_info(codec, "Headset jack set to headphone (default) mode.\n"); } /* Iphone type */ @@ -3823,7 +3823,7 @@ static void alc_headset_mode_ctia(struct hda_codec *codec) alc_process_coef_fw(codec, coef0688); break; } - codec_dbg(codec, "Headset jack set to iPhone-style headset mode.\n"); + codec_info(codec, "Headset jack set to iPhone-style headset mode.\n"); } /* Nokia type */ @@ -3894,7 +3894,7 @@ static void alc_headset_mode_omtp(struct hda_codec *codec) alc_process_coef_fw(codec, coef0688); break; } - codec_dbg(codec, "Headset jack set to Nokia-style headset mode.\n"); + codec_info(codec, "Headset jack set to Nokia-style headset mode.\n"); } static void alc_determine_headset_type(struct hda_codec *codec) @@ -3970,7 +3970,7 @@ static void alc_determine_headset_type(struct hda_codec *codec) break; } - codec_dbg(codec, "Headset jack detected iPhone-style headset: %s\n", + codec_info(codec, "Headset jack detected iPhone-style headset: %s\n", is_ctia ? "yes" : "no"); spec->current_headset_type = is_ctia ? ALC_HEADSET_TYPE_CTIA : ALC_HEADSET_TYPE_OMTP; } @@ -3986,6 +3986,9 @@ static void alc_update_headset_mode(struct hda_codec *codec) if (!snd_hda_jack_detect(codec, hp_pin)) new_headset_mode = ALC_HEADSET_MODE_UNPLUGGED; + else if (codec->core.vendor_id == 0x10ec0668 && + spec->gen.hs_mic_use_hp_sense) + new_headset_mode = ALC_HEADSET_MODE_HEADSET; else if (mux_pin == spec->headset_mic_pin) new_headset_mode = ALC_HEADSET_MODE_HEADSET; else if (mux_pin == spec->headphone_mic_pin) @@ -4030,6 +4033,11 @@ static void alc_update_headset_mode(struct hda_codec *codec) } spec->current_headset_mode = new_headset_mode; + if (codec->core.vendor_id == 0x10ec0668 && + spec->gen.hs_mic_use_hp_sense && + spec->gen.auto_mic) + snd_hda_gen_mic_autoswitch(codec, NULL); + snd_hda_gen_update_outputs(codec); } @@ -6232,6 +6240,96 @@ static void alc_fixup_bass_chmap(struct hda_codec *codec, } } +static void alc663_fixup_asus_subwoofer(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + const struct hda_pintbl pincfgs[] = { + { 0x16, 0x04110011 }, /* external subwoofer */ + { } + }; + struct alc_spec *spec = codec->spec; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->gen.ext_sub_sense_nid = 0x1e; + snd_hda_apply_pincfgs(codec, pincfgs); + break; + case HDA_FIXUP_ACT_BUILD: + alc_fixup_bass_chmap(codec, fix, action); + break; + }; +} + +static void alc668_fixup_dell_alienware(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + const struct hda_pintbl pincfgs[] = { + { 0x1a, 0x99130112 }, /* subwoofer */ + { 0x1b, 0x03a1113c }, /* headset mic */ + { } + }; + static hda_nid_t preferred_pair[] = { + 0x14, 0x03, + 0x1a, 0x04, + 0x15, 0x02, + 0x16, 0x02, + 0 + }; + struct alc_spec *spec = codec->spec; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_apply_pincfgs(codec, pincfgs); + spec->gen.preferred_dacs = preferred_pair; + spec->gen.hs_mic_use_hp_sense = 1; + 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: +/* + better to use default hda 2.1 chmap + when we don't know internal subwoofer use which channel +*/ + break; + } +} + +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->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,12 +6411,15 @@ enum { ALC662_FIXUP_INV_DMIC, ALC662_FIXUP_DELL_MIC_NO_PRESENCE, ALC668_FIXUP_DELL_MIC_NO_PRESENCE, + ALC668_FIXUP_DELL_ALIENWARE, + ALC668_FIXUP_ASUS_HEADSET, ALC662_FIXUP_HEADSET_MODE, ALC668_FIXUP_HEADSET_MODE, ALC662_FIXUP_BASS_MODE4_CHMAP, ALC662_FIXUP_BASS_16, ALC662_FIXUP_BASS_1A, ALC662_FIXUP_BASS_CHMAP, + ALC663_FIXUP_ASUS_SUB, ALC668_FIXUP_AUTO_MUTE, ALC668_FIXUP_DELL_DISABLE_AAMIX, ALC668_FIXUP_DELL_XPS13, @@ -6530,6 +6631,14 @@ static const struct hda_fixup alc662_fixups[] = { .chained = true, .chain_id = ALC668_FIXUP_HEADSET_MODE }, + [ALC668_FIXUP_DELL_ALIENWARE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc668_fixup_dell_alienware, + }, + [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, @@ -6562,6 +6671,10 @@ static const struct hda_fixup alc662_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_bass_chmap, }, + [ALC663_FIXUP_ASUS_SUB] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc663_fixup_asus_subwoofer, + }, }; static const struct snd_pci_quirk alc662_fixup_tbl[] = { @@ -6583,7 +6696,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x069f, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), 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, 0x1477, "ASUS N56VZ", ALC663_FIXUP_ASUS_SUB), 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), @@ -6667,6 +6780,8 @@ 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_DELL_ALIENWARE, .name = "dell-alienware"}, + {.id = ALC668_FIXUP_ASUS_HEADSET, .name = "asus-headset"}, {} };