|
@@ -142,6 +142,7 @@ struct conexant_spec {
|
|
|
unsigned int asus:1;
|
|
|
unsigned int pin_eapd_ctrls:1;
|
|
|
unsigned int single_adc_amp:1;
|
|
|
+ unsigned int fixup_stereo_dmic:1;
|
|
|
|
|
|
unsigned int adc_switching:1;
|
|
|
|
|
@@ -4107,9 +4108,9 @@ static int cx_auto_init(struct hda_codec *codec)
|
|
|
|
|
|
static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename,
|
|
|
const char *dir, int cidx,
|
|
|
- hda_nid_t nid, int hda_dir, int amp_idx)
|
|
|
+ hda_nid_t nid, int hda_dir, int amp_idx, int chs)
|
|
|
{
|
|
|
- static char name[32];
|
|
|
+ static char name[44];
|
|
|
static struct snd_kcontrol_new knew[] = {
|
|
|
HDA_CODEC_VOLUME(name, 0, 0, 0),
|
|
|
HDA_CODEC_MUTE(name, 0, 0, 0),
|
|
@@ -4119,7 +4120,7 @@ static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename,
|
|
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
struct snd_kcontrol *kctl;
|
|
|
- knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, 3, amp_idx,
|
|
|
+ knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, chs, amp_idx,
|
|
|
hda_dir);
|
|
|
knew[i].subdevice = HDA_SUBDEV_AMP_FLAG;
|
|
|
knew[i].index = cidx;
|
|
@@ -4138,7 +4139,7 @@ static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename,
|
|
|
}
|
|
|
|
|
|
#define cx_auto_add_volume(codec, str, dir, cidx, nid, hda_dir) \
|
|
|
- cx_auto_add_volume_idx(codec, str, dir, cidx, nid, hda_dir, 0)
|
|
|
+ cx_auto_add_volume_idx(codec, str, dir, cidx, nid, hda_dir, 0, 3)
|
|
|
|
|
|
#define cx_auto_add_pb_volume(codec, nid, str, idx) \
|
|
|
cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT)
|
|
@@ -4208,6 +4209,36 @@ static int cx_auto_build_output_controls(struct hda_codec *codec)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/* Returns zero if this is a normal stereo channel, and non-zero if it should
|
|
|
+ be split in two independent channels.
|
|
|
+ dest_label must be at least 44 characters. */
|
|
|
+static int cx_auto_get_rightch_label(struct hda_codec *codec, const char *label,
|
|
|
+ char *dest_label, int nid)
|
|
|
+{
|
|
|
+ struct conexant_spec *spec = codec->spec;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (!spec->fixup_stereo_dmic)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ for (i = 0; i < AUTO_CFG_MAX_INS; i++) {
|
|
|
+ int def_conf;
|
|
|
+ if (spec->autocfg.inputs[i].pin != nid)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (spec->autocfg.inputs[i].type != AUTO_PIN_MIC)
|
|
|
+ return 0;
|
|
|
+ def_conf = snd_hda_codec_get_pincfg(codec, nid);
|
|
|
+ if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /* Finally found the inverted internal mic! */
|
|
|
+ snprintf(dest_label, 44, "Inverted %s", label);
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int cx_auto_add_capture_volume(struct hda_codec *codec, hda_nid_t nid,
|
|
|
const char *label, const char *pfx,
|
|
|
int cidx)
|
|
@@ -4216,14 +4247,25 @@ static int cx_auto_add_capture_volume(struct hda_codec *codec, hda_nid_t nid,
|
|
|
int i;
|
|
|
|
|
|
for (i = 0; i < spec->num_adc_nids; i++) {
|
|
|
+ char rightch_label[44];
|
|
|
hda_nid_t adc_nid = spec->adc_nids[i];
|
|
|
int idx = get_input_connection(codec, adc_nid, nid);
|
|
|
if (idx < 0)
|
|
|
continue;
|
|
|
if (spec->single_adc_amp)
|
|
|
idx = 0;
|
|
|
+
|
|
|
+ if (cx_auto_get_rightch_label(codec, label, rightch_label, nid)) {
|
|
|
+ /* Make two independent kcontrols for left and right */
|
|
|
+ int err = cx_auto_add_volume_idx(codec, label, pfx,
|
|
|
+ cidx, adc_nid, HDA_INPUT, idx, 1);
|
|
|
+ if (err < 0)
|
|
|
+ return err;
|
|
|
+ return cx_auto_add_volume_idx(codec, rightch_label, pfx,
|
|
|
+ cidx, adc_nid, HDA_INPUT, idx, 2);
|
|
|
+ }
|
|
|
return cx_auto_add_volume_idx(codec, label, pfx,
|
|
|
- cidx, adc_nid, HDA_INPUT, idx);
|
|
|
+ cidx, adc_nid, HDA_INPUT, idx, 3);
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
@@ -4236,9 +4278,19 @@ static int cx_auto_add_boost_volume(struct hda_codec *codec, int idx,
|
|
|
int i, con;
|
|
|
|
|
|
nid = spec->imux_info[idx].pin;
|
|
|
- if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)
|
|
|
+ if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) {
|
|
|
+ char rightch_label[44];
|
|
|
+ if (cx_auto_get_rightch_label(codec, label, rightch_label, nid)) {
|
|
|
+ int err = cx_auto_add_volume_idx(codec, label, " Boost",
|
|
|
+ cidx, nid, HDA_INPUT, 0, 1);
|
|
|
+ if (err < 0)
|
|
|
+ return err;
|
|
|
+ return cx_auto_add_volume_idx(codec, rightch_label, " Boost",
|
|
|
+ cidx, nid, HDA_INPUT, 0, 2);
|
|
|
+ }
|
|
|
return cx_auto_add_volume(codec, label, " Boost", cidx,
|
|
|
nid, HDA_INPUT);
|
|
|
+ }
|
|
|
con = __select_input_connection(codec, spec->imux_info[idx].adc, nid,
|
|
|
&mux, false, 0);
|
|
|
if (con < 0)
|
|
@@ -4405,22 +4457,30 @@ static void apply_pincfg(struct hda_codec *codec, const struct cxt_pincfg *cfg)
|
|
|
|
|
|
}
|
|
|
|
|
|
-static void apply_pin_fixup(struct hda_codec *codec,
|
|
|
+enum {
|
|
|
+ CXT_PINCFG_LENOVO_X200,
|
|
|
+ CXT_FIXUP_STEREO_DMIC,
|
|
|
+};
|
|
|
+
|
|
|
+static void apply_fixup(struct hda_codec *codec,
|
|
|
const struct snd_pci_quirk *quirk,
|
|
|
const struct cxt_pincfg **table)
|
|
|
{
|
|
|
+ struct conexant_spec *spec = codec->spec;
|
|
|
+
|
|
|
quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk);
|
|
|
- if (quirk) {
|
|
|
+ if (quirk && table[quirk->value]) {
|
|
|
snd_printdd(KERN_INFO "hda_codec: applying pincfg for %s\n",
|
|
|
quirk->name);
|
|
|
apply_pincfg(codec, table[quirk->value]);
|
|
|
}
|
|
|
+ if (quirk->value == CXT_FIXUP_STEREO_DMIC) {
|
|
|
+ snd_printdd(KERN_INFO "hda_codec: applying internal mic workaround for %s\n",
|
|
|
+ quirk->name);
|
|
|
+ spec->fixup_stereo_dmic = 1;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-enum {
|
|
|
- CXT_PINCFG_LENOVO_X200,
|
|
|
-};
|
|
|
-
|
|
|
static const struct cxt_pincfg cxt_pincfg_lenovo_x200[] = {
|
|
|
{ 0x16, 0x042140ff }, /* HP (seq# overridden) */
|
|
|
{ 0x17, 0x21a11000 }, /* dock-mic */
|
|
@@ -4431,10 +4491,12 @@ static const struct cxt_pincfg cxt_pincfg_lenovo_x200[] = {
|
|
|
|
|
|
static const struct cxt_pincfg *cxt_pincfg_tbl[] = {
|
|
|
[CXT_PINCFG_LENOVO_X200] = cxt_pincfg_lenovo_x200,
|
|
|
+ [CXT_FIXUP_STEREO_DMIC] = NULL,
|
|
|
};
|
|
|
|
|
|
static const struct snd_pci_quirk cxt_fixups[] = {
|
|
|
SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT_PINCFG_LENOVO_X200),
|
|
|
+ SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC),
|
|
|
{}
|
|
|
};
|
|
|
|
|
@@ -4477,7 +4539,7 @@ static int patch_conexant_auto(struct hda_codec *codec)
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- apply_pin_fixup(codec, cxt_fixups, cxt_pincfg_tbl);
|
|
|
+ apply_fixup(codec, cxt_fixups, cxt_pincfg_tbl);
|
|
|
|
|
|
/* Show mute-led control only on HP laptops
|
|
|
* This is a sort of white-list: on HP laptops, EAPD corresponds
|