@@ -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 plugged\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;
}
@@ -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;
@@ -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) ((i == 1) && \
+ 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,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 (is_subwoofer(codec, *p, i) && is_external_pin(codec, *p))
+ 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 +533,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 */
@@ -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 *
@@ -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;
}
@@ -3984,8 +3984,21 @@ static void alc_update_headset_mode(struct hda_codec *codec)
int new_headset_mode;
+ if (codec->core.vendor_id == 0x10ec0668 &&
+ spec->gen.hs_mic_use_hp_sense) {
+ if (spec->gen.auto_mic) {
+ codec_info(codec, "mux_pin before 0x%x\n", mux_pin);
+ snd_hda_gen_mic_autoswitch(codec, NULL);
+ mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]];
+ codec_info(codec, "mux_pin after 0x%x\n", mux_pin);
+ }
+ }
+
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)
@@ -6232,6 +6245,77 @@ static void alc_fixup_bass_chmap(struct hda_codec *codec,
}
}
+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 instead of asus 2.1 chmap
+ alc_fixup_bass_chmap(codec, fix, action);
+*/
+ 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,6 +6397,8 @@ 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,
@@ -6530,6 +6616,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,
@@ -6667,6 +6761,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"},
{}
};