The internal mic input is phase inverted on one channel.
To avoid people in userspace summing the channels together
and get zero result, use a separate mixer control for the
inverted channel.
+/* 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;
The internal mic input is phase inverted on one channel.
To avoid people in userspace summing the channels together
and get zero result, use a separate mixer control for the
inverted channel.
BugLink: https:/ /bugs.launchpad .net/bugs/ 903853 pci/hda/ patch_conexant. c | 88 +++++++ +++++++ +++++++ +++++++ ++++++- -----
Signed-off-by: David Henningsson <email address hidden>
---
sound/
1 files changed, 75 insertions(+), 13 deletions(-)
diff --git a/sound/ pci/hda/ patch_conexant. c b/sound/ pci/hda/ patch_conexant. c pci/hda/ patch_conexant. c pci/hda/ patch_conexant. c dmic:1;
index e6eafb1..c18e3b1 100644
--- a/sound/
+++ b/sound/
@@ -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_
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, CODEC_VOLUME( name, 0, 0, 0), CODEC_MUTE( name, 0, 0, 0), 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_
HDA_
@@ -4119,7 +4120,7 @@ static int cx_auto_
for (i = 0; i < 2; i++) { .private_ value = HDA_COMPOSE_ AMP_VAL( nid, 3, amp_idx, .private_ value = HDA_COMPOSE_ AMP_VAL( nid, chs, amp_idx,
hda_ dir); i].subdevice = HDA_SUBDEV_ AMP_FLAG; add_volume_ idx(struct hda_codec *codec, const char *basename,
struct snd_kcontrol *kctl;
- knew[i]
+ knew[i]
knew[
knew[i].index = cidx;
@@ -4138,7 +4139,7 @@ static int cx_auto_
}
#define cx_auto_ add_volume( codec, str, dir, cidx, nid, hda_dir) \ add_volume_ idx(codec, str, dir, cidx, nid, hda_dir, 0) add_volume_ idx(codec, str, dir, cidx, nid, hda_dir, 0, 3)
- cx_auto_
+ cx_auto_
#define cx_auto_ add_pb_ volume( codec, nid, str, idx) \ add_volume( codec, str, " Playback", idx, nid, HDA_OUTPUT) build_output_ controls( struct hda_codec *codec)
cx_auto_
@@ -4208,6 +4209,36 @@ static int cx_auto_
return 0;
}
+/* Returns zero if this is a normal stereo channel, and non-zero if it should get_rightch_ label(struct hda_codec *codec, const char *label, >fixup_ stereo_ dmic) autocfg. inputs[ i].pin != nid) autocfg. inputs[ i].type != AUTO_PIN_MIC) codec_get_ pincfg( codec, nid); get_input_ pin_attr( def_conf) != INPUT_PIN_ATTR_INT) dest_label, 44, "Inverted %s", label); add_capture_ volume( struct hda_codec *codec, hda_nid_t nid, add_capture_ volume( struct hda_codec *codec, hda_nid_t nid,
+ be split in two independent channels.
+ dest_label must be at least 44 characters. */
+static int cx_auto_
+ char *dest_label, int nid)
+{
+ struct conexant_spec *spec = codec->spec;
+ int i;
+
+ if (!spec-
+ return 0;
+
+ for (i = 0; i < AUTO_CFG_MAX_INS; i++) {
+ int def_conf;
+ if (spec->
+ continue;
+
+ if (spec->
+ return 0;
+ def_conf = snd_hda_
+ if (snd_hda_
+ return 0;
+
+ /* Finally found the inverted internal mic! */
+ snprintf(
+ return 1;
+ }
+ return 0;
+}
+
static int cx_auto_
const char *label, const char *pfx,
int cidx)
@@ -4216,14 +4247,25 @@ static int cx_auto_
int i;
for (i = 0; i < spec->num_adc_nids; i++) { connection( codec, adc_nid, nid); single_ adc_amp) get_rightch_ label(codec, label, rightch_label, nid)) { add_volume_ idx(codec, label, pfx, add_volume_ idx(codec, rightch_label, pfx, add_volume_ idx(codec, label, pfx, add_boost_ volume( struct hda_codec *codec, int idx,
+ char rightch_label[44];
hda_nid_t adc_nid = spec->adc_nids[i];
int idx = get_input_
if (idx < 0)
continue;
if (spec->
idx = 0;
+
+ if (cx_auto_
+ /* Make two independent kcontrols for left and right */
+ int err = cx_auto_
+ cidx, adc_nid, HDA_INPUT, idx, 1);
+ if (err < 0)
+ return err;
+ return cx_auto_
+ cidx, adc_nid, HDA_INPUT, idx, 2);
+ }
return cx_auto_
- cidx, adc_nid, HDA_INPUT, idx);
+ cidx, adc_nid, HDA_INPUT, idx, 3);
}
return 0;
}
@@ -4236,9 +4278,19 @@ static int cx_auto_
int i, con;
nid = spec->imux_ info[idx] .pin; get_rightch_ label(codec, label, rightch_label, nid)) { add_volume_ idx(codec, label, " Boost", add_volume_ idx(codec, rightch_label, " Boost", add_volume( codec, label, " Boost", cidx, input_connectio n(codec, spec->imux_ info[idx] .adc, nid,
- 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_
+ int err = cx_auto_
+ cidx, nid, HDA_INPUT, 0, 1);
+ if (err < 0)
+ return err;
+ return cx_auto_
+ cidx, nid, HDA_INPUT, 0, 2);
+ }
return cx_auto_
nid, HDA_INPUT);
+ }
con = __select_
&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, LENOVO_ X200, STEREO_ DMIC, quirk_lookup( codec-> bus->pci, quirk); >value] ) { printdd( KERN_INFO "hda_codec: applying pincfg for %s\n",
quirk- >name); pincfg( codec, table[quirk- >value] ); STEREO_ DMIC) { KERN_INFO "hda_codec: applying internal mic workaround for %s\n", stereo_ dmic = 1;
+enum {
+ CXT_PINCFG_
+ CXT_FIXUP_
+};
+
+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_
- if (quirk) {
+ if (quirk && table[quirk-
snd_
apply_
}
+ if (quirk->value == CXT_FIXUP_
+ snd_printdd(
+ quirk->name);
+ spec->fixup_
+ }
}
-enum { LENOVO_ X200, lenovo_ x200[] = { lenovo_ x200[] = {
- CXT_PINCFG_
-};
-
static const struct cxt_pincfg cxt_pincfg_
{ 0x16, 0x042140ff }, /* HP (seq# overridden) */
{ 0x17, 0x21a11000 }, /* dock-mic */
@@ -4431,10 +4491,12 @@ static const struct cxt_pincfg cxt_pincfg_
static const struct cxt_pincfg *cxt_pincfg_tbl[] = { PINCFG_ LENOVO_ X200] = cxt_pincfg_ lenovo_ x200, STEREO_ DMIC] = NULL,
[CXT_
+ [CXT_FIXUP_
};
static const struct snd_pci_quirk cxt_fixups[] = { QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT_PINCFG_ LENOVO_ X200), QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_ STEREO_ DMIC),
SND_PCI_
+ SND_PCI_
{}
};
@@ -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
--
1.7.9.1