]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - sound/pci/hda/patch_conexant.c
Merge branch 'fix/hda' into topic/hda
[karo-tx-linux.git] / sound / pci / hda / patch_conexant.c
index 3848711d89f7d141de9b170ff5570fd41195b0cc..cbe115b6c80cf9036e6aaa7a69aa15a7f76a75d7 100644 (file)
@@ -141,6 +141,7 @@ struct conexant_spec {
        unsigned int hp_laptop:1;
        unsigned int asus:1;
        unsigned int pin_eapd_ctrls:1;
+       unsigned int fixup_stereo_dmic:1;
 
        unsigned int adc_switching:1;
 
@@ -1607,7 +1608,7 @@ static void cxt5051_update_speaker(struct hda_codec *codec)
        pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
        snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
                            pinctl);
-       /* on ideapad there is an aditional speaker (subwoofer) to mute */
+       /* on ideapad there is an additional speaker (subwoofer) to mute */
        if (spec->ideapad)
                snd_hda_codec_write(codec, 0x1b, 0,
                                    AC_VERB_SET_PIN_WIDGET_CONTROL,
@@ -4079,9 +4080,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),
@@ -4091,7 +4092,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;
@@ -4110,7 +4111,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)
@@ -4180,6 +4181,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)
@@ -4188,14 +4219,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 (codec->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;
 }
@@ -4208,9 +4250,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)
@@ -4377,22 +4429,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 */
@@ -4403,10 +4463,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),
        {}
 };
 
@@ -4451,7 +4513,7 @@ static int patch_conexant_auto(struct hda_codec *codec)
                codec->pin_amp_workaround = 1;
        }
 
-       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