aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/hda/patch_conexant.c
diff options
context:
space:
mode:
authorDavid Henningsson <david.henningsson@canonical.com>2012-04-02 09:40:27 -0400
committerTakashi Iwai <tiwai@suse.de>2012-04-05 11:54:22 -0400
commit18dcd3044e4c4b3ab6341c98e8d0e81e0d58d5e3 (patch)
treed41a57e532a419172ce63d4db52588282f350c66 /sound/pci/hda/patch_conexant.c
parentdd775ae2549217d3ae09363e3edb305d0fa19928 (diff)
ALSA: hda - Fix internal mic for Lenovo Ideapad U300s
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 Signed-off-by: David Henningsson <david.henningsson@canonical.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/patch_conexant.c')
-rw-r--r--sound/pci/hda/patch_conexant.c88
1 files changed, 75 insertions, 13 deletions
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 8c6523bbc797..213fb80c11f5 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -142,6 +142,7 @@ struct conexant_spec {
142 unsigned int asus:1; 142 unsigned int asus:1;
143 unsigned int pin_eapd_ctrls:1; 143 unsigned int pin_eapd_ctrls:1;
144 unsigned int single_adc_amp:1; 144 unsigned int single_adc_amp:1;
145 unsigned int fixup_stereo_dmic:1;
145 146
146 unsigned int adc_switching:1; 147 unsigned int adc_switching:1;
147 148
@@ -4107,9 +4108,9 @@ static int cx_auto_init(struct hda_codec *codec)
4107 4108
4108static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename, 4109static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename,
4109 const char *dir, int cidx, 4110 const char *dir, int cidx,
4110 hda_nid_t nid, int hda_dir, int amp_idx) 4111 hda_nid_t nid, int hda_dir, int amp_idx, int chs)
4111{ 4112{
4112 static char name[32]; 4113 static char name[44];
4113 static struct snd_kcontrol_new knew[] = { 4114 static struct snd_kcontrol_new knew[] = {
4114 HDA_CODEC_VOLUME(name, 0, 0, 0), 4115 HDA_CODEC_VOLUME(name, 0, 0, 0),
4115 HDA_CODEC_MUTE(name, 0, 0, 0), 4116 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,
4119 4120
4120 for (i = 0; i < 2; i++) { 4121 for (i = 0; i < 2; i++) {
4121 struct snd_kcontrol *kctl; 4122 struct snd_kcontrol *kctl;
4122 knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, 3, amp_idx, 4123 knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, chs, amp_idx,
4123 hda_dir); 4124 hda_dir);
4124 knew[i].subdevice = HDA_SUBDEV_AMP_FLAG; 4125 knew[i].subdevice = HDA_SUBDEV_AMP_FLAG;
4125 knew[i].index = cidx; 4126 knew[i].index = cidx;
@@ -4138,7 +4139,7 @@ static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename,
4138} 4139}
4139 4140
4140#define cx_auto_add_volume(codec, str, dir, cidx, nid, hda_dir) \ 4141#define cx_auto_add_volume(codec, str, dir, cidx, nid, hda_dir) \
4141 cx_auto_add_volume_idx(codec, str, dir, cidx, nid, hda_dir, 0) 4142 cx_auto_add_volume_idx(codec, str, dir, cidx, nid, hda_dir, 0, 3)
4142 4143
4143#define cx_auto_add_pb_volume(codec, nid, str, idx) \ 4144#define cx_auto_add_pb_volume(codec, nid, str, idx) \
4144 cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT) 4145 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)
4208 return 0; 4209 return 0;
4209} 4210}
4210 4211
4212/* Returns zero if this is a normal stereo channel, and non-zero if it should
4213 be split in two independent channels.
4214 dest_label must be at least 44 characters. */
4215static int cx_auto_get_rightch_label(struct hda_codec *codec, const char *label,
4216 char *dest_label, int nid)
4217{
4218 struct conexant_spec *spec = codec->spec;
4219 int i;
4220
4221 if (!spec->fixup_stereo_dmic)
4222 return 0;
4223
4224 for (i = 0; i < AUTO_CFG_MAX_INS; i++) {
4225 int def_conf;
4226 if (spec->autocfg.inputs[i].pin != nid)
4227 continue;
4228
4229 if (spec->autocfg.inputs[i].type != AUTO_PIN_MIC)
4230 return 0;
4231 def_conf = snd_hda_codec_get_pincfg(codec, nid);
4232 if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT)
4233 return 0;
4234
4235 /* Finally found the inverted internal mic! */
4236 snprintf(dest_label, 44, "Inverted %s", label);
4237 return 1;
4238 }
4239 return 0;
4240}
4241
4211static int cx_auto_add_capture_volume(struct hda_codec *codec, hda_nid_t nid, 4242static int cx_auto_add_capture_volume(struct hda_codec *codec, hda_nid_t nid,
4212 const char *label, const char *pfx, 4243 const char *label, const char *pfx,
4213 int cidx) 4244 int cidx)
@@ -4216,14 +4247,25 @@ static int cx_auto_add_capture_volume(struct hda_codec *codec, hda_nid_t nid,
4216 int i; 4247 int i;
4217 4248
4218 for (i = 0; i < spec->num_adc_nids; i++) { 4249 for (i = 0; i < spec->num_adc_nids; i++) {
4250 char rightch_label[44];
4219 hda_nid_t adc_nid = spec->adc_nids[i]; 4251 hda_nid_t adc_nid = spec->adc_nids[i];
4220 int idx = get_input_connection(codec, adc_nid, nid); 4252 int idx = get_input_connection(codec, adc_nid, nid);
4221 if (idx < 0) 4253 if (idx < 0)
4222 continue; 4254 continue;
4223 if (spec->single_adc_amp) 4255 if (spec->single_adc_amp)
4224 idx = 0; 4256 idx = 0;
4257
4258 if (cx_auto_get_rightch_label(codec, label, rightch_label, nid)) {
4259 /* Make two independent kcontrols for left and right */
4260 int err = cx_auto_add_volume_idx(codec, label, pfx,
4261 cidx, adc_nid, HDA_INPUT, idx, 1);
4262 if (err < 0)
4263 return err;
4264 return cx_auto_add_volume_idx(codec, rightch_label, pfx,
4265 cidx, adc_nid, HDA_INPUT, idx, 2);
4266 }
4225 return cx_auto_add_volume_idx(codec, label, pfx, 4267 return cx_auto_add_volume_idx(codec, label, pfx,
4226 cidx, adc_nid, HDA_INPUT, idx); 4268 cidx, adc_nid, HDA_INPUT, idx, 3);
4227 } 4269 }
4228 return 0; 4270 return 0;
4229} 4271}
@@ -4236,9 +4278,19 @@ static int cx_auto_add_boost_volume(struct hda_codec *codec, int idx,
4236 int i, con; 4278 int i, con;
4237 4279
4238 nid = spec->imux_info[idx].pin; 4280 nid = spec->imux_info[idx].pin;
4239 if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) 4281 if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) {
4282 char rightch_label[44];
4283 if (cx_auto_get_rightch_label(codec, label, rightch_label, nid)) {
4284 int err = cx_auto_add_volume_idx(codec, label, " Boost",
4285 cidx, nid, HDA_INPUT, 0, 1);
4286 if (err < 0)
4287 return err;
4288 return cx_auto_add_volume_idx(codec, rightch_label, " Boost",
4289 cidx, nid, HDA_INPUT, 0, 2);
4290 }
4240 return cx_auto_add_volume(codec, label, " Boost", cidx, 4291 return cx_auto_add_volume(codec, label, " Boost", cidx,
4241 nid, HDA_INPUT); 4292 nid, HDA_INPUT);
4293 }
4242 con = __select_input_connection(codec, spec->imux_info[idx].adc, nid, 4294 con = __select_input_connection(codec, spec->imux_info[idx].adc, nid,
4243 &mux, false, 0); 4295 &mux, false, 0);
4244 if (con < 0) 4296 if (con < 0)
@@ -4405,22 +4457,30 @@ static void apply_pincfg(struct hda_codec *codec, const struct cxt_pincfg *cfg)
4405 4457
4406} 4458}
4407 4459
4408static void apply_pin_fixup(struct hda_codec *codec, 4460enum {
4461 CXT_PINCFG_LENOVO_X200,
4462 CXT_FIXUP_STEREO_DMIC,
4463};
4464
4465static void apply_fixup(struct hda_codec *codec,
4409 const struct snd_pci_quirk *quirk, 4466 const struct snd_pci_quirk *quirk,
4410 const struct cxt_pincfg **table) 4467 const struct cxt_pincfg **table)
4411{ 4468{
4469 struct conexant_spec *spec = codec->spec;
4470
4412 quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk); 4471 quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk);
4413 if (quirk) { 4472 if (quirk && table[quirk->value]) {
4414 snd_printdd(KERN_INFO "hda_codec: applying pincfg for %s\n", 4473 snd_printdd(KERN_INFO "hda_codec: applying pincfg for %s\n",
4415 quirk->name); 4474 quirk->name);
4416 apply_pincfg(codec, table[quirk->value]); 4475 apply_pincfg(codec, table[quirk->value]);
4417 } 4476 }
4477 if (quirk->value == CXT_FIXUP_STEREO_DMIC) {
4478 snd_printdd(KERN_INFO "hda_codec: applying internal mic workaround for %s\n",
4479 quirk->name);
4480 spec->fixup_stereo_dmic = 1;
4481 }
4418} 4482}
4419 4483
4420enum {
4421 CXT_PINCFG_LENOVO_X200,
4422};
4423
4424static const struct cxt_pincfg cxt_pincfg_lenovo_x200[] = { 4484static const struct cxt_pincfg cxt_pincfg_lenovo_x200[] = {
4425 { 0x16, 0x042140ff }, /* HP (seq# overridden) */ 4485 { 0x16, 0x042140ff }, /* HP (seq# overridden) */
4426 { 0x17, 0x21a11000 }, /* dock-mic */ 4486 { 0x17, 0x21a11000 }, /* dock-mic */
@@ -4431,10 +4491,12 @@ static const struct cxt_pincfg cxt_pincfg_lenovo_x200[] = {
4431 4491
4432static const struct cxt_pincfg *cxt_pincfg_tbl[] = { 4492static const struct cxt_pincfg *cxt_pincfg_tbl[] = {
4433 [CXT_PINCFG_LENOVO_X200] = cxt_pincfg_lenovo_x200, 4493 [CXT_PINCFG_LENOVO_X200] = cxt_pincfg_lenovo_x200,
4494 [CXT_FIXUP_STEREO_DMIC] = NULL,
4434}; 4495};
4435 4496
4436static const struct snd_pci_quirk cxt_fixups[] = { 4497static const struct snd_pci_quirk cxt_fixups[] = {
4437 SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT_PINCFG_LENOVO_X200), 4498 SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT_PINCFG_LENOVO_X200),
4499 SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC),
4438 {} 4500 {}
4439}; 4501};
4440 4502
@@ -4477,7 +4539,7 @@ static int patch_conexant_auto(struct hda_codec *codec)
4477 break; 4539 break;
4478 } 4540 }
4479 4541
4480 apply_pin_fixup(codec, cxt_fixups, cxt_pincfg_tbl); 4542 apply_fixup(codec, cxt_fixups, cxt_pincfg_tbl);
4481 4543
4482 /* Show mute-led control only on HP laptops 4544 /* Show mute-led control only on HP laptops
4483 * This is a sort of white-list: on HP laptops, EAPD corresponds 4545 * This is a sort of white-list: on HP laptops, EAPD corresponds